arcadiasuite/server/governance/routes.ts

292 lines
9.3 KiB
TypeScript

import { Router, type Request, type Response } from "express";
import { governanceService } from "./service";
import { jobQueueService } from "./jobQueue";
import { runPolicyTests } from "./policyTests";
const router = Router();
router.get("/stats", async (_req: Request, res: Response) => {
try {
const stats = await governanceService.getGovernanceStats();
res.json(stats);
} catch (error) {
console.error("Error fetching governance stats:", error);
res.status(500).json({ error: "Failed to fetch governance stats" });
}
});
router.get("/policies", async (_req: Request, res: Response) => {
try {
const policies = await governanceService.getPolicies();
res.json(policies);
} catch (error) {
res.status(500).json({ error: "Failed to fetch policies" });
}
});
router.post("/policies", async (req: Request, res: Response) => {
try {
const { name, scope, target, effect, conditions, priority, description } = req.body;
if (!name || !scope || !target || !effect) {
return res.status(400).json({ error: "name, scope, target, effect are required" });
}
const policy = await governanceService.createPolicy({
name, scope, target, effect,
conditions: conditions || {},
priority: priority || 100,
description: description || null,
});
res.status(201).json(policy);
} catch (error) {
res.status(500).json({ error: "Failed to create policy" });
}
});
router.post("/evaluate", async (req: Request, res: Response) => {
try {
const { agent, action, target, context } = req.body;
if (!agent || !action || !target) {
return res.status(400).json({ error: "agent, action, target are required" });
}
const result = await governanceService.evaluatePolicy(agent, action, target, context);
res.json(result);
} catch (error) {
res.status(500).json({ error: "Failed to evaluate policy" });
}
});
router.get("/contracts", async (_req: Request, res: Response) => {
try {
const contracts = await governanceService.getContracts();
res.json(contracts);
} catch (error) {
res.status(500).json({ error: "Failed to fetch contracts" });
}
});
router.post("/contracts", async (req: Request, res: Response) => {
try {
const { name, action, description, inputSchema, outputSchema, requiredPermissions, category } = req.body;
if (!name || !action) {
return res.status(400).json({ error: "name, action are required" });
}
const contract = await governanceService.registerContract({
name, action,
description: description || null,
inputSchema: inputSchema || null,
outputSchema: outputSchema || null,
requiredPermissions: requiredPermissions || [],
category: category || null,
});
res.status(201).json(contract);
} catch (error) {
res.status(500).json({ error: "Failed to create contract" });
}
});
router.get("/tools", async (_req: Request, res: Response) => {
try {
const tools = await governanceService.getTools();
res.json(tools);
} catch (error) {
res.status(500).json({ error: "Failed to fetch tools" });
}
});
router.get("/skills", async (req: Request, res: Response) => {
try {
const status = req.query.status as string | undefined;
const skills = await governanceService.getSkills(status);
res.json(skills);
} catch (error) {
res.status(500).json({ error: "Failed to fetch skills" });
}
});
router.post("/skills", async (req: Request, res: Response) => {
try {
const { name, version, description, steps, tools, inputSchema, outputSchema, createdBy } = req.body;
if (!name || !version) {
return res.status(400).json({ error: "name, version are required" });
}
const skill = await governanceService.createSkill({
name, version,
description: description || null,
steps: steps || null,
tools: tools || [],
inputSchema: inputSchema || null,
outputSchema: outputSchema || null,
createdBy: createdBy || "system",
});
res.status(201).json(skill);
} catch (error) {
res.status(500).json({ error: "Failed to create skill" });
}
});
router.get("/skills/:id", async (req: Request, res: Response) => {
try {
const id = parseInt(req.params.id);
if (isNaN(id)) return res.status(400).json({ error: "Invalid ID" });
const skill = await governanceService.getSkill(id);
if (!skill) return res.status(404).json({ error: "Skill not found" });
res.json(skill);
} catch (error) {
res.status(500).json({ error: "Failed to fetch skill" });
}
});
router.delete("/skills/:id", async (req: Request, res: Response) => {
try {
const id = parseInt(req.params.id);
if (isNaN(id)) return res.status(400).json({ error: "Invalid ID" });
await governanceService.deactivateSkill(id);
res.json({ success: true });
} catch (error) {
res.status(500).json({ error: "Failed to deactivate skill" });
}
});
router.get("/audit", async (req: Request, res: Response) => {
try {
const limit = parseInt(req.query.limit as string) || 50;
const agentName = req.query.agent as string | undefined;
const trail = await governanceService.getAuditTrail(limit, agentName);
res.json(trail);
} catch (error) {
res.status(500).json({ error: "Failed to fetch audit trail" });
}
});
router.get("/jobs", async (req: Request, res: Response) => {
try {
const status = req.query.status as string | undefined;
const limit = parseInt(req.query.limit as string) || 50;
const jobs = await jobQueueService.getJobs(status, limit);
res.json(jobs);
} catch (error) {
res.status(500).json({ error: "Failed to fetch jobs" });
}
});
router.get("/jobs/stats", async (_req: Request, res: Response) => {
try {
const stats = await jobQueueService.getJobStats();
res.json(stats);
} catch (error) {
res.status(500).json({ error: "Failed to fetch job stats" });
}
});
router.post("/jobs", async (req: Request, res: Response) => {
try {
const { type, priority, payload, scheduledAt, maxAttempts, createdBy, parentJobId, metadata } = req.body;
if (!type) return res.status(400).json({ error: "type is required" });
const job = await jobQueueService.enqueue({
type,
priority: priority || 50,
payload: payload || null,
scheduledAt: scheduledAt ? new Date(scheduledAt) : null,
maxAttempts: maxAttempts || 3,
createdBy: createdBy || "api",
parentJobId: parentJobId || null,
metadata: metadata || null,
});
res.status(201).json(job);
} catch (error) {
res.status(500).json({ error: "Failed to enqueue job" });
}
});
router.post("/jobs/:id/cancel", async (req: Request, res: Response) => {
try {
const id = parseInt(req.params.id);
if (isNaN(id)) return res.status(400).json({ error: "Invalid ID" });
await jobQueueService.cancelJob(id);
res.json({ success: true });
} catch (error) {
res.status(500).json({ error: "Failed to cancel job" });
}
});
router.post("/jobs/:id/retry", async (req: Request, res: Response) => {
try {
const id = parseInt(req.params.id);
if (isNaN(id)) return res.status(400).json({ error: "Invalid ID" });
const retried = await jobQueueService.retryJob(id);
res.json({ success: retried });
} catch (error) {
res.status(500).json({ error: "Failed to retry job" });
}
});
router.get("/metrics", async (req: Request, res: Response) => {
try {
const agent = req.query.agent as string | undefined;
const limit = parseInt(req.query.limit as string) || 100;
const metrics = await jobQueueService.getAgentMetrics(agent, limit);
res.json(metrics);
} catch (error) {
res.status(500).json({ error: "Failed to fetch metrics" });
}
});
router.get("/metrics/summary", async (_req: Request, res: Response) => {
try {
const summary = await jobQueueService.getAgentSummary();
res.json(summary);
} catch (error) {
res.status(500).json({ error: "Failed to fetch metrics summary" });
}
});
router.get("/policy-tests", async (_req: Request, res: Response) => {
try {
const results = await runPolicyTests();
res.json(results);
} catch (error) {
res.status(500).json({ error: "Failed to run policy tests" });
}
});
router.post("/tools/:id/rbac", async (req: Request, res: Response) => {
try {
const id = parseInt(req.params.id);
if (isNaN(id)) return res.status(400).json({ error: "Invalid ID" });
const { allowedAgents } = req.body;
if (!Array.isArray(allowedAgents)) {
return res.status(400).json({ error: "allowedAgents must be an array" });
}
await governanceService.updateToolRBAC(id, allowedAgents);
res.json({ success: true });
} catch (error) {
res.status(500).json({ error: "Failed to update tool RBAC" });
}
});
router.get("/dashboard", async (_req: Request, res: Response) => {
try {
const [govStats, jobStats, agentSummary, recentAudit, policies, skills] = await Promise.all([
governanceService.getGovernanceStats(),
jobQueueService.getJobStats(),
jobQueueService.getAgentSummary(),
governanceService.getAuditTrail(20),
governanceService.getPolicies(),
governanceService.getSkills("active"),
]);
res.json({
governance: govStats,
jobs: jobStats,
agents: agentSummary,
recentAudit,
policies,
skills,
});
} catch (error) {
res.status(500).json({ error: "Failed to fetch dashboard data" });
}
});
export default router;