150 lines
3.9 KiB
TypeScript
150 lines
3.9 KiB
TypeScript
import { Router, Request, Response, NextFunction } from 'express';
|
|
import trainingController from '../controllers/training.controller';
|
|
import { authenticate, optionalAuth } from '../middleware/auth.middleware';
|
|
import { requirePermission, getUserRole } from '../middleware/rbac.middleware';
|
|
|
|
const router = Router();
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// injectTeilnahmenFlag
|
|
//
|
|
// Sets req.canSeeTeilnahmen = true for Gruppenführer and above.
|
|
// Regular Mitglieder see only attendance counts; officers see the full list.
|
|
// ---------------------------------------------------------------------------
|
|
|
|
async function injectTeilnahmenFlag(
|
|
req: Request,
|
|
_res: Response,
|
|
next: NextFunction
|
|
): Promise<void> {
|
|
try {
|
|
if (req.user) {
|
|
const role = await getUserRole(req.user.id);
|
|
const ROLE_ORDER: Record<string, number> = {
|
|
bewerber: -1, mitglied: 0, gruppenfuehrer: 1, kommandant: 2, admin: 3,
|
|
};
|
|
(req as any).canSeeTeilnahmen =
|
|
(ROLE_ORDER[role] ?? 0) >= ROLE_ORDER.gruppenfuehrer;
|
|
}
|
|
} catch (_err) {
|
|
// Non-fatal — default to restricted view
|
|
}
|
|
next();
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Routes
|
|
// ---------------------------------------------------------------------------
|
|
|
|
/**
|
|
* GET /api/training
|
|
* Public list of upcoming events (limit param).
|
|
* Optional auth to include own RSVP status.
|
|
*/
|
|
router.get('/', optionalAuth, trainingController.getUpcoming);
|
|
|
|
/**
|
|
* GET /api/training/calendar?from=<ISO>&to=<ISO>
|
|
* Events in a date range for the calendar view.
|
|
* Optional auth to include own RSVP status.
|
|
*/
|
|
router.get('/calendar', optionalAuth, trainingController.getCalendarRange);
|
|
|
|
/**
|
|
* GET /api/training/calendar.ics?token=<calendarToken>
|
|
* iCal export — authenticated via per-user calendar token OR Bearer JWT.
|
|
* No `authenticate` enforced here; controller resolves auth itself.
|
|
* See training.controller.ts for full auth tradeoff discussion.
|
|
*/
|
|
router.get('/calendar.ics', optionalAuth, trainingController.getIcalExport);
|
|
|
|
/**
|
|
* GET /api/training/calendar-token
|
|
* Returns (or creates) the user's personal iCal subscribe token + URL.
|
|
* Requires authentication.
|
|
*/
|
|
router.get('/calendar-token', authenticate, trainingController.getCalendarToken);
|
|
|
|
/**
|
|
* GET /api/training/stats?year=<YYYY>
|
|
* Annual participation stats.
|
|
* Requires Kommandant or above (requirePermission('reports:read')).
|
|
*/
|
|
router.get(
|
|
'/stats',
|
|
authenticate,
|
|
requirePermission('reports:read'),
|
|
trainingController.getStats
|
|
);
|
|
|
|
/**
|
|
* GET /api/training/:id
|
|
* Single event with attendance counts.
|
|
* Gruppenführer+ also gets the full attendee list.
|
|
*/
|
|
router.get(
|
|
'/:id',
|
|
authenticate,
|
|
injectTeilnahmenFlag,
|
|
trainingController.getById
|
|
);
|
|
|
|
/**
|
|
* POST /api/training
|
|
* Create a new training event.
|
|
* Requires Gruppenführer or above (requirePermission('training:write')).
|
|
*/
|
|
router.post(
|
|
'/',
|
|
authenticate,
|
|
requirePermission('training:write'),
|
|
trainingController.createEvent
|
|
);
|
|
|
|
/**
|
|
* PATCH /api/training/:id
|
|
* Update an existing event.
|
|
* Requires Gruppenführer or above.
|
|
*/
|
|
router.patch(
|
|
'/:id',
|
|
authenticate,
|
|
requirePermission('training:write'),
|
|
trainingController.updateEvent
|
|
);
|
|
|
|
/**
|
|
* DELETE /api/training/:id
|
|
* Soft-cancel an event (sets abgesagt=true, records reason).
|
|
* Requires Kommandant or above.
|
|
*/
|
|
router.delete(
|
|
'/:id',
|
|
authenticate,
|
|
requirePermission('training:cancel'),
|
|
trainingController.cancelEvent
|
|
);
|
|
|
|
/**
|
|
* PATCH /api/training/:id/attendance
|
|
* Any authenticated member updates their own RSVP.
|
|
*/
|
|
router.patch(
|
|
'/:id/attendance',
|
|
authenticate,
|
|
trainingController.updateRsvp
|
|
);
|
|
|
|
/**
|
|
* POST /api/training/:id/attendance/mark
|
|
* Gruppenführer bulk-marks who actually appeared.
|
|
*/
|
|
router.post(
|
|
'/:id/attendance/mark',
|
|
authenticate,
|
|
requirePermission('training:mark_attendance'),
|
|
trainingController.markAttendance
|
|
);
|
|
|
|
export default router;
|