refactor: extract business logic from routes into reusable service modules

This commit is contained in:
Maurice
2026-04-02 17:14:53 +02:00
parent f0131632a7
commit 979322025d
48 changed files with 8851 additions and 6182 deletions
+14 -41
View File
@@ -1,48 +1,33 @@
import express, { Request, Response } from 'express';
import { db, canAccessTrip } from '../db/database';
import { authenticate } from '../middleware/auth';
import { broadcast } from '../websocket';
import { validateStringLengths } from '../middleware/validate';
import { checkPermission } from '../services/permissions';
import { AuthRequest, DayNote } from '../types';
import { AuthRequest } from '../types';
import * as dayNoteService from '../services/dayNoteService';
const router = express.Router({ mergeParams: true });
function verifyAccess(tripId: string | number, userId: number) {
return canAccessTrip(tripId, userId);
}
router.get('/', authenticate, (req: Request, res: Response) => {
const authReq = req as AuthRequest;
const { tripId, dayId } = req.params;
if (!verifyAccess(tripId, authReq.user.id)) return res.status(404).json({ error: 'Trip not found' });
const notes = db.prepare(
'SELECT * FROM day_notes WHERE day_id = ? AND trip_id = ? ORDER BY sort_order ASC, created_at ASC'
).all(dayId, tripId);
res.json({ notes });
if (!dayNoteService.verifyTripAccess(tripId, authReq.user.id)) return res.status(404).json({ error: 'Trip not found' });
res.json({ notes: dayNoteService.listNotes(dayId, tripId) });
});
router.post('/', authenticate, validateStringLengths({ text: 500, time: 150 }), (req: Request, res: Response) => {
const authReq = req as AuthRequest;
const { tripId, dayId } = req.params;
const access = verifyAccess(tripId, authReq.user.id);
const access = dayNoteService.verifyTripAccess(tripId, authReq.user.id);
if (!access) return res.status(404).json({ error: 'Trip not found' });
if (!checkPermission('day_edit', authReq.user.role, access.user_id, authReq.user.id, access.user_id !== authReq.user.id))
return res.status(403).json({ error: 'No permission' });
const day = db.prepare('SELECT id FROM days WHERE id = ? AND trip_id = ?').get(dayId, tripId);
if (!day) return res.status(404).json({ error: 'Day not found' });
if (!dayNoteService.dayExists(dayId, tripId)) return res.status(404).json({ error: 'Day not found' });
const { text, time, icon, sort_order } = req.body;
if (!text?.trim()) return res.status(400).json({ error: 'Text required' });
const result = db.prepare(
'INSERT INTO day_notes (day_id, trip_id, text, time, icon, sort_order) VALUES (?, ?, ?, ?, ?, ?)'
).run(dayId, tripId, text.trim(), time || null, icon || '\uD83D\uDCDD', sort_order ?? 9999);
const note = db.prepare('SELECT * FROM day_notes WHERE id = ?').get(result.lastInsertRowid);
const note = dayNoteService.createNote(dayId, tripId, text, time, icon, sort_order);
res.status(201).json({ note });
broadcast(tripId, 'dayNote:created', { dayId: Number(dayId), note }, req.headers['x-socket-id'] as string);
});
@@ -50,26 +35,16 @@ router.post('/', authenticate, validateStringLengths({ text: 500, time: 150 }),
router.put('/:id', authenticate, validateStringLengths({ text: 500, time: 150 }), (req: Request, res: Response) => {
const authReq = req as AuthRequest;
const { tripId, dayId, id } = req.params;
const access = verifyAccess(tripId, authReq.user.id);
const access = dayNoteService.verifyTripAccess(tripId, authReq.user.id);
if (!access) return res.status(404).json({ error: 'Trip not found' });
if (!checkPermission('day_edit', authReq.user.role, access.user_id, authReq.user.id, access.user_id !== authReq.user.id))
return res.status(403).json({ error: 'No permission' });
const note = db.prepare('SELECT * FROM day_notes WHERE id = ? AND day_id = ? AND trip_id = ?').get(id, dayId, tripId) as DayNote | undefined;
if (!note) return res.status(404).json({ error: 'Note not found' });
const current = dayNoteService.getNote(id, dayId, tripId);
if (!current) return res.status(404).json({ error: 'Note not found' });
const { text, time, icon, sort_order } = req.body;
db.prepare(
'UPDATE day_notes SET text = ?, time = ?, icon = ?, sort_order = ? WHERE id = ?'
).run(
text !== undefined ? text.trim() : note.text,
time !== undefined ? time : note.time,
icon !== undefined ? icon : note.icon,
sort_order !== undefined ? sort_order : note.sort_order,
id
);
const updated = db.prepare('SELECT * FROM day_notes WHERE id = ?').get(id);
const updated = dayNoteService.updateNote(id, current, { text, time, icon, sort_order });
res.json({ note: updated });
broadcast(tripId, 'dayNote:updated', { dayId: Number(dayId), note: updated }, req.headers['x-socket-id'] as string);
});
@@ -77,15 +52,13 @@ router.put('/:id', authenticate, validateStringLengths({ text: 500, time: 150 })
router.delete('/:id', authenticate, (req: Request, res: Response) => {
const authReq = req as AuthRequest;
const { tripId, dayId, id } = req.params;
const access = verifyAccess(tripId, authReq.user.id);
const access = dayNoteService.verifyTripAccess(tripId, authReq.user.id);
if (!access) return res.status(404).json({ error: 'Trip not found' });
if (!checkPermission('day_edit', authReq.user.role, access.user_id, authReq.user.id, access.user_id !== authReq.user.id))
return res.status(403).json({ error: 'No permission' });
const note = db.prepare('SELECT id FROM day_notes WHERE id = ? AND day_id = ? AND trip_id = ?').get(id, dayId, tripId);
if (!note) return res.status(404).json({ error: 'Note not found' });
db.prepare('DELETE FROM day_notes WHERE id = ?').run(id);
if (!dayNoteService.getNote(id, dayId, tripId)) return res.status(404).json({ error: 'Note not found' });
dayNoteService.deleteNote(id);
res.json({ success: true });
broadcast(tripId, 'dayNote:deleted', { noteId: Number(id), dayId: Number(dayId) }, req.headers['x-socket-id'] as string);
});