import { env } from "../config/env.js"; import { prisma } from "../db/prisma.js"; import { hashAdminPassword } from "../modules/admin/admin-auth.service.js"; /** * Creates the first (or an additional) `AdminUser` for the admin * application — there is no self-service admin signup, on purpose (see the * `AdminUser` model doc comment in schema.prisma). * * pnpm --filter api exec tsx src/scripts/create-admin.ts \ * --email=ops@example.com --password='...' --name='Ops' * * Each flag falls back to the matching `ADMIN_INITIAL_*` env var when * omitted, so a deployment can bake the first admin's credentials into its * environment and run this once from the container without passing args. * Refuses (exit 1) if an `AdminUser` with that email already exists — * changing an existing admin's password is a manual DB operation for now, * not something this script does. */ function flag(name: string): string | undefined { const prefix = `--${name}=`; const arg = process.argv.find((value) => value.startsWith(prefix)); return arg === undefined ? undefined : arg.slice(prefix.length); } async function createAdmin(): Promise { const email = (flag("email") ?? env.ADMIN_INITIAL_EMAIL)?.trim().toLowerCase(); const password = flag("password") ?? env.ADMIN_INITIAL_PASSWORD; const name = (flag("name") ?? env.ADMIN_INITIAL_NAME)?.trim(); if (!email || !password || !name) { throw new Error( "Missing required input. Provide --email, --password and --name (or set ADMIN_INITIAL_EMAIL / ADMIN_INITIAL_PASSWORD / ADMIN_INITIAL_NAME).", ); } if (password.length < 8) { throw new Error("Password must be at least 8 characters."); } const existing = await prisma.adminUser.findUnique({ where: { email } }); if (existing) { throw new Error(`An admin with email "${email}" already exists (id ${existing.id}).`); } const passwordHash = await hashAdminPassword(password); const admin = await prisma.adminUser.create({ data: { email, name, passwordHash } }); console.info(`Created admin #${admin.id} <${admin.email}> ("${admin.name}").`); } createAdmin() .then(() => prisma.$disconnect()) .catch(async (err) => { console.error(err instanceof Error ? err.message : err); await prisma.$disconnect(); process.exit(1); });