Files
microdao-daarion/backend/http/audit.routes.ts
Apple e233d32ae7 feat(governance): Governance Engine MVP implementation
- Backend:
  - Migration 032: agent_gov_level, status, incidents, permissions tables
  - Domain types for governance layer
  - Permission Engine with all governance checks
  - Governance Service (promote/demote/roles)
  - Revocation Service (revoke/suspend/reinstate)
  - Audit Service (events filtering and stats)
  - Incidents Service (create/assign/escalate/resolve)
  - REST API routes for governance, audit, incidents

- Frontend:
  - TypeScript types for governance
  - API clients for governance, audit, incidents
  - GovernanceLevelBadge component
  - CityGovernancePanel component
  - AuditDashboard component
  - IncidentsList component with detail modal

Based on: Agent_Governance_Protocol_v1.md
2025-11-29 16:02:06 -08:00

179 lines
4.8 KiB
TypeScript

/**
* Audit Routes
* Based on: docs/foundation/Agent_Governance_Protocol_v1.md
*/
import { Router, Request, Response } from 'express';
import { auditService } from '../services/governance/audit.service';
import { GovernanceEventType, GovernanceScope } from '../domain/governance/types';
import { logger } from '../infra/logger/logger';
const router = Router();
/**
* GET /api/v1/audit/events
* Get audit events with filters
*/
router.get('/events', async (req: Request, res: Response) => {
try {
const {
eventType,
actorId,
targetId,
scope,
createdAtFrom,
createdAtTo,
limit,
offset,
} = req.query;
const result = await auditService.getEvents({
eventType: eventType as GovernanceEventType | undefined,
actorId: actorId as string | undefined,
targetId: targetId as string | undefined,
scope: scope as GovernanceScope | undefined,
createdAtFrom: createdAtFrom ? new Date(createdAtFrom as string) : undefined,
createdAtTo: createdAtTo ? new Date(createdAtTo as string) : undefined,
limit: limit ? parseInt(limit as string, 10) : 50,
offset: offset ? parseInt(offset as string, 10) : 0,
});
res.json(result);
} catch (error) {
logger.error('Error getting audit events', error);
res.status(500).json({ error: 'Failed to get audit events' });
}
});
/**
* GET /api/v1/audit/events/:id
* Get single audit event
*/
router.get('/events/:id', async (req: Request, res: Response) => {
try {
const { id } = req.params;
const event = await auditService.getEvent(id);
if (!event) {
return res.status(404).json({ error: 'Event not found' });
}
res.json(event);
} catch (error) {
logger.error('Error getting audit event', error);
res.status(500).json({ error: 'Failed to get audit event' });
}
});
/**
* GET /api/v1/audit/actor/:actorId
* Get events by actor
*/
router.get('/actor/:actorId', async (req: Request, res: Response) => {
try {
const { actorId } = req.params;
const { limit } = req.query;
const events = await auditService.getEventsByActor(
actorId,
limit ? parseInt(limit as string, 10) : 50
);
res.json(events);
} catch (error) {
logger.error('Error getting events by actor', error);
res.status(500).json({ error: 'Failed to get events by actor' });
}
});
/**
* GET /api/v1/audit/target/:targetId
* Get events by target
*/
router.get('/target/:targetId', async (req: Request, res: Response) => {
try {
const { targetId } = req.params;
const { limit } = req.query;
const events = await auditService.getEventsByTarget(
targetId,
limit ? parseInt(limit as string, 10) : 50
);
res.json(events);
} catch (error) {
logger.error('Error getting events by target', error);
res.status(500).json({ error: 'Failed to get events by target' });
}
});
/**
* GET /api/v1/audit/scope/:scope
* Get events by scope
*/
router.get('/scope/:scope', async (req: Request, res: Response) => {
try {
const { scope } = req.params;
const { limit } = req.query;
const events = await auditService.getEventsByScope(
scope as GovernanceScope,
limit ? parseInt(limit as string, 10) : 50
);
res.json(events);
} catch (error) {
logger.error('Error getting events by scope', error);
res.status(500).json({ error: 'Failed to get events by scope' });
}
});
/**
* GET /api/v1/audit/stats
* Get event statistics
*/
router.get('/stats', async (req: Request, res: Response) => {
try {
const { fromDate, toDate } = req.query;
const stats = await auditService.getEventStats(
fromDate ? new Date(fromDate as string) : undefined,
toDate ? new Date(toDate as string) : undefined
);
res.json(stats);
} catch (error) {
logger.error('Error getting audit stats', error);
res.status(500).json({ error: 'Failed to get audit stats' });
}
});
/**
* GET /api/v1/audit/entity/:entityType/:entityId
* Get governance history for specific entity
*/
router.get('/entity/:entityType/:entityId', async (req: Request, res: Response) => {
try {
const { entityType, entityId } = req.params;
const { limit } = req.query;
if (!['agent', 'microdao', 'district', 'node', 'room'].includes(entityType)) {
return res.status(400).json({ error: 'Invalid entity type' });
}
const events = await auditService.getEntityHistory(
entityType as 'agent' | 'microdao' | 'district' | 'node' | 'room',
entityId,
limit ? parseInt(limit as string, 10) : 50
);
res.json(events);
} catch (error) {
logger.error('Error getting entity history', error);
res.status(500).json({ error: 'Failed to get entity history' });
}
});
export default router;