import express from 'express'; import path from 'path'; import * as aiKurzyController from '../controllers/ai-kurzy.controller.js'; import { authenticate } from '../middlewares/auth/authMiddleware.js'; import { requireAdmin } from '../middlewares/auth/roleMiddleware.js'; import { validateBody, validateParams, validateQuery } from '../middlewares/security/validateInput.js'; import { z } from 'zod'; import { createUpload, ALLOWED_FILE_TYPES } from '../config/upload.js'; const router = express.Router(); const upload = createUpload({ maxSizeMB: 10, allowedTypes: ALLOWED_FILE_TYPES, errorMessage: 'Nepovolený typ súboru. Povolené: PDF, Word, Excel, obrázky, CSV, TXT.', diskPath: path.join(process.cwd(), 'uploads', 'ai-kurzy'), }); // Validation schemas const kurzIdSchema = z.object({ kurzId: z.string().regex(/^\d+$/), }); const ucastnikIdSchema = z.object({ ucastnikId: z.string().regex(/^\d+$/), }); const registraciaIdSchema = z.object({ registraciaId: z.string().regex(/^\d+$/), }); const createKurzSchema = z.object({ nazov: z.string().min(1).max(255), typKurzu: z.string().min(1).max(100), popis: z.string().optional().nullable(), cena: z.string().or(z.number()), maxKapacita: z.number().int().positive().optional().nullable(), aktivny: z.boolean().optional(), farba: z.string().max(20).optional().nullable(), }); const updateKurzSchema = createKurzSchema.partial(); const createUcastnikSchema = z.object({ titul: z.string().max(50).optional().nullable(), meno: z.string().min(1).max(100), priezvisko: z.string().min(1).max(100), email: z.string().email().max(255), telefon: z.string().max(50).optional().nullable(), firma: z.string().max(255).optional().nullable(), firmaIco: z.string().max(20).optional().nullable(), firmaDic: z.string().max(20).optional().nullable(), firmaIcDph: z.string().max(25).optional().nullable(), firmaSidlo: z.string().optional().nullable(), mesto: z.string().max(100).optional().nullable(), ulica: z.string().max(255).optional().nullable(), psc: z.string().max(10).optional().nullable(), }); const updateUcastnikSchema = createUcastnikSchema.partial(); const createRegistraciaSchema = z.object({ kurzId: z.number().int().positive(), ucastnikId: z.number().int().positive(), datumOd: z.string().optional().nullable(), datumDo: z.string().optional().nullable(), formaKurzu: z.enum(['prezencne', 'online', 'hybridne']).optional(), pocetUcastnikov: z.number().int().positive().optional(), fakturaCislo: z.string().max(100).optional().nullable(), fakturaVystavena: z.boolean().optional(), zaplatene: z.boolean().optional(), stav: z.enum(['potencialny', 'registrovany', 'potvrdeny', 'absolvoval', 'zruseny']).optional(), poznamka: z.string().optional().nullable(), }); const updateRegistraciaSchema = createRegistraciaSchema.partial(); const registracieQuerySchema = z.object({ kurzId: z.string().regex(/^\d+$/).optional(), }); // All routes require authentication and admin role router.use(authenticate); router.use(requireAdmin); // ==================== STATISTICS ==================== router.get('/stats', aiKurzyController.getStats); // ==================== KURZY ==================== router.get('/kurzy', aiKurzyController.getAllKurzy); router.post( '/kurzy', validateBody(createKurzSchema), aiKurzyController.createKurz ); router.get( '/kurzy/:kurzId', validateParams(kurzIdSchema), aiKurzyController.getKurzById ); router.put( '/kurzy/:kurzId', validateParams(kurzIdSchema), validateBody(updateKurzSchema), aiKurzyController.updateKurz ); router.delete( '/kurzy/:kurzId', validateParams(kurzIdSchema), aiKurzyController.deleteKurz ); // ==================== UCASTNICI ==================== router.get('/ucastnici', aiKurzyController.getAllUcastnici); router.post( '/ucastnici', validateBody(createUcastnikSchema), aiKurzyController.createUcastnik ); router.get( '/ucastnici/:ucastnikId', validateParams(ucastnikIdSchema), aiKurzyController.getUcastnikById ); router.put( '/ucastnici/:ucastnikId', validateParams(ucastnikIdSchema), validateBody(updateUcastnikSchema), aiKurzyController.updateUcastnik ); router.delete( '/ucastnici/:ucastnikId', validateParams(ucastnikIdSchema), aiKurzyController.deleteUcastnik ); // ==================== REGISTRACIE ==================== router.get( '/registracie', validateQuery(registracieQuerySchema), aiKurzyController.getAllRegistracie ); router.post( '/registracie', validateBody(createRegistraciaSchema), aiKurzyController.createRegistracia ); router.get( '/registracie/:registraciaId', validateParams(registraciaIdSchema), aiKurzyController.getRegistraciaById ); router.put( '/registracie/:registraciaId', validateParams(registraciaIdSchema), validateBody(updateRegistraciaSchema), aiKurzyController.updateRegistracia ); router.delete( '/registracie/:registraciaId', validateParams(registraciaIdSchema), aiKurzyController.deleteRegistracia ); // ==================== COMBINED TABLE (Excel-style) ==================== router.get('/table', aiKurzyController.getCombinedTable); const updateFieldSchema = z.object({ field: z.string(), value: z.any(), }); router.patch( '/table/:registraciaId/field', validateParams(registraciaIdSchema), validateBody(updateFieldSchema), aiKurzyController.updateField ); // ==================== PRILOHY (Documents) ==================== const prilohaIdSchema = z.object({ prilohaId: z.string().regex(/^\d+$/), }); router.get( '/registracie/:registraciaId/prilohy', validateParams(registraciaIdSchema), aiKurzyController.getPrilohy ); router.post( '/registracie/:registraciaId/prilohy', validateParams(registraciaIdSchema), upload.single('file'), aiKurzyController.uploadPriloha ); router.delete( '/prilohy/:prilohaId', validateParams(prilohaIdSchema), aiKurzyController.deletePriloha ); export default router;