#!/usr/bin/env node const bcrypt = require('bcryptjs') const path = require('node:path') const { ensureDatabaseUrl, loadDefaultEnvFiles, normalizeDatabaseUrlForExecution, } = require('../packages/database/src/runtime-config') const ADMIN_ROLES = new Set(['SUPER_ADMIN', 'ADMIN', 'SUPPORT', 'FINANCE', 'VIEWER']) function readArg(name) { const prefix = `--${name}=` const match = process.argv.slice(2).find((arg) => arg.startsWith(prefix)) return match ? match.slice(prefix.length) : undefined } function readValue(argName, envName, fallback) { return readArg(argName) ?? process.env[envName] ?? fallback } function requireValue(argName, envName) { const value = readValue(argName, envName) if (!value || !value.trim()) { throw new Error(`Missing required value. Provide --${argName}=... or ${envName}=...`) } return value.trim() } function readBoolean(argName, envName, fallback = false) { const value = readValue(argName, envName) if (value === undefined) return fallback return ['1', 'true', 'yes', 'y'].includes(String(value).toLowerCase()) } async function main() { loadDefaultEnvFiles(path.resolve(__dirname, '..')) ensureDatabaseUrl() normalizeDatabaseUrlForExecution() const email = requireValue('email', 'ADMIN_EMAIL').toLowerCase() const checkOnly = process.argv.includes('--check') const { PrismaClient } = require('../packages/database/generated') const prisma = new PrismaClient() try { const existing = await prisma.adminUser.findUnique({ where: { email } }) if (checkOnly) { console.log(existing ? 'EXISTS' : 'MISSING') process.exitCode = existing ? 0 : 1 return } const password = requireValue('password', 'ADMIN_PASSWORD') const firstName = readValue('first-name', 'ADMIN_FIRST_NAME', 'Admin').trim() const lastName = readValue('last-name', 'ADMIN_LAST_NAME', 'User').trim() const role = readValue('role', 'ADMIN_ROLE', 'SUPER_ADMIN').trim().toUpperCase() const updateExisting = readBoolean('update-existing', 'ADMIN_UPDATE_EXISTING', false) if (!ADMIN_ROLES.has(role)) { throw new Error(`Invalid ADMIN_ROLE "${role}". Use one of: ${Array.from(ADMIN_ROLES).join(', ')}`) } if (password.length < 8) { throw new Error('Admin password must be at least 8 characters long.') } const passwordHash = await bcrypt.hash(password, 12) if (existing) { if (!updateExisting) { console.log(`Admin user already exists: ${email}`) console.log('Set ADMIN_UPDATE_EXISTING=true or pass --update-existing=true to update the name, role, password, and active status.') return } const admin = await prisma.adminUser.update({ where: { email }, data: { firstName, lastName, role, passwordHash, isActive: true, passwordResetToken: null, passwordResetExpiresAt: null, }, }) console.log(`Updated ${admin.role}: ${admin.email} (id: ${admin.id})`) return } const admin = await prisma.adminUser.create({ data: { email, firstName, lastName, passwordHash, role, isActive: true, }, }) console.log(`Created ${admin.role}: ${admin.email} (id: ${admin.id})`) } finally { await prisma.$disconnect() } } main().catch((error) => { console.error(error.message || error) process.exit(1) })