import { Router, Request, Response, NextFunction } from 'express'; import { authenticate } from '../../middleware/auth.middleware'; import { requireRole } from '../../middleware/rbac.middleware'; import { validate } from '../../middleware/validate'; import { quickJoinRateLimit } from '../../middleware/rate-limit'; import { volunteerInviteService } from './volunteer-invite.service'; import { generateInviteSchema, redeemInviteSchema } from './volunteer-invite.schemas'; import { env } from '../../config/env'; const router = Router(); // POST /api/volunteer-invite/generate — Admin-only: create a signed invite token router.post( '/generate', authenticate, requireRole('SUPER_ADMIN', 'MAP_ADMIN', 'INFLUENCE_ADMIN'), validate(generateInviteSchema), async (req: Request, res: Response, next: NextFunction) => { try { const { cutId, shiftId } = req.body; const token = volunteerInviteService.generateInviteToken(req.user!.id, cutId, shiftId); res.json({ token }); } catch (err) { next(err); } }, ); // POST /api/volunteer-invite/redeem — Public: redeem an invite token router.post( '/redeem', quickJoinRateLimit, validate(redeemInviteSchema), async (req: Request, res: Response, next: NextFunction) => { try { const result = await volunteerInviteService.redeemInvite(req.body); // Set refresh token as httpOnly cookie (Secure flag based on actual protocol) res.cookie('cml_refresh', result.tokens.refreshToken, { httpOnly: true, secure: req.secure, sameSite: req.secure ? 'strict' : 'lax', maxAge: 7 * 24 * 60 * 60 * 1000, path: '/api/auth', }); res.json({ accessToken: result.tokens.accessToken, cutId: result.cutId, shiftId: result.shiftId, }); } catch (err) { next(err); } }, ); export { router as volunteerInviteRouter };