264 lines
8.8 KiB
JavaScript
264 lines
8.8 KiB
JavaScript
"use strict";
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.shiftsVolunteerRouter = exports.shiftsPublicRouter = exports.shiftsAdminRouter = void 0;
|
|
const express_1 = require("express");
|
|
const shifts_service_1 = require("./shifts.service");
|
|
const shifts_schemas_1 = require("./shifts.schemas");
|
|
const validate_1 = require("../../../middleware/validate");
|
|
const auth_middleware_1 = require("../../../middleware/auth.middleware");
|
|
const rbac_middleware_1 = require("../../../middleware/rbac.middleware");
|
|
const rate_limit_1 = require("../../../middleware/rate-limit");
|
|
const database_1 = require("../../../config/database");
|
|
const redis_1 = require("../../../config/redis");
|
|
const roles_1 = require("../../../utils/roles");
|
|
// --- Admin Router ---
|
|
const adminRouter = (0, express_1.Router)();
|
|
exports.shiftsAdminRouter = adminRouter;
|
|
adminRouter.use(auth_middleware_1.authenticate);
|
|
adminRouter.use((0, rbac_middleware_1.requireRole)(...roles_1.SCHEDULING_ROLES));
|
|
// GET /api/map/shifts — list paginated
|
|
adminRouter.get('/', (0, validate_1.validate)(shifts_schemas_1.listShiftsSchema, 'query'), async (req, res, next) => {
|
|
try {
|
|
const result = await shifts_service_1.shiftsService.findAll(req.query);
|
|
res.json(result);
|
|
}
|
|
catch (err) {
|
|
next(err);
|
|
}
|
|
});
|
|
// GET /api/map/shifts/stats — statistics
|
|
adminRouter.get('/stats', async (_req, res, next) => {
|
|
try {
|
|
const stats = await shifts_service_1.shiftsService.getStats();
|
|
res.json(stats);
|
|
}
|
|
catch (err) {
|
|
next(err);
|
|
}
|
|
});
|
|
// GET /api/map/shifts/calendar — calendar data
|
|
adminRouter.get('/calendar', async (req, res, next) => {
|
|
try {
|
|
const { startDate, endDate } = req.query;
|
|
if (!startDate || !endDate) {
|
|
return res.status(400).json({ error: 'startDate and endDate required' });
|
|
}
|
|
const data = await shifts_service_1.shiftsService.getCalendarData(startDate, endDate);
|
|
res.json(data);
|
|
}
|
|
catch (err) {
|
|
next(err);
|
|
}
|
|
});
|
|
// GET /api/map/shifts/:id — single shift with signups
|
|
adminRouter.get('/:id', async (req, res, next) => {
|
|
try {
|
|
const id = req.params.id;
|
|
const shift = await shifts_service_1.shiftsService.findById(id);
|
|
res.json(shift);
|
|
}
|
|
catch (err) {
|
|
next(err);
|
|
}
|
|
});
|
|
// POST /api/map/shifts — create
|
|
adminRouter.post('/', (0, validate_1.validate)(shifts_schemas_1.createShiftSchema), async (req, res, next) => {
|
|
try {
|
|
const shift = await shifts_service_1.shiftsService.create(req.body, req.user.id);
|
|
res.status(201).json(shift);
|
|
}
|
|
catch (err) {
|
|
next(err);
|
|
}
|
|
});
|
|
// PUT /api/map/shifts/:id — update
|
|
adminRouter.put('/:id', (0, validate_1.validate)(shifts_schemas_1.updateShiftSchema), async (req, res, next) => {
|
|
try {
|
|
const id = req.params.id;
|
|
const shift = await shifts_service_1.shiftsService.update(id, req.body);
|
|
res.json(shift);
|
|
}
|
|
catch (err) {
|
|
next(err);
|
|
}
|
|
});
|
|
// DELETE /api/map/shifts/:id — delete
|
|
adminRouter.delete('/:id', async (req, res, next) => {
|
|
try {
|
|
const id = req.params.id;
|
|
await shifts_service_1.shiftsService.delete(id);
|
|
res.status(204).send();
|
|
}
|
|
catch (err) {
|
|
next(err);
|
|
}
|
|
});
|
|
// POST /api/map/shifts/:id/signups — admin add volunteer
|
|
adminRouter.post('/:id/signups', (0, validate_1.validate)(shifts_schemas_1.addSignupSchema), async (req, res, next) => {
|
|
try {
|
|
const id = req.params.id;
|
|
const signup = await shifts_service_1.shiftsService.addSignup(id, req.body);
|
|
res.status(201).json(signup);
|
|
}
|
|
catch (err) {
|
|
next(err);
|
|
}
|
|
});
|
|
// DELETE /api/map/shifts/:id/signups/:signupId — admin remove volunteer
|
|
adminRouter.delete('/:id/signups/:signupId', async (req, res, next) => {
|
|
try {
|
|
const signupId = req.params.signupId;
|
|
await shifts_service_1.shiftsService.removeSignup(signupId);
|
|
res.status(204).send();
|
|
}
|
|
catch (err) {
|
|
next(err);
|
|
}
|
|
});
|
|
// POST /api/map/shifts/:id/meeting — create and link a video briefing meeting
|
|
adminRouter.post('/:id/meeting', async (req, res, next) => {
|
|
try {
|
|
const id = req.params.id;
|
|
const meeting = await shifts_service_1.shiftsService.createMeetingForShift(id, req.user.id);
|
|
res.status(201).json(meeting);
|
|
}
|
|
catch (err) {
|
|
next(err);
|
|
}
|
|
});
|
|
// DELETE /api/map/shifts/:id/meeting — remove video briefing from shift
|
|
adminRouter.delete('/:id/meeting', async (req, res, next) => {
|
|
try {
|
|
const id = req.params.id;
|
|
await shifts_service_1.shiftsService.removeMeetingFromShift(id);
|
|
res.status(204).send();
|
|
}
|
|
catch (err) {
|
|
next(err);
|
|
}
|
|
});
|
|
// POST /api/map/shifts/:id/email-details — email all volunteers
|
|
adminRouter.post('/:id/email-details', async (req, res, next) => {
|
|
try {
|
|
const id = req.params.id;
|
|
const result = await shifts_service_1.shiftsService.emailShiftDetails(id);
|
|
res.json(result);
|
|
}
|
|
catch (err) {
|
|
next(err);
|
|
}
|
|
});
|
|
// --- Volunteer Router ---
|
|
const volunteerRouter = (0, express_1.Router)();
|
|
exports.shiftsVolunteerRouter = volunteerRouter;
|
|
volunteerRouter.use(auth_middleware_1.authenticate);
|
|
// GET /api/map/shifts/volunteer/upcoming — upcoming shifts with signup status
|
|
volunteerRouter.get('/volunteer/upcoming', async (req, res, next) => {
|
|
try {
|
|
const shifts = await shifts_service_1.shiftsService.getUpcomingForVolunteer(req.user.id);
|
|
res.json(shifts);
|
|
}
|
|
catch (err) {
|
|
next(err);
|
|
}
|
|
});
|
|
// GET /api/map/shifts/volunteer/my-signups — own confirmed signups
|
|
volunteerRouter.get('/volunteer/my-signups', async (req, res, next) => {
|
|
try {
|
|
const signups = await shifts_service_1.shiftsService.getMySignups(req.user.id);
|
|
res.json(signups);
|
|
}
|
|
catch (err) {
|
|
next(err);
|
|
}
|
|
});
|
|
// POST /api/map/shifts/volunteer/:id/signup — sign up for shift
|
|
volunteerRouter.post('/volunteer/:id/signup', rate_limit_1.shiftSignupRateLimit, async (req, res, next) => {
|
|
try {
|
|
const id = req.params.id;
|
|
const signup = await shifts_service_1.shiftsService.volunteerSignup(id, req.user.id);
|
|
res.status(201).json(signup);
|
|
}
|
|
catch (err) {
|
|
next(err);
|
|
}
|
|
});
|
|
// DELETE /api/map/shifts/volunteer/:id/signup — cancel own signup
|
|
volunteerRouter.delete('/volunteer/:id/signup', async (req, res, next) => {
|
|
try {
|
|
const id = req.params.id;
|
|
await shifts_service_1.shiftsService.cancelVolunteerSignup(id, req.user.id);
|
|
res.status(204).send();
|
|
}
|
|
catch (err) {
|
|
next(err);
|
|
}
|
|
});
|
|
// --- Public Router ---
|
|
const publicRouter = (0, express_1.Router)();
|
|
exports.shiftsPublicRouter = publicRouter;
|
|
// GET /api/map/shifts/public — list upcoming public shifts
|
|
publicRouter.get('/public', async (_req, res, next) => {
|
|
try {
|
|
const shifts = await shifts_service_1.shiftsService.getPublicShifts();
|
|
res.json(shifts);
|
|
}
|
|
catch (err) {
|
|
next(err);
|
|
}
|
|
});
|
|
// POST /api/map/shifts/public/:id/signup — public signup
|
|
publicRouter.post('/public/:id/signup', rate_limit_1.shiftSignupRateLimit, (0, validate_1.validate)(shifts_schemas_1.publicSignupSchema), async (req, res, next) => {
|
|
try {
|
|
const id = req.params.id;
|
|
const result = await shifts_service_1.shiftsService.publicSignup(id, req.body);
|
|
res.status(201).json(result);
|
|
}
|
|
catch (err) {
|
|
next(err);
|
|
}
|
|
});
|
|
// GET /api/map/shifts/public/related — related active campaigns
|
|
publicRouter.get('/public/related', async (_req, res, next) => {
|
|
try {
|
|
const cacheKey = 'shifts:related:campaigns';
|
|
try {
|
|
const cached = await redis_1.redis.get(cacheKey);
|
|
if (cached) {
|
|
res.json(JSON.parse(cached));
|
|
return;
|
|
}
|
|
}
|
|
catch { /* cache miss */ }
|
|
const campaigns = await database_1.prisma.campaign.findMany({
|
|
where: { status: 'ACTIVE' },
|
|
select: {
|
|
id: true,
|
|
slug: true,
|
|
title: true,
|
|
description: true,
|
|
_count: { select: { emails: true } },
|
|
},
|
|
orderBy: { createdAt: 'desc' },
|
|
take: 3,
|
|
});
|
|
const result = {
|
|
campaigns: campaigns.map(c => ({
|
|
id: c.id,
|
|
slug: c.slug,
|
|
title: c.title,
|
|
description: c.description?.slice(0, 150) ?? null,
|
|
emailCount: c._count.emails,
|
|
})),
|
|
};
|
|
try {
|
|
await redis_1.redis.setex(cacheKey, 300, JSON.stringify(result));
|
|
}
|
|
catch { /* non-critical */ }
|
|
res.json(result);
|
|
}
|
|
catch (err) {
|
|
next(err);
|
|
}
|
|
});
|
|
//# sourceMappingURL=shifts.routes.js.map
|