← back to Govarbitrage
scripts/set-admin.ts
43 lines
// Set/reset the admin login credential without running the full seed.
// Usage:
// ADMIN_EMAIL=admin@agentabrams.com ADMIN_PASSWORD='...' npx tsx scripts/set-admin.ts
// (omit ADMIN_PASSWORD to GENERATE a strong random one and print it once)
// Never hardcodes a real password.
import { randomBytes } from "node:crypto";
import { prisma } from "../src/lib/db";
import { hashPassword } from "../src/lib/password";
// Readable strong password: 3 base32-ish blocks, ~120 bits.
function generatePassword(): string {
const alphabet = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789"; // no ambiguous 0/O/1/I
const bytes = randomBytes(18);
const chars = Array.from(bytes, (b) => alphabet[b % alphabet.length]);
return `${chars.slice(0, 6).join("")}-${chars.slice(6, 12).join("")}-${chars.slice(12, 18).join("")}`;
}
async function main() {
const email = process.env.ADMIN_EMAIL || process.argv[2] || "admin@agentabrams.com";
const provided = process.env.ADMIN_PASSWORD || process.argv[3];
const generated = !provided;
const password = provided || generatePassword();
const hash = hashPassword(password);
const user = await prisma.user.upsert({
where: { email },
update: { passwordHash: hash, role: "ADMIN" },
create: { email, name: "Admin", role: "ADMIN", passwordHash: hash },
});
console.log(`Admin credential set: ${user.email} (role ${user.role}).`);
if (generated) {
// Printed ONCE so the operator can capture it; never stored to disk/git.
console.log(`GENERATED_PASSWORD=${password}`);
}
await prisma.$disconnect();
}
main().catch(async (e) => {
console.error("[set-admin] ERROR:", e.message);
await prisma.$disconnect();
process.exit(1);
});