Spaces:
Paused
Paused
| import { Router } from 'express'; | |
| import { z } from 'zod'; | |
| import { | |
| getFeedOverview, | |
| executeSecuritySearch, | |
| securityTemplates, | |
| securityHistory, | |
| securityActivity, | |
| } from './securityService.js'; | |
| import { registerActivityStream } from './activityStream.js'; | |
| import { setActivityAcknowledged } from './securityRepository.js'; | |
| import { | |
| getWidgetPermissions, | |
| checkWidgetAccess, | |
| setWidgetPermission, | |
| setPlatformDefault | |
| } from './securityRepository.js'; | |
| export const securityRouter = Router(); | |
| securityRouter.get('/feeds', async (_req, res) => { | |
| const overview = await getFeedOverview(); | |
| res.json(overview); | |
| }); | |
| securityRouter.get('/search/templates', (_req, res) => { | |
| securityTemplates.list().then(templates => res.json({ templates })).catch(() => res.status(500).json({ error: 'Could not fetch templates' })); | |
| }); | |
| securityRouter.get('/search/history', (req, res) => { | |
| const limit = Number(req.query.limit ?? 6); | |
| securityHistory.list(limit).then(history => res.json({ history })).catch(() => res.status(500).json({ error: 'Could not fetch history' })); | |
| }); | |
| const searchSchema = z.object({ | |
| query: z.string().default(''), | |
| severity: z.string().default('all'), | |
| timeframe: z.string().default('24h'), | |
| sources: z.array(z.string()).default([]), | |
| }); | |
| securityRouter.post('/search/query', async (req, res) => { | |
| const parsed = searchSchema.safeParse(req.body); | |
| if (!parsed.success) { | |
| return res.status(400).json({ error: 'Invalid payload', details: parsed.error.flatten() }); | |
| } | |
| const payload = await executeSecuritySearch(parsed.data as any); | |
| res.json(payload); | |
| }); | |
| securityRouter.get('/activity', (req, res) => { | |
| const severity = typeof req.query.severity === 'string' ? req.query.severity : undefined; | |
| const category = typeof req.query.category === 'string' ? req.query.category : undefined; | |
| const limit = req.query.limit ? Number(req.query.limit) : undefined; | |
| securityActivity.list(severity, category, limit) | |
| .then(events => res.json({ events })) | |
| .catch(() => res.status(500).json({ error: 'Could not fetch activity' })); | |
| }); | |
| securityRouter.post('/activity/:id/ack', (req, res) => { | |
| const acknowledged = typeof req.body?.acknowledged === 'boolean' ? req.body.acknowledged : true; | |
| setActivityAcknowledged(req.params.id, acknowledged).then(updated => { | |
| if (!updated) { | |
| return res.status(404).json({ error: 'Event not found' }); | |
| } | |
| res.json(updated); | |
| }).catch(() => res.status(500).json({ error: 'Could not update event' })); | |
| }); | |
| securityRouter.get('/activity/stream', (req, res) => { | |
| registerActivityStream(res, { | |
| severity: typeof req.query.severity === 'string' ? req.query.severity : undefined, | |
| category: typeof req.query.category === 'string' ? req.query.category : undefined, | |
| }); | |
| }); | |
| const registryEventSchema = z.object({ | |
| id: z.string().optional(), | |
| title: z.string(), | |
| description: z.string(), | |
| category: z.union([z.literal('ingestion'), z.literal('alert'), z.literal('automation'), z.literal('audit')]), | |
| severity: z.union([z.literal('low'), z.literal('medium'), z.literal('high'), z.literal('critical')]), | |
| source: z.string(), | |
| rule: z.string().optional(), | |
| channel: z.union([z.literal('SSE'), z.literal('Webhook'), z.literal('Job')]).optional(), | |
| payload: z.record(z.any()).optional(), | |
| createdAt: z.string().optional(), | |
| acknowledged: z.boolean().optional(), | |
| }); | |
| securityRouter.post('/activity/registry', (req, res) => { | |
| const parsed = registryEventSchema.safeParse(req.body); | |
| if (!parsed.success) { | |
| return res.status(400).json({ error: 'Invalid registry event', details: parsed.error.flatten() }); | |
| } | |
| const event = securityActivity.publish({ | |
| id: parsed.data.id ?? `evt-${Date.now()}`, | |
| title: parsed.data.title, | |
| description: parsed.data.description, | |
| category: parsed.data.category, | |
| severity: parsed.data.severity, | |
| source: parsed.data.source, | |
| rule: parsed.data.rule, | |
| channel: parsed.data.channel ?? 'SSE', | |
| payload: parsed.data.payload, | |
| createdAt: parsed.data.createdAt ?? new Date().toISOString(), | |
| acknowledged: parsed.data.acknowledged ?? false, | |
| }); | |
| res.status(201).json(event); | |
| }); | |
| // Get permissions for a widget | |
| securityRouter.get('/permissions/:widgetId', async (req, res) => { | |
| try { | |
| const { widgetId } = req.params; | |
| const permissions = await getWidgetPermissions(widgetId); | |
| res.json(permissions); | |
| } catch (_error) { | |
| res.status(500).json({ error: 'Could not fetch permissions' }); | |
| } | |
| }); | |
| // Set widget permission (override) | |
| securityRouter.put('/permissions/:widgetId', async (req, res) => { | |
| try { | |
| const { widgetId } = req.params; | |
| const { resourceType, accessLevel, override = true } = req.body; | |
| await setWidgetPermission(widgetId, resourceType, accessLevel, override); | |
| res.json({ success: true }); | |
| } catch (_error) { | |
| res.status(500).json({ error: 'Could not set permission' }); | |
| } | |
| }); | |
| // Check access | |
| securityRouter.post('/check-access', async (req, res) => { | |
| try { | |
| const { widgetId, resourceType, requiredLevel } = req.body; | |
| const hasAccess = await checkWidgetAccess(widgetId, resourceType, requiredLevel); | |
| res.json({ hasAccess }); | |
| } catch (_error) { | |
| res.status(500).json({ error: 'Could not check access' }); | |
| } | |
| }); | |
| // Set platform default | |
| securityRouter.post('/platform-defaults', async (req, res) => { | |
| try { | |
| const { resourceType, accessLevel } = req.body; | |
| await setPlatformDefault(resourceType, accessLevel); | |
| res.json({ success: true }); | |
| } catch (_error) { | |
| res.status(500).json({ error: 'Could not set platform default' }); | |
| } | |
| }); | |