TypeScript 97.6%
SQL 1.4%
JavaScript 0.5%
1/**2 * Create (or reset) an admin account with TOTP.3 * pnpm admin:create <username> [password]4 * Prints the TOTP secret + otpauth URL once. Never logs the password.5 */6import { adminUsers, closeDb, db, eq } from "@spinza/database";7import { encrypt, generateTotpSecret, hashPassword, otpauthUrl, randomToken } from "./lib/crypto";89async function main() {10 const [username, passwordArg] = process.argv.slice(2);11 if (!username) {12 console.error("usage: admin:create <username> [password]");13 process.exit(1);14 }15 const password = passwordArg ?? randomToken(12);16 const secret = generateTotpSecret();17 const passwordHash = await hashPassword(password);18 const existing = await db.query.adminUsers.findFirst({ where: eq(adminUsers.username, username.toLowerCase()) });19 if (existing) {20 await db.update(adminUsers).set({ passwordHash, totpSecret: encrypt(secret), disabled: false }).where(eq(adminUsers.id, existing.id));21 console.log(`Admin "${username}" reset.`);22 } else {23 await db.insert(adminUsers).values({ username: username.toLowerCase(), passwordHash, totpSecret: encrypt(secret) });24 console.log(`Admin "${username}" created.`);25 }26 console.log(`Password: ${passwordArg ? "(as provided)" : password}`);27 console.log(`TOTP secret (base32): ${secret}`);28 console.log(`otpauth URL: ${otpauthUrl(username, secret)}`);29 console.log("Add the secret to an authenticator app now — it is not shown again.");30 await closeDb();31}3233main().catch((e) => {34 console.error(e);35 process.exit(1);36});37