import { Router, Request, Response, NextFunction } from 'express'; import { authenticate } from '../../middleware/auth.middleware'; import { requireRole } from '../../middleware/rbac.middleware'; import { getDashboardSummary, getSystemInfo, getContainerStatuses, getWeather, getApiMetrics, getTimeSeries, getContainerResources, getActivityFeed, getConnectivity, getTodayEvents, getChatSummary, getTopVideos, getRecentComments, getUpcomingShifts, getRecentSignups, } from './dashboard.service'; const router = Router(); router.use(authenticate); router.use(requireRole('SUPER_ADMIN', 'INFLUENCE_ADMIN', 'MAP_ADMIN')); // GET /api/dashboard/summary — platform counts router.get('/summary', async (_req: Request, res: Response, next: NextFunction) => { try { const summary = await getDashboardSummary(); res.json(summary); } catch (err) { next(err); } }); // GET /api/dashboard/system — hardware + OS info (SUPER_ADMIN only) router.get('/system', requireRole('SUPER_ADMIN'), async (_req: Request, res: Response, next: NextFunction) => { try { const info = getSystemInfo(); res.json(info); } catch (err) { next(err); } }); // GET /api/dashboard/containers — Docker container statuses (SUPER_ADMIN only) router.get('/containers', requireRole('SUPER_ADMIN'), async (_req: Request, res: Response, next: NextFunction) => { try { const containers = await getContainerStatuses(); res.json(containers); } catch (err) { next(err); } }); // GET /api/dashboard/weather — weather from map settings location router.get('/weather', async (_req: Request, res: Response, next: NextFunction) => { try { const weather = await getWeather(); if (!weather) { res.json({ error: 'No location configured or weather unavailable' }); return; } res.json(weather); } catch (err) { next(err); } }); // GET /api/dashboard/api-metrics — Prometheus API performance metrics (SUPER_ADMIN only) router.get('/api-metrics', requireRole('SUPER_ADMIN'), async (_req: Request, res: Response, next: NextFunction) => { try { const metrics = await getApiMetrics(); if (!metrics) { res.json({ error: 'Prometheus unavailable' }); return; } res.json(metrics); } catch (err) { next(err); } }); // GET /api/dashboard/time-series — predefined-key time-series from Prometheus (SUPER_ADMIN only) const ALLOWED_METRIC_KEYS = new Set([ 'request_rate_2xx', 'request_rate_4xx', 'request_rate_5xx', 'latency_p50', 'latency_p95', 'latency_p99', 'email_sent_rate', 'email_failed_rate', 'email_queue_size', 'cpu_usage', 'memory_usage', 'active_sessions', 'login_rate', ]); const ALLOWED_RANGES = new Set(['1h', '6h', '24h']); const ALLOWED_STEPS = new Set(['1m', '5m', '15m']); router.get('/time-series', requireRole('SUPER_ADMIN'), async (req: Request, res: Response, next: NextFunction) => { try { const metricsParam = (req.query.metrics as string) || ''; const range = (req.query.range as string) || '1h'; const step = (req.query.step as string) || '5m'; if (!ALLOWED_RANGES.has(range)) { res.status(400).json({ error: 'Invalid range. Allowed: 1h, 6h, 24h' }); return; } if (!ALLOWED_STEPS.has(step)) { res.status(400).json({ error: 'Invalid step. Allowed: 1m, 5m, 15m' }); return; } const metricKeys = metricsParam.split(',').filter(k => ALLOWED_METRIC_KEYS.has(k.trim())); if (metricKeys.length === 0) { res.status(400).json({ error: 'No valid metric keys provided' }); return; } const data = await getTimeSeries(metricKeys, range, step); res.json(data); } catch (err) { next(err); } }); // GET /api/dashboard/container-resources — cAdvisor container CPU/memory/network (SUPER_ADMIN only) router.get('/container-resources', requireRole('SUPER_ADMIN'), async (_req: Request, res: Response, next: NextFunction) => { try { const containers = await getContainerResources(); res.json({ containers }); } catch (err) { next(err); } }); // GET /api/dashboard/activity — recent activity feed (paginated) router.get('/activity', async (req: Request, res: Response, next: NextFunction) => { try { const page = Math.max(1, parseInt(req.query.page as string) || 1); const limit = Math.min(50, Math.max(1, parseInt(req.query.limit as string) || 20)); const module = (req.query.module as string) || 'all'; const result = await getActivityFeed({ page, limit, module }); res.json(result); } catch (err) { next(err); } }); // GET /api/dashboard/connectivity — service connectivity checks router.get('/connectivity', async (_req: Request, res: Response, next: NextFunction) => { try { const connectivity = await getConnectivity(); res.json(connectivity); } catch (err) { next(err); } }); // GET /api/dashboard/today-events — today's events from Gancio router.get('/today-events', async (_req: Request, res: Response, next: NextFunction) => { try { const events = await getTodayEvents(); res.json(events); } catch (err) { next(err); } }); // GET /api/dashboard/chat-summary — recent messages from Rocket.Chat router.get('/chat-summary', async (_req: Request, res: Response, next: NextFunction) => { try { const summary = await getChatSummary(); res.json(summary); } catch (err) { next(err); } }); // GET /api/dashboard/upcoming-shifts — next 5 shifts router.get('/upcoming-shifts', async (_req: Request, res: Response, next: NextFunction) => { try { const result = await getUpcomingShifts(); res.json(result); } catch (err) { next(err); } }); // GET /api/dashboard/recent-signups — latest 8 shift signups router.get('/recent-signups', async (_req: Request, res: Response, next: NextFunction) => { try { const result = await getRecentSignups(); res.json(result); } catch (err) { next(err); } }); // GET /api/dashboard/top-videos — top 5 videos by view count (if media enabled) router.get('/top-videos', async (_req: Request, res: Response, next: NextFunction) => { try { const result = await getTopVideos(); res.json(result); } catch (err) { next(err); } }); // GET /api/dashboard/recent-comments — latest 8 visible comments (if media enabled) router.get('/recent-comments', async (_req: Request, res: Response, next: NextFunction) => { try { const result = await getRecentComments(); res.json(result); } catch (err) { next(err); } }); export const dashboardRouter = router;