Eingaben werden mit zod validiert, jede aendernde Aktion schreibt ins Audit-Log. Beim ersten Start entsteht ein Administrator samt Startwerten fuer die Produkterkennung; solange ein Erstpasswort gilt, bleiben schreibende Wege bis auf den Passwortwechsel gesperrt. Nicht-API-Pfade fallen auf die SPA zurueck, damit Deep-Links funktionieren.
64 lines
2.2 KiB
TypeScript
64 lines
2.2 KiB
TypeScript
import { randomBytes } from 'node:crypto';
|
|
import { ProductTarget } from '@prisma/client';
|
|
import { prisma } from './db.js';
|
|
import { env } from './env.js';
|
|
import { hashPassword } from './auth/password.js';
|
|
|
|
// Startpunkte fuer die Produkterkennung beim Import. Jederzeit im Admin-Bereich
|
|
// anpassbar, sobald die echten Produktnamen bekannt sind.
|
|
const DEFAULT_MAPPINGS = [
|
|
{ matchPattern: 'cc-paket', target: ProductTarget.CC_PACKAGE, priority: 10 },
|
|
{ matchPattern: 'cc paket', target: ProductTarget.CC_PACKAGE, priority: 11 },
|
|
{ matchPattern: 'custom character', target: ProductTarget.CC_PACKAGE, priority: 12 },
|
|
{ matchPattern: 'redesign', target: ProductTarget.REDESIGN_PACKAGE, priority: 20 },
|
|
];
|
|
|
|
export interface BootstrapResult {
|
|
createdAdmin: { username: string; password: string } | null;
|
|
createdMappings: number;
|
|
}
|
|
|
|
/**
|
|
* Legt beim allerersten Start einen Administrator an. Ohne gesetztes
|
|
* Bootstrap-Passwort wird eines erzeugt und einmalig ins Log geschrieben.
|
|
*/
|
|
export async function bootstrap(log: {
|
|
info: (msg: string) => void;
|
|
warn: (msg: string) => void;
|
|
}): Promise<BootstrapResult> {
|
|
const result: BootstrapResult = { createdAdmin: null, createdMappings: 0 };
|
|
|
|
const userCount = await prisma.user.count();
|
|
if (userCount === 0) {
|
|
const password = env.BOOTSTRAP_ADMIN_PASSWORD ?? randomBytes(12).toString('base64url');
|
|
|
|
await prisma.user.create({
|
|
data: {
|
|
username: env.BOOTSTRAP_ADMIN_USER,
|
|
passwordHash: await hashPassword(password),
|
|
role: 'ADMIN',
|
|
mustChangePassword: true,
|
|
},
|
|
});
|
|
|
|
result.createdAdmin = { username: env.BOOTSTRAP_ADMIN_USER, password };
|
|
|
|
log.warn('─'.repeat(60));
|
|
log.warn(`Administrator angelegt: ${env.BOOTSTRAP_ADMIN_USER}`);
|
|
if (!env.BOOTSTRAP_ADMIN_PASSWORD) {
|
|
log.warn(`Einmal-Passwort: ${password}`);
|
|
}
|
|
log.warn('Das Passwort muss bei der ersten Anmeldung geaendert werden.');
|
|
log.warn('─'.repeat(60));
|
|
}
|
|
|
|
const mappingCount = await prisma.productMapping.count();
|
|
if (mappingCount === 0) {
|
|
const created = await prisma.productMapping.createMany({ data: DEFAULT_MAPPINGS });
|
|
result.createdMappings = created.count;
|
|
log.info(`${created.count} Produkt-Mappings als Startwerte angelegt`);
|
|
}
|
|
|
|
return result;
|
|
}
|