since3-portal/modules/shared/actions/credentials.ts
2026-09-29 13:05:38 +02:00

143 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 { getClientId } from "@/modules/shared/queries/profile";
const CredentialSchema = z.object({
profileId: z.string().uuid(),
service: z.string().min(1, { error: "Dienst ist erforderlich." }),
username: z.string().min(1, { error: "Benutzername ist erforderlich." }),
password: z.string().min(1, { error: "Passwort ist erforderlich." }),
});
export type CredentialState = { error: string } | { success: true } | undefined;
export async function createCredential(
_prevState: CredentialState,
formData: FormData
): Promise<CredentialState> {
const session = await verifySession();
const profileId = String(formData.get("profileId") ?? "");
if (!session.isAdmin && session.userId !== profileId) {
return { error: "Keine Berechtigung." };
}
const validatedFields = CredentialSchema.safeParse({
profileId,
service: formData.get("service"),
username: formData.get("username"),
password: formData.get("password"),
});
if (!validatedFields.success) {
const { fieldErrors } = z.flattenError(validatedFields.error);
const firstError = Object.values(fieldErrors).flat()[0];
return { error: firstError ?? "Bitte alle Felder ausfüllen." };
}
const clientId = await getClientId(profileId);
if (!clientId) {
return { error: "Client hat noch keine Stammdaten hinterlegt." };
}
const supabase = await createClient();
const { error } = await supabase.rpc("create_credential", {
p_client_id: clientId,
p_service: validatedFields.data.service,
p_username: validatedFields.data.username,
p_password: validatedFields.data.password,
});
if (error) return { error: "Eintrag konnte nicht gespeichert werden." };
revalidatePath("/client/passwords");
revalidatePath(`/admin/clients/${profileId}/passwords`);
return { success: true };
}
const UpdateCredentialSchema = z.object({
profileId: z.string().uuid(),
credentialId: z.string().uuid(),
service: z.string().min(1, { error: "Dienst ist erforderlich." }),
username: z.string().min(1, { error: "Benutzername ist erforderlich." }),
password: z.string().optional(),
});
export async function updateCredential(
_prevState: CredentialState,
formData: FormData
): Promise<CredentialState> {
const session = await verifySession();
const profileId = String(formData.get("profileId") ?? "");
if (!session.isAdmin && session.userId !== profileId) {
return { error: "Keine Berechtigung." };
}
const password = String(formData.get("password") ?? "").trim();
const validatedFields = UpdateCredentialSchema.safeParse({
profileId,
credentialId: formData.get("credentialId"),
service: formData.get("service"),
username: formData.get("username"),
password: password === "" ? undefined : password,
});
if (!validatedFields.success) {
const { fieldErrors } = z.flattenError(validatedFields.error);
const firstError = Object.values(fieldErrors).flat()[0];
return { error: firstError ?? "Bitte alle Felder ausfüllen." };
}
const supabase = await createClient();
const { error } = await supabase.rpc("update_credential", {
p_id: validatedFields.data.credentialId,
p_service: validatedFields.data.service,
p_username: validatedFields.data.username,
p_password: validatedFields.data.password ?? null,
});
if (error) return { error: "Eintrag konnte nicht aktualisiert werden." };
revalidatePath("/client/passwords");
revalidatePath(`/admin/clients/${profileId}/passwords`);
return { success: true };
}
export async function revealCredential(credentialId: string): Promise<{ password: string } | { error: string }> {
await verifySession();
const supabase = await createClient();
const { data, error } = await supabase.rpc("reveal_credential", { p_id: credentialId });
if (error || typeof data !== "string") {
return { error: "Passwort konnte nicht abgerufen werden." };
}
return { password: data };
}
export async function deleteCredential(profileId: string, credentialId: string): Promise<CredentialState> {
const session = await verifySession();
if (!session.isAdmin) {
return { error: "Keine Berechtigung." };
}
const supabase = createAdminClient();
const { error } = await supabase.from("credentials").delete().eq("id", credentialId);
if (error) return { error: "Eintrag konnte nicht gelöscht werden." };
revalidatePath(`/admin/clients/${profileId}/passwords`);
return { success: true };
}