import type { FastifyInstance } from 'fastify'; import { z } from 'zod'; import { CreditKind, InactiveReason } from '@prisma/client'; import { requireEditor } from '../auth/guard.js'; import { createCharacter, deactivateCharacter, reactivateFree, reactivatePaid, redesignCharacter, revertCharacter, updateCharacter, } from '../services/characters.js'; import { contextOf, idParam, optionalNote, parseWith } from './helpers.js'; const redesignKind = z.enum([CreditKind.REDESIGN_PAID, CreditKind.REDESIGN_KULANZ]); const reviveKind = z.enum([CreditKind.CC, CreditKind.REDESIGN_PAID, CreditKind.REDESIGN_KULANZ]); export async function characterRoutes(app: FastifyInstance): Promise { app.post('/api/players/:id/characters', { preHandler: requireEditor }, async (request, reply) => { const { id } = parseWith(idParam, request.params); const body = parseWith( z.object({ name: z.string().trim().min(1).max(160), loreText: z.string().trim().max(5000).optional(), note: optionalNote, }), request.body, ); const result = await createCharacter(id, body, contextOf(request)); reply.code(201); return result; }); app.patch('/api/characters/:id', { preHandler: requireEditor }, async (request) => { const { id } = parseWith(idParam, request.params); const body = parseWith( z.object({ name: z.string().trim().min(1).max(160).optional(), loreText: z.string().trim().max(5000).nullable().optional(), }), request.body, ); return updateCharacter(id, body, contextOf(request)); }); app.post('/api/characters/:id/redesign', { preHandler: requireEditor }, async (request) => { const { id } = parseWith(idParam, request.params); const body = parseWith( z.object({ name: z.string().trim().min(1).max(160), loreText: z.string().trim().max(5000).optional(), useKind: redesignKind, note: optionalNote, }), request.body, ); return redesignCharacter(id, body, contextOf(request)); }); app.post('/api/characters/:id/deactivate', { preHandler: requireEditor }, async (request) => { const { id } = parseWith(idParam, request.params); const body = parseWith( z.object({ reason: z.enum([InactiveReason.PLAYER_INACTIVITY, InactiveReason.OTHER]), note: optionalNote, }), request.body, ); return deactivateCharacter(id, body.reason, contextOf(request), body.note); }); app.post('/api/characters/:id/reactivate-free', { preHandler: requireEditor }, async (request) => { const { id } = parseWith(idParam, request.params); return reactivateFree(id, contextOf(request)); }); app.post('/api/characters/:id/reactivate-paid', { preHandler: requireEditor }, async (request) => { const { id } = parseWith(idParam, request.params); const body = parseWith( z.object({ useKind: reviveKind, note: optionalNote }), request.body, ); return reactivatePaid(id, body.useKind, contextOf(request), body.note); }); app.post('/api/characters/:id/revert', { preHandler: requireEditor }, async (request) => { const { id } = parseWith(idParam, request.params); const body = parseWith( z.object({ reason: z.string().trim().min(1, 'Eine Begruendung ist Pflicht').max(500) }), request.body, ); return revertCharacter(id, body.reason, contextOf(request)); }); }