Files
platform/scripts/migrate-pg-to-files.ts
T
2026-02-23 09:58:08 +00:00

80 lines
2.5 KiB
TypeScript

/**
* One-time migration: PostgreSQL auth tables → JSON files
*
* Usage:
* POSTGRES_URL="postgres://..." bun run scripts/migrate-pg-to-files.ts
*
* Reads users and passkeys from Postgres, writes JSON files to {DATA_PATH}/auth/.
* Safe to run multiple times (overwrites files).
*/
import { join } from 'node:path';
import { mkdir } from 'node:fs/promises';
import postgres from 'postgres';
const POSTGRES_URL = process.env.POSTGRES_URL;
if (!POSTGRES_URL) {
console.error('POSTGRES_URL env var is required');
process.exit(1);
}
const DATA_PATH = process.env.DATA_PATH ?? join(process.cwd(), 'data');
const AUTH_DIR = join(DATA_PATH, 'auth');
const sql = postgres(POSTGRES_URL);
try {
await mkdir(AUTH_DIR, { recursive: true });
const users = await sql`SELECT id, email, password, role, status, name, username, avatar, password_changed_at FROM users ORDER BY id`;
const passkeys = await sql`SELECT id, email, origin, credential_id, public_key, counter FROM passkeys ORDER BY id`;
const mappedUsers = users.map((u) => ({
id: Number(u.id),
email: u.email,
password: u.password ?? null,
role: u.role ?? 'Member',
status: u.status ?? 'Unverified',
name: u.name ?? null,
username: u.username ?? null,
avatar: u.avatar ?? null,
passwordChangedAt: u.password_changed_at ? Number(u.password_changed_at) : null,
}));
const mappedPasskeys = passkeys.map((p) => ({
id: Number(p.id),
email: p.email,
origin: p.origin ?? null,
credentialId: p.credential_id ?? null,
publicKey: p.public_key ?? null,
counter: Number(p.counter ?? 0),
}));
const maxUserId = mappedUsers.reduce((max, u) => Math.max(max, u.id), 0);
const maxPasskeyId = mappedPasskeys.reduce((max, p) => Math.max(max, p.id), 0);
const meta = {
nextUserId: maxUserId + 1,
nextPasskeyId: maxPasskeyId + 1,
};
const write = (file: string, data: unknown) => Bun.write(join(AUTH_DIR, file), JSON.stringify(data, null, 2));
await Promise.all([
write('users.json', mappedUsers),
write('passkeys.json', mappedPasskeys),
write('passkey-challenges.json', []),
write('token-blacklist.json', []),
write('meta.json', meta),
]);
console.log(`Migrated ${mappedUsers.length} users, ${mappedPasskeys.length} passkeys`);
console.log(`Files written to ${AUTH_DIR}`);
console.log(`meta: nextUserId=${meta.nextUserId}, nextPasskeyId=${meta.nextPasskeyId}`);
} catch (err) {
console.error('Migration failed:', err);
process.exit(1);
} finally {
await sql.end();
}