96 lines
4.2 KiB
TypeScript
96 lines
4.2 KiB
TypeScript
import bcrypt from "bcryptjs";
|
|
import { PrismaClient } from "../src/generated/prisma/client";
|
|
import { PrismaClient as MySqlPrismaClient } from "../src/generated/prisma-mysql/client";
|
|
import { createDbAdapter } from "../src/lib/db-adapter";
|
|
import { isPasswordBreached } from "../src/lib/password-breach-check";
|
|
|
|
// Bewusst kein Import aus "@/lib/auth": das Modul zieht next/headers (cookies())
|
|
// nach sich, das ausserhalb eines Next.js-Request-Kontexts (hier: Standalone-
|
|
// Skript via tsx) fehlschlaegt. Der Hash-Rundenwert 12 ist mit auth.ts identisch.
|
|
const BCRYPT_ROUNDS = 12;
|
|
|
|
const adapter = createDbAdapter();
|
|
// Gleiches Prinzip wie lib/prisma.ts: zwei generierte Clients im Image,
|
|
// Auswahl zur Laufzeit ueber DATABASE_PROVIDER.
|
|
const prisma: PrismaClient =
|
|
(process.env.DATABASE_PROVIDER ?? "postgresql").trim() === "mysql"
|
|
? (new MySqlPrismaClient({ adapter }) as unknown as PrismaClient)
|
|
: new PrismaClient({ adapter });
|
|
|
|
/**
|
|
* Laeuft automatisch bei jedem Container-Start (siehe Dockerfile-CMD in der
|
|
* runner-Stage) — legt/erneuert NUR das Admin-Konto, keine Beispieldaten.
|
|
* Ein produktiv gebautes Image soll nie automatisch Fake-Produkte in einer
|
|
* echten Datenbank anlegen. Beispieldaten fuer lokales Testen/Screenshots:
|
|
* "pnpm db:seed:demo" (prisma/seed-demo.ts), manuell und getrennt.
|
|
*/
|
|
async function main() {
|
|
const adminEmail = process.env.ADMIN_EMAIL?.trim().toLowerCase();
|
|
const adminPassword = process.env.ADMIN_PASSWORD;
|
|
|
|
if (adminEmail && adminPassword) {
|
|
// Dieselben Mindestanforderungen wie bei der normalen Kundenregistrierung
|
|
// (registration.ts) — ein Admin-Konto hat Zugriff auf Bestellungen,
|
|
// Rueckerstattungen und Kundendaten und verdient keine schwaecheren
|
|
// Passwort-Regeln nur weil es per Seed statt per Formular angelegt wird.
|
|
if (adminPassword.length < 8) {
|
|
throw new Error("ADMIN_PASSWORD muss mindestens 8 Zeichen lang sein.");
|
|
}
|
|
if (await isPasswordBreached(adminPassword)) {
|
|
throw new Error(
|
|
"ADMIN_PASSWORD wurde in bekannten Datenlecks gefunden (Have I Been Pwned). Bitte ein anderes Passwort waehlen."
|
|
);
|
|
}
|
|
|
|
const existingAtEmail = await prisma.customer.findUnique({ where: { email: adminEmail } });
|
|
|
|
if (!existingAtEmail) {
|
|
await prisma.customer.create({
|
|
data: {
|
|
email: adminEmail,
|
|
passwordHash: await bcrypt.hash(adminPassword, BCRYPT_ROUNDS),
|
|
firstName: "Admin",
|
|
lastName: "Wabenhain",
|
|
role: "ADMIN",
|
|
},
|
|
});
|
|
console.log(`Admin-Konto angelegt: ${adminEmail} (Passwort aus ADMIN_PASSWORD).`);
|
|
} else if (existingAtEmail.role === "ADMIN") {
|
|
// Bereits Admin: Passwort-Rotation ueber ein geaendertes ADMIN_PASSWORD
|
|
// weiterhin unterstuetzen (dokumentierter Weg, ein Admin-Passwort zu
|
|
// erneuern) — unproblematisch, da das Konto schon Admin ist.
|
|
await prisma.customer.update({
|
|
where: { id: existingAtEmail.id },
|
|
data: { passwordHash: await bcrypt.hash(adminPassword, BCRYPT_ROUNDS) },
|
|
});
|
|
console.log(`Admin-Passwort synchronisiert: ${adminEmail}.`);
|
|
} else {
|
|
// Sicherheitsrelevant: seed befoerdert NIE automatisch ein bestehendes
|
|
// Nicht-Admin-Konto zu ADMIN, nur weil dessen E-Mail mit ADMIN_EMAIL
|
|
// uebereinstimmt — sonst koennte sich jemand die Admin-Mail vor dem
|
|
// ersten Deploy-Seed-Lauf selbst registrieren und beim naechsten
|
|
// "pnpm prisma db seed" automatisch Admin werden, mit selbstgewaehltem
|
|
// Passwort. Promotion eines bestehenden Kontos daher nur manuell ueber
|
|
// die Datenbank, siehe README "Admin-Zugang".
|
|
console.warn(
|
|
`WARNUNG: ${adminEmail} existiert bereits als Kundenkonto (nicht Admin) und wird NICHT automatisch befoerdert. ` +
|
|
'Siehe README, Abschnitt "Admin-Zugang", fuer die manuelle Befoerderung.'
|
|
);
|
|
}
|
|
} else {
|
|
console.log(
|
|
"Kein Admin-Konto angelegt: ADMIN_EMAIL/ADMIN_PASSWORD sind nicht gesetzt. " +
|
|
"Siehe README, wie ein bestehendes Konto nachtraeglich zu ADMIN befoerdert wird."
|
|
);
|
|
}
|
|
}
|
|
|
|
main()
|
|
.catch((error: unknown) => {
|
|
console.error(error);
|
|
process.exitCode = 1;
|
|
})
|
|
.finally(async () => {
|
|
await prisma.$disconnect();
|
|
});
|