SPB Git
1.7 KB · 23 lines typescript
1/** Creates/rotates the two application roles. Usage: DATABASE_URL_ADMIN=postgres://<superuser>@host/db pnpm db:roles [--rotate]2 *  Prints the generated passwords ONCE to stdout (write them into .env) — never stored anywhere else. */3import "./_env";4import postgres from "postgres";5import { randomBytes } from "node:crypto";6const admin = process.env.DATABASE_URL_ADMIN; if (!admin) { console.error("DATABASE_URL_ADMIN required (superuser/owner connection)"); process.exit(1); }7const sql = postgres(admin, { max: 1 });8const rotate = process.argv.includes("--rotate");9const pw = () => randomBytes(24).toString("base64url");10const out: Record<string, string> = {};11for (const role of ["tools_app", "tools_reader"]) {12  const exists = (await sql`SELECT 1 FROM pg_roles WHERE rolname=${role}`).length > 0;13  if (!exists) { const p = pw(); await sql.unsafe(`CREATE ROLE ${role} LOGIN PASSWORD '${p}' NOSUPERUSER NOCREATEDB NOCREATEROLE NOINHERIT`); out[role] = p; }14  else if (rotate) { const p = pw(); await sql.unsafe(`ALTER ROLE ${role} PASSWORD '${p}'`); out[role] = p; }15}16await sql.unsafe(`ALTER ROLE tools_reader RESET default_transaction_read_only`); // reader may INSERT into error_reports/search_events (explicit grants); everything else is denied by privileges17await sql.unsafe(`ALTER ROLE tools_reader SET statement_timeout = '5s'`);18await sql.unsafe(`ALTER ROLE tools_app SET statement_timeout = '60s'`);19const db = (await sql`SELECT current_database() AS d`)[0].d;20await sql.unsafe(`GRANT CONNECT ON DATABASE "${db}" TO tools_app, tools_reader`);21console.log(Object.keys(out).length ? JSON.stringify(out, null, 2) : "roles exist (use --rotate to rotate passwords)");22await sql.end();23
Files · ba7bd272caa00f8811bdea5814fbf1150547c055
Menu

Theme