import { PrismaClient } from '@prisma/client'; import { generateUsernameFromEmail, hashPassword, validateUsername, } from '@hexahost/auth'; const PLATFORM_ROLES = [ 'USER', 'SUPPORT', 'MODERATOR', 'ADMIN', 'SUPER_ADMIN', ] as const; interface CliArgs { email: string; password: string; username?: string; displayName?: string; } function parseArgs(argv: string[]): CliArgs { const args: Record = {}; for (let i = 0; i < argv.length; i += 1) { const key = argv[i]; if (key === '--') { continue; } const value = argv[i + 1]; if (key?.startsWith('--') && value) { args[key.slice(2)] = value; i += 1; } } if (!args['email'] || !args['password']) { console.error( 'Usage: bootstrap-admin --email --password [--username ] [--display-name ]', ); process.exit(1); } if (args['password'].length < 12) { console.error('Password must be at least 12 characters.'); process.exit(1); } return { email: args['email'].trim().toLowerCase(), password: args['password'], username: args['username'], displayName: args['display-name'], }; } async function ensurePlatformRoles(prisma: PrismaClient): Promise { for (const name of PLATFORM_ROLES) { await prisma.platformRole.upsert({ where: { name }, create: { name, description: `${name} platform role` }, update: {}, }); } } async function ensureUniqueUsername( prisma: PrismaClient, baseUsername: string, ): Promise { let candidate = baseUsername; let suffix = 0; while (true) { const existing = await prisma.user.findUnique({ where: { username: candidate }, select: { id: true }, }); if (!existing) { return candidate; } suffix += 1; const suffixText = String(suffix); candidate = `${baseUsername.slice(0, Math.max(1, 32 - suffixText.length))}${suffixText}`; } } async function main(): Promise { const input = parseArgs(process.argv.slice(2)); const prisma = new PrismaClient(); try { await ensurePlatformRoles(prisma); const existing = await prisma.user.findUnique({ where: { email: input.email }, }); if (existing) { const superAdminRole = await prisma.platformRole.findUnique({ where: { name: 'SUPER_ADMIN' }, }); if (!superAdminRole) { throw new Error('SUPER_ADMIN role missing after seed'); } await prisma.userPlatformRole.upsert({ where: { userId_roleId: { userId: existing.id, roleId: superAdminRole.id, }, }, create: { userId: existing.id, roleId: superAdminRole.id, }, update: {}, }); console.log(`User ${input.email} already exists — SUPER_ADMIN role ensured.`); return; } let username = input.username?.trim().toLowerCase(); if (username) { const validation = validateUsername(username); if (!validation.valid) { throw new Error(validation.error ?? 'Invalid username'); } } else { username = generateUsernameFromEmail(input.email); } username = await ensureUniqueUsername(prisma, username); const passwordHash = await hashPassword(input.password); const superAdminRole = await prisma.platformRole.findUniqueOrThrow({ where: { name: 'SUPER_ADMIN' }, }); const user = await prisma.$transaction(async (tx) => { const created = await tx.user.create({ data: { email: input.email, username, displayName: input.displayName ?? 'Administrator', emailVerifiedAt: new Date(), credentials: { create: { passwordHash }, }, platformRoles: { create: { roleId: superAdminRole.id }, }, }, }); await tx.auditEvent.create({ data: { userId: created.id, action: 'admin.bootstrap', entityType: 'user', entityId: created.id, metadata: { source: 'bootstrap-cli' }, }, }); return created; }); console.log(`Admin user created: ${user.email} (${user.id})`); } finally { await prisma.$disconnect(); } } void main().catch((error: unknown) => { console.error(error instanceof Error ? error.message : error); process.exit(1); });