changemaker.lite/api/src/modules/volunteer-invite/volunteer-invite.routes.ts
bunker-admin 0fc9ea80bf Fix cookie Secure flag for HTTP dev, un-track generated nginx confs
- Cookie Secure flag now uses req.secure (respects trust proxy +
  X-Forwarded-Proto) instead of NODE_ENV. Works correctly over both
  HTTP (local dev) and HTTPS (production tunnel).
- SameSite=Strict over HTTPS, SameSite=Lax over HTTP (browsers reject
  Strict cookies over plain HTTP).
- Un-track generated nginx/conf.d/api.conf and services.conf (gitignored,
  regenerated from templates at startup).
- Update CLAUDE.md: ENCRYPTION_KEY now required in all environments.

Bunker Admin
2026-03-27 10:06:38 -06:00

57 lines
1.9 KiB
TypeScript

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 };