const router = require('express').Router(); const { body, query } = require('express-validator'); const prisma = require('../lib/prisma'); const auditMiddleware = require('../middleware/auditMiddleware'); const { validateRequest } = require('../middleware/errorHandler'); const animalValidation = [ body('experiment_id').notEmpty().withMessage('experiment_id is required').matches(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i).withMessage('experiment_id must be a valid UUID'), body('animal_id_string').trim().notEmpty().withMessage('animal_id_string is required'), body('animal_name').trim().notEmpty().withMessage('animal_name is required'), ]; const animalUpdateValidation = [ body('animal_id_string').optional().trim().notEmpty().withMessage('animal_id_string cannot be empty'), body('animal_name').optional().trim().notEmpty().withMessage('animal_name cannot be empty'), body('subject_info').optional({ nullable: true }).isObject().withMessage('subject_info must be an object'), ]; // GET /api/animals?experimentId= — list for experiment router.get( '/', [query('experimentId').optional().matches(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i).withMessage('experimentId must be a valid UUID')], validateRequest, async (req, res, next) => { try { const where = req.query.experimentId ? { experiment_id: req.query.experimentId } : {}; const animals = await prisma.animal.findMany({ where, orderBy: { animal_name: 'asc' }, include: { _count: { select: { daily_statuses: true } } }, }); res.json(animals); } catch (err) { next(err); } } ); // GET /api/animals/:id — get one with daily statuses router.get('/:id', async (req, res, next) => { try { const animal = await prisma.animal.findUnique({ where: { id: req.params.id }, include: { daily_statuses: { orderBy: { date: 'desc' } } }, }); if (!animal) return res.status(404).json({ error: 'Animal not found' }); res.json(animal); } catch (err) { next(err); } }); // POST /api/animals — create router.post('/', animalValidation, validateRequest, async (req, res, next) => { try { const { experiment_id, animal_id_string, animal_name } = req.body; const animal = await prisma.animal.create({ data: { experiment_id, animal_id_string, animal_name }, }); await prisma.auditLog.create({ data: { table_name: 'animals', record_id: animal.id, action: 'CREATE', changes: { before: null, after: animal }, }, }); res.status(201).json(animal); } catch (err) { next(err); } }); // PUT /api/animals/:id — update router.put( '/:id', auditMiddleware('animals', prisma.animal), animalUpdateValidation, validateRequest, async (req, res, next) => { try { const { animal_id_string, animal_name, subject_info } = req.body; const data = {}; if (animal_id_string !== undefined) data.animal_id_string = animal_id_string; if (animal_name !== undefined) data.animal_name = animal_name; if (subject_info !== undefined) data.subject_info = subject_info; const animal = await prisma.animal.update({ where: { id: req.params.id }, data, }); res.json(animal); } catch (err) { next(err); } } ); // DELETE /api/animals/:id — delete router.delete( '/:id', auditMiddleware('animals', prisma.animal), async (req, res, next) => { try { await prisma.animal.delete({ where: { id: req.params.id } }); res.status(204).send(); } catch (err) { next(err); } } ); module.exports = router;