import "dotenv/config"; import { PrismaClient } from "@prisma/client"; import { PrismaPg } from "@prisma/adapter-pg"; import { provisionTenant } from "@/server/provision"; /** * Erst-Bootstrap für Produktiv: legt den ersten Plattform-Admin + einen Mandanten an * — als Ersatz für den Demo-Seed (der NUR für Test/Dev ist). Ohne diesen Schritt gibt * es in Prod keinen Benutzer und niemand kann sich anmelden. * * Ausführung: im migrate-Job nach `prisma migrate deploy`, gesteuert per BOOTSTRAP_ADMIN=true * (siehe docker-compose.coolify.yml und docs/DEPLOY-PROD-CONTABO.md). * * Idempotent: existiert bereits ein Plattform-Admin mit der E-Mail, passiert nichts * (kein Passwort-Reset, keine Duplikate). provisionTenant selbst arbeitet mit upserts. * * Erforderliche Umgebungsvariablen: * BOOTSTRAP_ADMIN_EMAIL E-Mail des ersten Plattform-Admins * BOOTSTRAP_ADMIN_PASSWORD Initiales Passwort (nach erstem Login ändern) * BOOTSTRAP_ADMIN_NAME Anzeigename * BOOTSTRAP_TENANT_NAME Name des ersten Mandanten * BOOTSTRAP_TENANT_SLUG URL-Slug des Mandanten (klein, eindeutig) * Optional: * BOOTSTRAP_TENANT_SHORT Kürzel des Mandanten * BOOTSTRAP_TENANT_SECTOR Branche */ const prisma = new PrismaClient({ adapter: new PrismaPg({ connectionString: process.env.DATABASE_URL }), }); function req(name: string): string { const v = process.env[name]; if (!v || v.trim() === "") { console.error(`✖ Bootstrap abgebrochen: Umgebungsvariable ${name} fehlt.`); process.exit(1); } return v.trim(); } async function main() { const email = req("BOOTSTRAP_ADMIN_EMAIL").toLowerCase(); const password = req("BOOTSTRAP_ADMIN_PASSWORD"); const name = req("BOOTSTRAP_ADMIN_NAME"); const tenantName = req("BOOTSTRAP_TENANT_NAME"); const tenantSlug = req("BOOTSTRAP_TENANT_SLUG").toLowerCase(); const short = process.env.BOOTSTRAP_TENANT_SHORT?.trim() || undefined; const sector = process.env.BOOTSTRAP_TENANT_SECTOR?.trim() || undefined; // Idempotenz: existiert der Plattform-Admin schon, nichts tun (kein PW-Reset). const existing = await prisma.user.findFirst({ where: { email, isPlatformAdmin: true }, select: { id: true }, }); if (existing) { console.log(`✔ Plattform-Admin ${email} existiert bereits — Bootstrap übersprungen.`); return; } const tenant = await provisionTenant(prisma, { name: tenantName, slug: tenantSlug, short, sector, admin: { email, name, password, isPlatformAdmin: true }, }); console.log( `✔ Bootstrap fertig: Mandant "${tenant.name}" (${tenantSlug}) + Plattform-Admin ${email} angelegt.` ); console.log(" → Bitte nach dem ersten Login das Passwort ändern."); } main() .then(() => prisma.$disconnect()) .catch(async (e) => { console.error(e); await prisma.$disconnect(); process.exit(1); });