45 lines
1.7 KiB
TypeScript
45 lines
1.7 KiB
TypeScript
// One-time migration: replace each seat line's raw token with its SHA-256,
|
|
// so no ledger on disk or in a backup holds a live seat key. Idempotent; a
|
|
// line already hashed is left alone. Run ON the droplet from the server
|
|
// directory (tsx is installed there):
|
|
// cd /opt/hnefatafl/app/server && npx tsx ../deploy/hash-tokens.ts /var/lib/hnefatafl/rooms
|
|
// Browsers keep their raw tokens; the server hashes what they send and
|
|
// compares, so nobody loses a seat.
|
|
|
|
import { createHash } from 'node:crypto';
|
|
import { readdirSync, readFileSync, renameSync, statSync, writeFileSync, chownSync } from 'node:fs';
|
|
import { join } from 'node:path';
|
|
|
|
const dir = process.argv[2];
|
|
if (!dir) {
|
|
console.error('usage: hash-tokens.ts <ledger-dir>');
|
|
process.exit(2);
|
|
}
|
|
|
|
let files = 0;
|
|
let lines = 0;
|
|
for (const file of readdirSync(dir).filter((f) => f.endsWith('.jsonl'))) {
|
|
const path = join(dir, file);
|
|
const raw = readFileSync(path, 'utf8');
|
|
let changed = 0;
|
|
const out = raw
|
|
.split('\n')
|
|
.map((line) => {
|
|
if (!line.trim()) return line;
|
|
const entry = JSON.parse(line) as Record<string, unknown>;
|
|
if (entry.t !== 'seat' || typeof entry.token !== 'string') return line;
|
|
const { token, ...rest } = entry;
|
|
changed += 1;
|
|
return JSON.stringify({ ...rest, tokenHash: token ? createHash('sha256').update(token).digest('hex') : '' });
|
|
})
|
|
.join('\n');
|
|
if (!changed) continue;
|
|
const { uid, gid } = statSync(path);
|
|
writeFileSync(path + '.tmp', out);
|
|
chownSync(path + '.tmp', uid, gid);
|
|
renameSync(path + '.tmp', path);
|
|
files += 1;
|
|
lines += changed;
|
|
}
|
|
console.log(`${files} ledger${files === 1 ? '' : 's'} rewritten, ${lines} seat line${lines === 1 ? '' : 's'} hashed`);
|