/* SPDX-FileCopyrightText: © 2025 Hornwitser SPDX-License-Identifier: AGPL-3.0-or-later */ import { z } from "zod/v4-mini"; import { readSchedule, writeSchedule } from "~/server/database"; import { broadcastEvent } from "~/server/streams"; import { apiScheduleSchema } from "~/shared/types/api"; import { applyUpdatesToArray } from "~/shared/utils/update"; export default defineEventHandler(async (event) => { const session = await requireServerSession(event); if (session.account.type !== "admin" && session.account.type !== "crew") { throw createError({ status: 403, statusMessage: "Forbidden", message: "Only crew and admin accounts can edit the schedule.", }); } const { success, error, data: update } = apiScheduleSchema.safeParse(await readBody(event)); if (!success) { throw createError({ status: 400, statusText: "Bad Request", message: z.prettifyError(error), }); } if (update.deleted) { throw createError({ statusCode: 400, statusMessage: "Not implemented", }); } const schedule = await readSchedule(); if (schedule.deleted) { throw createError({ statusCode: 400, statusMessage: "Not implemented", }); } // Validate edit restrictions for crew if (session.account.type === "crew") { if (update.locations?.length) { throw createError({ status: 403, statusMessage: "Forbidden", message: "Only admin accounts can edit locations.", }); } for (const event of update.events ?? []) { const original = schedule.events?.find(e => e.id === event.id); if (original && !original.deleted && !original.crew) { throw createError({ status: 403, statusMessage: "Forbidden", message: "Only admin accounts can edit public events.", }); } } } // Update schedule const updatedFrom = schedule.updatedAt; update.updatedAt = new Date().toISOString(); if (update.events) { for (const event of update.events) event.updatedAt = update.updatedAt; applyUpdatesToArray(update.events, schedule.events = schedule.events ?? []); } if (update.locations) { for (const location of update.locations) location.updatedAt = update.updatedAt; applyUpdatesToArray(update.locations, schedule.locations = schedule.locations ?? []); } if (update.roles) { for (const role of update.roles) role.updatedAt = update.updatedAt; applyUpdatesToArray(update.roles, schedule.roles = schedule.roles ?? []); } if (update.shifts) { for (const shift of update.shifts) shift.updatedAt = update.updatedAt; applyUpdatesToArray(update.shifts, schedule.shifts = schedule.shifts ?? []); } await writeSchedule(schedule); await broadcastEvent({ type: "schedule-update", updatedFrom, data: update, }); })