1bbdd7acba
- Add apps/api/ — Hono REST API server for managing pentest scans via K8s Jobs - POST/GET /api/scans, GET /api/scans/:id, cancel, report endpoints - Bearer token auth, Temporal client integration, K8s Job builder - Dockerfile, Kustomize manifests (Deployment, Service, RBAC) - Add CLI orchestrator abstraction (docker.ts → Orchestrator interface) - DockerOrchestrator and K8sOrchestrator implementations - Backend detection via SHANNON_BACKEND env var or --backend flag - Add CI workflow: type-check + lint on PR, build+push both images on main - Switch all workflows to self-hosted runners (runners-farhoodliquor) - Add shannon-api image build to release and release-beta workflows - Add root infra/kustomization.yaml as Flux entry point - Export PipelineProgress from @shannon/worker/pipeline Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
66 lines
1.9 KiB
TypeScript
66 lines
1.9 KiB
TypeScript
/**
|
|
* Scan CRUD routes — POST/GET /api/scans, GET/POST /api/scans/:id/*
|
|
*/
|
|
|
|
import { Hono } from 'hono';
|
|
import type { AppDeps } from '../app.js';
|
|
import type { Config } from '../config.js';
|
|
import { cancelScan, getReport, getScan, listScans, startScan } from '../services/scan-manager.js';
|
|
import { CreateScanSchema } from '../types/api.js';
|
|
|
|
export function scanRoutes(config: Config, deps: AppDeps): Hono {
|
|
const app = new Hono();
|
|
|
|
// POST /api/scans — start a new scan
|
|
app.post('/', async (c) => {
|
|
const body = await c.req.json();
|
|
const parsed = CreateScanSchema.safeParse(body);
|
|
|
|
if (!parsed.success) {
|
|
return c.json({ error: 'Validation failed', details: parsed.error.issues }, 400);
|
|
}
|
|
|
|
const result = await startScan(config, deps.batchApi, parsed.data);
|
|
return c.json(result, 201);
|
|
});
|
|
|
|
// GET /api/scans — list all scans
|
|
app.get('/', async (c) => {
|
|
const scans = await listScans(config, deps.temporalClient, deps.batchApi);
|
|
return c.json({ scans });
|
|
});
|
|
|
|
// GET /api/scans/:id — get scan status/progress
|
|
app.get('/:id', async (c) => {
|
|
const scanId = c.req.param('id');
|
|
const result = await getScan(config, deps.temporalClient, scanId);
|
|
|
|
if (!result) {
|
|
return c.json({ error: 'Scan not found' }, 404);
|
|
}
|
|
|
|
return c.json(result);
|
|
});
|
|
|
|
// POST /api/scans/:id/cancel — cancel a running scan
|
|
app.post('/:id/cancel', async (c) => {
|
|
const scanId = c.req.param('id');
|
|
await cancelScan(config, deps.temporalClient, deps.batchApi, scanId);
|
|
return c.json({ status: 'cancelled' });
|
|
});
|
|
|
|
// GET /api/scans/:id/report — get the scan report
|
|
app.get('/:id/report', async (c) => {
|
|
const scanId = c.req.param('id');
|
|
const report = await getReport(config, scanId);
|
|
|
|
if (!report) {
|
|
return c.json({ error: 'Report not found' }, 404);
|
|
}
|
|
|
|
return c.text(report);
|
|
});
|
|
|
|
return app;
|
|
}
|