file based auth
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
Reference in New Issue
Block a user