bunker-admin abdfd50cb8 Make embed proxy ports configurable via env vars for multi-instance deployments
All 13 nginx embed proxy ports (8881-8895) are now driven by environment
variables instead of being hardcoded. This prevents port conflicts when
running multiple Changemaker instances on the same host.

Chain: .env → docker-compose port mappings → nginx container env →
entrypoint.sh envsubst → services.conf.template listen directives →
API /services/config endpoint → frontend buildServiceUrl().

Existing deployments are unaffected (all vars default to current values).

Bunker Admin
2026-03-25 15:25:00 -06:00

81 lines
2.8 KiB
TypeScript

import { Router, Request, Response } from 'express';
import { EventSeverity } from '@prisma/client';
import { authenticate, requireRole } from '../../middleware/auth';
import * as eventService from '../../services/event.service';
const router = Router();
router.use(authenticate);
// ─── Cross-Instance Event Queries ────────────────────────────────
// GET /api/events — list events with filters
router.get(
'/',
async (req: Request, res: Response) => {
const { instanceId, severity, acknowledged, source, from, to, page, limit } = req.query;
const filters: eventService.EventFilters = {
instanceId: instanceId as string | undefined,
severity: severity ? (severity as EventSeverity) : undefined,
acknowledged: acknowledged !== undefined ? acknowledged === 'true' : undefined,
source: source as string | undefined,
from: from ? new Date(from as string) : undefined,
to: to ? new Date(to as string) : undefined,
page: page ? Math.max(1, parseInt(page as string, 10)) : 1,
limit: limit ? Math.min(100, Math.max(1, parseInt(limit as string, 10))) : 50,
};
const result = await eventService.listEvents(filters);
res.json(result);
}
);
// GET /api/events/summary — unacknowledged counts for dashboard
router.get(
'/summary',
async (_req: Request, res: Response) => {
const summary = await eventService.getUnacknowledgedSummary();
res.json({ data: summary });
}
);
// PUT /api/events/:id/acknowledge — acknowledge a single event
router.put(
'/:id/acknowledge',
requireRole('SUPER_ADMIN', 'OPERATOR'),
async (req: Request, res: Response) => {
const event = await eventService.acknowledgeEvent(req.params.id as string, req.user!.id);
res.json({ data: event });
}
);
// ─── Instance-Scoped Event Routes ────────────────────────────────
// These are mounted at /api/instances/:id/events via a sub-export
export const instanceEventsRouter = Router({ mergeParams: true });
instanceEventsRouter.use(authenticate);
// GET /api/instances/:id/events
instanceEventsRouter.get(
'/',
async (req: Request, res: Response) => {
const page = Math.max(1, parseInt(req.query.page as string, 10) || 1);
const limit = Math.min(100, Math.max(1, parseInt(req.query.limit as string, 10) || 50));
const result = await eventService.listInstanceEvents(req.params.id as string, page, limit);
res.json(result);
}
);
// PUT /api/instances/:id/events/acknowledge-all
instanceEventsRouter.put(
'/acknowledge-all',
requireRole('SUPER_ADMIN', 'OPERATOR'),
async (req: Request, res: Response) => {
const result = await eventService.acknowledgeAllForInstance(req.params.id as string, req.user!.id);
res.json({ data: result });
}
);
export default router;