88 lines
3.4 KiB
TypeScript
88 lines
3.4 KiB
TypeScript
"use server";
|
|
|
|
import { revalidatePath } from "next/cache";
|
|
import { redirect } from "next/navigation";
|
|
import { z } from "zod";
|
|
import { auth } from "@/auth";
|
|
import { UserRole } from "@/generated/prisma/enums";
|
|
import { requireRole } from "@/lib/authorization";
|
|
import { prisma } from "@/lib/prisma";
|
|
|
|
async function audit(event: string, target: string, actor: string | null, details: unknown) {
|
|
console.log(JSON.stringify({ scope: "admin.organizations", event, target, actor, details, at: new Date().toISOString() }));
|
|
}
|
|
|
|
const slugRe = /^[a-z0-9](?:[a-z0-9-]{0,80}[a-z0-9])?$/;
|
|
|
|
const orgSchema = z.object({
|
|
name: z.string().trim().min(2).max(200),
|
|
slug: z.string().trim().regex(slugRe, "Slug invalide (a-z, 0-9, -)"),
|
|
description: z.string().trim().max(5000).optional().nullable(),
|
|
});
|
|
|
|
function parseFD(fd: FormData) {
|
|
return {
|
|
name: (fd.get("name") as string | null) ?? "",
|
|
slug: (fd.get("slug") as string | null) ?? "",
|
|
description: ((fd.get("description") as string | null) ?? "") || null,
|
|
};
|
|
}
|
|
|
|
export async function createOrganizationAction(fd: FormData) {
|
|
await requireRole([UserRole.ADMIN]);
|
|
const parsed = orgSchema.safeParse(parseFD(fd));
|
|
if (!parsed.success) {
|
|
return { ok: false as const, error: parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join(" · ") };
|
|
}
|
|
const session = await auth();
|
|
try {
|
|
const created = await prisma.organization.create({
|
|
data: { name: parsed.data.name, slug: parsed.data.slug, description: parsed.data.description ?? null },
|
|
});
|
|
await audit("organization.create", created.id, session?.user?.email ?? null, { slug: created.slug });
|
|
revalidatePath("/admin/organizations");
|
|
} catch (e) {
|
|
if (e instanceof Error && e.message.includes("Unique")) {
|
|
return { ok: false as const, error: "Ce slug existe déjà." };
|
|
}
|
|
return { ok: false as const, error: "Erreur lors de la création." };
|
|
}
|
|
redirect("/admin/organizations");
|
|
}
|
|
|
|
export async function updateOrganizationAction(id: string, fd: FormData) {
|
|
await requireRole([UserRole.ADMIN]);
|
|
const parsed = orgSchema.safeParse(parseFD(fd));
|
|
if (!parsed.success) {
|
|
return { ok: false as const, error: parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join(" · ") };
|
|
}
|
|
const session = await auth();
|
|
try {
|
|
await prisma.organization.update({
|
|
where: { id },
|
|
data: { name: parsed.data.name, slug: parsed.data.slug, description: parsed.data.description ?? null },
|
|
});
|
|
} catch (e) {
|
|
if (e instanceof Error && e.message.includes("Unique")) {
|
|
return { ok: false as const, error: "Ce slug est déjà pris." };
|
|
}
|
|
return { ok: false as const, error: "Erreur lors de la mise à jour." };
|
|
}
|
|
await audit("organization.update", id, session?.user?.email ?? null, { slug: parsed.data.slug });
|
|
revalidatePath("/admin/organizations");
|
|
revalidatePath(`/admin/organizations/${id}`);
|
|
return { ok: true as const };
|
|
}
|
|
|
|
export async function deleteOrganizationAction(id: string) {
|
|
await requireRole([UserRole.ADMIN]);
|
|
const session = await auth();
|
|
const count = await prisma.user.count({ where: { organizationId: id } });
|
|
if (count > 0) {
|
|
return { ok: false as const, error: `Impossible : ${count} membre(s) encore rattaché(s).` };
|
|
}
|
|
await prisma.organization.delete({ where: { id } });
|
|
await audit("organization.delete", id, session?.user?.email ?? null, {});
|
|
revalidatePath("/admin/organizations");
|
|
redirect("/admin/organizations");
|
|
}
|