feat: add K8s API server, orchestrator abstraction, and CI pipeline
- 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>
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
Reference in New Issue
Block a user