Add tier-based scan scheduler and webhook triggers (FRE-4498)
- ScanScheduler: tier-based scheduling (BASIC=24h, PLUS=6h, PREMIUM=1h) - WebhookHandler: HMAC-verified webhook ingestion with SCAN_TRIGGER support - API routes: /scheduler and /webhooks endpoints under /api/v1/darkwatch - Jobs: scheduled scan checker + webhook retry processor via BullMQ - Schema: ScanSchedule, WebhookEvent models; ScanJob.scheduledBy field - Types: ScheduleStatus, WebhookEventType, WebhookTriggerInput - Tests: scheduler lifecycle + webhook signature/processing tests Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
67
packages/api/src/routes/webhook.routes.ts
Normal file
67
packages/api/src/routes/webhook.routes.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
import { FastifyInstance } from "fastify";
|
||||
import { WebhookHandler } from "@shieldai/darkwatch";
|
||||
|
||||
export function webhookRoutes(fastify: FastifyInstance) {
|
||||
const handler = new WebhookHandler();
|
||||
|
||||
fastify.post(
|
||||
"/",
|
||||
async (request, reply) => {
|
||||
const body = request.body as {
|
||||
eventType: string;
|
||||
payload: Record<string, unknown>;
|
||||
source?: string;
|
||||
};
|
||||
|
||||
const signature =
|
||||
(request.headers["x-webhook-signature"] as string) ||
|
||||
(request.headers["x-hub-signature-256"] as string) ||
|
||||
undefined;
|
||||
|
||||
try {
|
||||
const result = await handler.processEvent(
|
||||
body.eventType,
|
||||
body.payload,
|
||||
body.source,
|
||||
signature
|
||||
);
|
||||
|
||||
return reply.code(200).send({
|
||||
eventId: result.eventId,
|
||||
scanTriggered: result.scanTriggered,
|
||||
});
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : String(err);
|
||||
|
||||
if (message.includes("signature")) {
|
||||
return reply.code(401).send({ error: message });
|
||||
}
|
||||
|
||||
return reply.code(400).send({ error: message });
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
fastify.get(
|
||||
"/history",
|
||||
async (request, reply) => {
|
||||
const limit = parseInt((request.query as { limit?: string }).limit || "50");
|
||||
const offset = parseInt((request.query as { offset?: string }).offset || "0");
|
||||
|
||||
const events = await handler.getEventHistory(limit, offset);
|
||||
return reply.send(events);
|
||||
}
|
||||
);
|
||||
|
||||
fastify.get(
|
||||
"/user/:userId",
|
||||
async (request, reply) => {
|
||||
const userId = (request.params as { userId: string }).userId;
|
||||
const limit = parseInt((request.query as { limit?: string }).limit || "50");
|
||||
const offset = parseInt((request.query as { offset?: string }).offset || "0");
|
||||
|
||||
const events = await handler.getUserEvents(userId, limit, offset);
|
||||
return reply.send(events);
|
||||
}
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user