mirror of
https://github.com/tiennm99/vngeoguessr.git
synced 2026-10-11 12:29:01 +00:00
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.
45 lines
1.8 KiB
JavaScript
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}`);
|