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,34 @@
|
||||
/**
|
||||
* Bearer token authentication middleware.
|
||||
* Validates the Authorization header against the configured API key.
|
||||
* Skips health check endpoints.
|
||||
*/
|
||||
|
||||
import crypto from 'node:crypto';
|
||||
import type { Context, Next } from 'hono';
|
||||
|
||||
const PUBLIC_PATHS = new Set(['/healthz', '/readyz']);
|
||||
|
||||
export function authMiddleware(apiKey: string) {
|
||||
const expectedBuffer = Buffer.from(apiKey);
|
||||
|
||||
return async (c: Context, next: Next) => {
|
||||
if (PUBLIC_PATHS.has(c.req.path)) {
|
||||
return next();
|
||||
}
|
||||
|
||||
const header = c.req.header('Authorization');
|
||||
if (!header?.startsWith('Bearer ')) {
|
||||
return c.json({ error: 'Missing or invalid Authorization header' }, 401);
|
||||
}
|
||||
|
||||
const token = header.slice(7);
|
||||
const tokenBuffer = Buffer.from(token);
|
||||
|
||||
if (tokenBuffer.length !== expectedBuffer.length || !crypto.timingSafeEqual(tokenBuffer, expectedBuffer)) {
|
||||
return c.json({ error: 'Invalid API key' }, 401);
|
||||
}
|
||||
|
||||
return next();
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* Global error handler middleware.
|
||||
* Catches unhandled errors and returns structured JSON responses.
|
||||
*/
|
||||
|
||||
import type { Context } from 'hono';
|
||||
|
||||
export function errorHandler(err: Error, c: Context): Response {
|
||||
console.error('Unhandled error:', err);
|
||||
|
||||
const status = 'statusCode' in err && typeof err.statusCode === 'number' ? err.statusCode : 500;
|
||||
|
||||
return c.json(
|
||||
{
|
||||
error: status === 500 ? 'Internal server error' : err.message,
|
||||
code: err.name || 'UNKNOWN_ERROR',
|
||||
},
|
||||
status as 500,
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user