196 lines
5.7 KiB
TypeScript
196 lines
5.7 KiB
TypeScript
"use server";
|
|
|
|
import { z } from "zod";
|
|
import { PERMS } from "@/lib/permissions";
|
|
import { prisma } from "@/lib/prisma";
|
|
import { adminAction } from "@/lib/safe-action";
|
|
import { ActionError, actionOk } from "@/lib/safe-action-shared";
|
|
import { logAudit } from "@/lib/services/audit";
|
|
import {
|
|
createEventSchema,
|
|
eventPrizeSchema,
|
|
eventTypeSchema,
|
|
eventWinnerSchema,
|
|
updateEventSchema,
|
|
} from "@/lib/validators/event";
|
|
|
|
// ── Event Types ─────────────────────────────────────────────────────
|
|
|
|
export const createEventType = adminAction(
|
|
{ permission: PERMS.EVENTS_EDIT, schema: eventTypeSchema },
|
|
async (ctx) => {
|
|
const eventType = await prisma.websiteEventType.create({
|
|
data: ctx.data,
|
|
});
|
|
logAudit({
|
|
userId: ctx.session.user.id,
|
|
action: "event_type_create",
|
|
target: "WebsiteEventType",
|
|
targetId: eventType.id,
|
|
after: { name: eventType.name },
|
|
});
|
|
return actionOk({ id: eventType.id });
|
|
},
|
|
);
|
|
|
|
const updateEventTypeInput = eventTypeSchema.partial().extend({
|
|
id: z.coerce.number().int().positive(),
|
|
});
|
|
|
|
export const updateEventType = adminAction(
|
|
{ permission: PERMS.EVENTS_EDIT, schema: updateEventTypeInput },
|
|
async (ctx) => {
|
|
const { id, ...data } = ctx.data;
|
|
const existing = await prisma.websiteEventType.findUnique({
|
|
where: { id },
|
|
});
|
|
if (!existing) throw new ActionError("Event type not found");
|
|
|
|
await prisma.websiteEventType.update({ where: { id }, data });
|
|
logAudit({
|
|
userId: ctx.session.user.id,
|
|
action: "event_type_update",
|
|
target: "WebsiteEventType",
|
|
targetId: id,
|
|
before: { name: existing.name },
|
|
after: data,
|
|
});
|
|
return actionOk({ id });
|
|
},
|
|
);
|
|
|
|
const deleteEventTypeInput = z.object({
|
|
id: z.coerce.number().int().positive(),
|
|
});
|
|
|
|
export const deleteEventType = adminAction(
|
|
{ permission: PERMS.EVENTS_EDIT, schema: deleteEventTypeInput },
|
|
async (ctx) => {
|
|
const existing = await prisma.websiteEventType.findUnique({
|
|
where: { id: ctx.data.id },
|
|
});
|
|
if (!existing) throw new ActionError("Event type not found");
|
|
|
|
await prisma.websiteEventType.delete({ where: { id: ctx.data.id } });
|
|
logAudit({
|
|
userId: ctx.session.user.id,
|
|
action: "event_type_delete",
|
|
target: "WebsiteEventType",
|
|
targetId: ctx.data.id,
|
|
before: { name: existing.name },
|
|
});
|
|
return actionOk();
|
|
},
|
|
);
|
|
|
|
// ── Events ──────────────────────────────────────────────────────────
|
|
|
|
export const createEvent = adminAction(
|
|
{ permission: PERMS.EVENTS_EDIT, schema: createEventSchema },
|
|
async (ctx) => {
|
|
const event = await prisma.websiteEvent.create({
|
|
data: {
|
|
...ctx.data,
|
|
hostUserId: Number(ctx.session.user.id),
|
|
},
|
|
});
|
|
logAudit({
|
|
userId: ctx.session.user.id,
|
|
action: "event_create",
|
|
target: "WebsiteEvent",
|
|
targetId: event.id,
|
|
after: { title: event.title },
|
|
});
|
|
return actionOk({ id: event.id });
|
|
},
|
|
);
|
|
|
|
const updateEventInput = updateEventSchema.extend({
|
|
id: z.coerce.number().int().positive(),
|
|
});
|
|
|
|
export const updateEvent = adminAction(
|
|
{ permission: PERMS.EVENTS_EDIT, schema: updateEventInput },
|
|
async (ctx) => {
|
|
const { id, ...data } = ctx.data;
|
|
const existing = await prisma.websiteEvent.findUnique({ where: { id } });
|
|
if (!existing) throw new ActionError("Event not found");
|
|
|
|
await prisma.websiteEvent.update({ where: { id }, data });
|
|
logAudit({
|
|
userId: ctx.session.user.id,
|
|
action: "event_update",
|
|
target: "WebsiteEvent",
|
|
targetId: id,
|
|
before: { title: existing.title, status: existing.status },
|
|
after: data,
|
|
});
|
|
return actionOk({ id });
|
|
},
|
|
);
|
|
|
|
const deleteEventInput = z.object({
|
|
id: z.coerce.number().int().positive(),
|
|
});
|
|
|
|
export const deleteEvent = adminAction(
|
|
{ permission: PERMS.EVENTS_EDIT, schema: deleteEventInput },
|
|
async (ctx) => {
|
|
const existing = await prisma.websiteEvent.findUnique({
|
|
where: { id: ctx.data.id },
|
|
});
|
|
if (!existing) throw new ActionError("Event not found");
|
|
|
|
await prisma.websiteEvent.delete({ where: { id: ctx.data.id } });
|
|
logAudit({
|
|
userId: ctx.session.user.id,
|
|
action: "event_delete",
|
|
target: "WebsiteEvent",
|
|
targetId: ctx.data.id,
|
|
before: { title: existing.title },
|
|
});
|
|
return actionOk();
|
|
},
|
|
);
|
|
|
|
// ── Prizes ──────────────────────────────────────────────────────────
|
|
|
|
export const addEventPrize = adminAction(
|
|
{ permission: PERMS.EVENTS_EDIT, schema: eventPrizeSchema },
|
|
async (ctx) => {
|
|
const prize = await prisma.websiteEventPrize.create({ data: ctx.data });
|
|
return actionOk({ id: prize.id });
|
|
},
|
|
);
|
|
|
|
const deletePrizeInput = z.object({ id: z.coerce.number().int().positive() });
|
|
|
|
export const deleteEventPrize = adminAction(
|
|
{ permission: PERMS.EVENTS_EDIT, schema: deletePrizeInput },
|
|
async (ctx) => {
|
|
await prisma.websiteEventPrize.delete({ where: { id: ctx.data.id } });
|
|
return actionOk();
|
|
},
|
|
);
|
|
|
|
// ── Winners ─────────────────────────────────────────────────────────
|
|
|
|
export const addEventWinner = adminAction(
|
|
{ permission: PERMS.EVENTS_EDIT, schema: eventWinnerSchema },
|
|
async (ctx) => {
|
|
const winner = await prisma.websiteEventWinner.create({ data: ctx.data });
|
|
logAudit({
|
|
userId: ctx.session.user.id,
|
|
action: "event_winner_add",
|
|
target: "WebsiteEventWinner",
|
|
targetId: winner.id,
|
|
after: {
|
|
eventId: ctx.data.eventId,
|
|
userId: ctx.data.userId,
|
|
position: ctx.data.position,
|
|
},
|
|
});
|
|
return actionOk({ id: winner.id });
|
|
},
|
|
);
|