Files
vngeoguessr/scripts/export-leaderboards.mjs
tiennm99 4d25a82448 chore(ci): raise the test hook timeout, fail an empty backup
PGlite start-up alone takes about seven seconds on an ARM host, so the
10s beforeAll default failed whole files at random. The weekly backup
fails instead of uploading an empty export, and both workflows run with
read-only permissions; CI cancels superseded runs.
2026-09-28 15:01:41 +07:00

45 lines
1.8 KiB
JavaScript

// Write every leaderboard to one JSON file, for backup.
//
// Usage: node scripts/export-leaderboards.mjs [out.json]
// default output: leaderboard-backup-YYYYMMDD.json (gitignored)
//
// Needs the Upstash pair (UPSTASH_REDIS_REST_URL/_TOKEN or KV_REST_API_URL/
// _TOKEN) in .env or the environment. Read-only. Every score and distance
// board is dumped whole, member and score, under its logical key, so the file
// can be replayed with ZADD against any prefix.
import { writeFileSync } from 'node:fs';
import { getUpstash, scanKeys, zRangeWithScores } from '../src/lib/upstash.js';
import { applyEnvFile } from './lib/env.mjs';
applyEnvFile();
const stamp = new Date().toISOString().slice(0, 10).replace(/-/g, '');
const outPath = process.argv[2] ?? `leaderboard-backup-${stamp}.json`;
const h = getUpstash();
const keys = [...(await scanKeys(h, 'leaderboard:*')), ...(await scanKeys(h, 'distance:*'))].sort();
// Nothing found is a misconfiguration (a wrong KEY_PREFIX, the wrong
// database), not an empty game. Failing here keeps the weekly job from
// uploading an empty file and reporting success.
if (keys.length === 0) {
console.error(`No boards found under prefix "${h.prefix}"; check KEY_PREFIX and the Redis credentials.`);
process.exit(1);
}
const boards = {};
let members = 0;
for (const key of keys) {
// Ascending, whole set: the same order for every board, so two exports diff
// cleanly.
const entries = await zRangeWithScores(h, key, 0, -1, false);
boards[key] = entries.map(({ value, score }) => ({ member: value, score }));
members += entries.length;
}
writeFileSync(
outPath,
JSON.stringify({ exportedAt: new Date().toISOString(), prefix: h.prefix, boards }, null, 1) + '\n'
);
console.log(`${keys.length} boards, ${members} members -> ${outPath}`);