mirror of
https://github.com/Since-3/since3-portal.git
synced 2026-10-12 21:41:16 +00:00
109 lines
4.8 KiB
TypeScript
109 lines
4.8 KiB
TypeScript
"use server";
|
||
|
||
import * as z from "zod";
|
||
import { revalidatePath } from "next/cache";
|
||
import { createAdminClient } from "@/lib/supabase/admin";
|
||
import { createClient } from "@/lib/supabase/server";
|
||
import { verifySession } from "@/lib/supabase/dal";
|
||
import { generatePassword } from "@/lib/generate-password";
|
||
import { MIN_PASSWORD_LENGTH } from "@/modules/admin/lib/constants";
|
||
import { CLIENT_STATUSES } from "@/modules/admin/lib/client-status";
|
||
|
||
export type ResetPasswordState = { error: string } | { success: true; password: string } | undefined;
|
||
export type UpdateClientState = { error: string } | { success: true } | undefined;
|
||
|
||
const IdSchema = z.string().uuid();
|
||
|
||
// Nur Admins, und nur Konten mit Rolle "client" – Admin-Konten lassen sich hierüber nicht ändern.
|
||
async function assertClientTarget(profileId: string) {
|
||
const session = await verifySession();
|
||
if (!session.isAdmin) return { error: "Keine Berechtigung." };
|
||
if (!IdSchema.safeParse(profileId).success) return { error: "Ungültiger Client." };
|
||
|
||
const supabase = await createClient();
|
||
const { data } = await supabase.from("profiles").select("role").eq("id", profileId).maybeSingle();
|
||
if (data?.role !== "client") return { error: "Client nicht gefunden." };
|
||
|
||
return null;
|
||
}
|
||
|
||
const ResetSchema = z.discriminatedUnion("mode", [
|
||
z.object({ mode: z.literal("generate") }),
|
||
z.object({
|
||
mode: z.literal("manual"),
|
||
password: z.string().min(MIN_PASSWORD_LENGTH, { error: `Mindestens ${MIN_PASSWORD_LENGTH} Zeichen.` }).max(72),
|
||
}),
|
||
]);
|
||
|
||
export async function resetClientPassword(_prev: ResetPasswordState, formData: FormData): Promise<ResetPasswordState> {
|
||
const profileId = String(formData.get("profileId") ?? "");
|
||
const denied = await assertClientTarget(profileId);
|
||
if (denied) return denied;
|
||
|
||
const parsed = ResetSchema.safeParse({
|
||
mode: formData.get("mode"),
|
||
password: formData.get("password") ?? undefined,
|
||
});
|
||
if (!parsed.success) return { error: parsed.error.issues[0]?.message ?? "Ungültige Eingabe." };
|
||
|
||
const password = parsed.data.mode === "generate" ? generatePassword() : parsed.data.password;
|
||
|
||
const admin = createAdminClient();
|
||
const { error } = await admin.auth.admin.updateUserById(profileId, { password });
|
||
if (error) return { error: "Passwort konnte nicht geändert werden." };
|
||
|
||
return { success: true, password };
|
||
}
|
||
|
||
const UpdateSchema = z.object({
|
||
name: z.string().trim().min(3, { error: "Mindestens drei Zeichen eingeben." }),
|
||
email: z.email({ error: "Bitte eine gültige Email eingeben." }),
|
||
});
|
||
|
||
export async function updateClientAccount(_prev: UpdateClientState, formData: FormData): Promise<UpdateClientState> {
|
||
const profileId = String(formData.get("profileId") ?? "");
|
||
const denied = await assertClientTarget(profileId);
|
||
if (denied) return denied;
|
||
|
||
const parsed = UpdateSchema.safeParse({ name: formData.get("name"), email: formData.get("email") });
|
||
if (!parsed.success) return { error: parsed.error.issues[0]?.message ?? "Ungültige Eingabe." };
|
||
|
||
const email = parsed.data.email.toLowerCase();
|
||
|
||
// Login-Email lebt in auth.users; ohne email_confirm würde Supabase erst eine Bestätigungsmail verlangen.
|
||
const admin = createAdminClient();
|
||
const { error: authError } = await admin.auth.admin.updateUserById(profileId, {
|
||
email,
|
||
email_confirm: true,
|
||
user_metadata: { name: parsed.data.name },
|
||
});
|
||
if (authError) {
|
||
return { error: authError.code === "email_exists" ? "Diese Email ist bereits vergeben." : "Client konnte nicht aktualisiert werden." };
|
||
}
|
||
|
||
const supabase = await createClient();
|
||
const { error } = await supabase.from("profiles").update({ name: parsed.data.name, email }).eq("id", profileId);
|
||
if (error) return { error: "Login-Email wurde geändert, das Profil aber nicht. Bitte erneut speichern." };
|
||
|
||
revalidatePath("/admin/clients");
|
||
revalidatePath(`/admin/clients/${profileId}`, "layout");
|
||
return { success: true };
|
||
}
|
||
|
||
const StatusSchema = z.enum(CLIENT_STATUSES, { error: "Bitte einen gültigen Status wählen." });
|
||
|
||
export async function updateClientStatus(_prev: UpdateClientState, formData: FormData): Promise<UpdateClientState> {
|
||
const profileId = String(formData.get("profileId") ?? "");
|
||
const denied = await assertClientTarget(profileId);
|
||
if (denied) return denied;
|
||
|
||
const parsed = StatusSchema.safeParse(formData.get("status"));
|
||
if (!parsed.success) return { error: parsed.error.issues[0]?.message ?? "Ungültige Eingabe." };
|
||
|
||
const supabase = await createClient();
|
||
const { error } = await supabase.from("profiles").update({ status: parsed.data }).eq("id", profileId);
|
||
if (error) return { error: "Status konnte nicht gespeichert werden." };
|
||
|
||
revalidatePath("/admin/clients");
|
||
return { success: true };
|
||
}
|