Files
Kordant/packages/api/src/routes/scheduler.routes.ts
Michael Freno 9fb5379b7a 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>
2026-04-30 10:57:56 -04:00

64 lines
1.7 KiB
TypeScript

import { FastifyInstance } from "fastify";
import { ScanScheduler } from "@shieldai/darkwatch";
export function schedulerRoutes(fastify: FastifyInstance) {
const scheduler = new ScanScheduler();
fastify.post(
"/ensure",
async (request, reply) => {
const userId = (request.user as { id: string })?.id;
if (!userId) {
return reply.code(401).send({ error: "User not authenticated" });
}
const result = await scheduler.ensureScheduleForUser(userId);
return reply.send(result);
}
);
fastify.get(
"/:userId",
async (request, reply) => {
const userId = (request.params as { userId: string }).userId;
const schedule = await scheduler.getSchedule(userId);
if (!schedule) {
return reply.code(404).send({ error: "Schedule not found" });
}
return reply.send(schedule);
}
);
fastify.post(
"/:userId/pause",
async (request, reply) => {
const userId = (request.params as { userId: string }).userId;
await scheduler.pauseSchedule(userId);
return reply.send({ paused: true });
}
);
fastify.post(
"/:userId/resume",
async (request, reply) => {
const userId = (request.params as { userId: string }).userId;
await scheduler.resumeSchedule(userId);
return reply.send({ resumed: true });
}
);
fastify.get(
"/",
async (request, reply) => {
const limit = parseInt((request.query as { limit?: string }).limit || "100");
const offset = parseInt((request.query as { offset?: string }).offset || "0");
const schedules = await scheduler.listActiveSchedules(limit, offset);
return reply.send(schedules);
}
);
}