Add non-destructive dev seed on startup
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
"use server";
|
||||
|
||||
import { hash } from "bcryptjs";
|
||||
import { Prisma, UserRole } from "@prisma/client";
|
||||
import { z } from "zod";
|
||||
|
||||
import { signIn } from "@/auth";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
|
||||
// =====================================================================
|
||||
// /register · Server Action
|
||||
// ---------------------------------------------------------------------
|
||||
// Legt einen "heimatlosen" Admin-Account an (kitaId === null) und loggt
|
||||
// ihn direkt ein. Die Tenant-Erstellung folgt im Onboarding-Wizard.
|
||||
// =====================================================================
|
||||
|
||||
const registerSchema = z.object({
|
||||
email: z.string().email("Bitte eine gültige E-Mail-Adresse angeben.").toLowerCase().trim(),
|
||||
// bcrypt akzeptiert max. 72 Bytes — wir capen davor zur Sicherheit
|
||||
password: z
|
||||
.string()
|
||||
.min(8, "Mindestens 8 Zeichen.")
|
||||
.max(72, "Maximal 72 Zeichen."),
|
||||
firstName: z.string().min(1, "Pflichtfeld.").max(100).trim(),
|
||||
lastName: z.string().min(1, "Pflichtfeld.").max(100).trim(),
|
||||
// HTML-Checkboxen senden "on" wenn aktiviert, sonst nichts
|
||||
acceptPrivacyPolicy: z.literal("on", {
|
||||
error: "Bitte Datenschutzerklärung akzeptieren.",
|
||||
}),
|
||||
});
|
||||
|
||||
const PRIVACY_POLICY_VERSION = "2026-05-01";
|
||||
|
||||
export type RegisterState = {
|
||||
errors?: {
|
||||
email?: string[];
|
||||
password?: string[];
|
||||
firstName?: string[];
|
||||
lastName?: string[];
|
||||
acceptPrivacyPolicy?: string[];
|
||||
_form?: string[];
|
||||
};
|
||||
};
|
||||
|
||||
export async function registerAction(
|
||||
_prevState: RegisterState,
|
||||
formData: FormData,
|
||||
): Promise<RegisterState> {
|
||||
const parsed = registerSchema.safeParse(Object.fromEntries(formData));
|
||||
if (!parsed.success) {
|
||||
return { errors: parsed.error.flatten().fieldErrors };
|
||||
}
|
||||
const { email, password, firstName, lastName } = parsed.data;
|
||||
|
||||
const passwordHash = await hash(password, 12);
|
||||
const now = new Date();
|
||||
|
||||
try {
|
||||
await prisma.user.create({
|
||||
data: {
|
||||
email,
|
||||
passwordHash,
|
||||
firstName,
|
||||
lastName,
|
||||
// Gründer wird "heimatloser" Admin — beim Onboarding-Abschluss
|
||||
// wird die Rolle bestätigt und der User mit einer Kita verknüpft.
|
||||
role: UserRole.ADMIN,
|
||||
kitaId: null,
|
||||
privacyPolicyAcceptedAt: now,
|
||||
privacyPolicyVersion: PRIVACY_POLICY_VERSION,
|
||||
emailVerifiedAt: now,
|
||||
},
|
||||
});
|
||||
} catch (err) {
|
||||
if (
|
||||
err instanceof Prisma.PrismaClientKnownRequestError &&
|
||||
err.code === "P2002"
|
||||
) {
|
||||
return {
|
||||
errors: {
|
||||
email: ["Mit dieser E-Mail-Adresse existiert bereits ein Account."],
|
||||
},
|
||||
};
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
|
||||
// Direkt einloggen → wirft NEXT_REDIRECT, das wir nicht abfangen.
|
||||
await signIn("credentials", {
|
||||
email,
|
||||
password,
|
||||
redirectTo: "/onboarding",
|
||||
});
|
||||
|
||||
// Unreachable – signIn redirected.
|
||||
return {};
|
||||
}
|
||||
Reference in New Issue
Block a user