import express from 'express'; import type { Config } from './types.js'; import { getRecentEvents, clearEvents } from './events.js'; import { validate, deepMerge } from './config.js'; export function createDashboardRouter(config: Config): express.Router { const router = express.Router(); const startTime = Date.now(); router.use(express.json()); // Authentication middleware if (config.dashboard?.auth) { router.use((req, res, next) => { const auth = config.dashboard!.auth!; if (auth.type === 'basic') { const authHeader = req.headers.authorization; if (!authHeader || !authHeader.startsWith('Basic ')) { res.setHeader('WWW-Authenticate', 'Basic realm="Troutbot Dashboard"'); res.status(401).json({ error: 'Authentication required' }); return; } const credentials = Buffer.from(authHeader.slice(6), 'base64').toString('utf8'); const [username, password] = credentials.split(':'); if (username !== auth.username || password !== auth.password) { res.setHeader('WWW-Authenticate', 'Basic realm="Troutbot Dashboard"'); res.status(401).json({ error: 'Invalid credentials' }); return; } } else if (auth.type === 'token') { const authHeader = req.headers.authorization; const token = req.query.token as string | undefined; const providedToken = authHeader?.startsWith('Bearer ') ? authHeader.slice(7) : token; if (!providedToken || providedToken !== auth.token) { res.status(401).json({ error: 'Invalid or missing token' }); return; } } next(); }); } // API routes router.get('/api/status', (_req, res) => { const enabledBackends = Object.entries(config.engine.backends) .filter(([, v]) => v.enabled) .map(([k]) => k); res.json({ uptime: Math.floor((Date.now() - startTime) / 1000), version: process.env.npm_package_version ?? 'unknown', dryRun: !process.env.GITHUB_TOKEN, backends: enabledBackends, repoCount: config.repositories.length || 'all', }); }); router.get('/api/events', (_req, res) => { res.json(getRecentEvents()); }); router.delete('/api/events', (_req, res) => { clearEvents(); res.json({ cleared: true }); }); router.get('/api/config', (_req, res) => { res.json(config); }); router.put('/api/config', (req, res) => { try { const partial = req.body as Partial; const merged = deepMerge( config as unknown as Record, partial as unknown as Record ) as unknown as Config; validate(merged); // Apply in-place Object.assign(config, merged); res.json(config); } catch (err) { const message = err instanceof Error ? err.message : String(err); res.status(400).json({ error: message }); } }); // Dashboard HTML router.get('/dashboard', (_req, res) => { res.type('html').send(dashboardHTML()); }); return router; } function dashboardHTML(): string { return ` Troutbot Dashboard

Troutbot Dashboard

Status

Recent Events

IDTimeRepo# ActionImpactConfidenceResult
Loading...

Configuration


    
`; }