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:
@@ -0,0 +1,59 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
export const NotificationConfigSchema = z.object({
|
||||
resend: z.object({
|
||||
apiKey: z.string().min(1, 'RESEND_API_KEY required'),
|
||||
baseUrl: z.string().default('https://api.resend.com'),
|
||||
}),
|
||||
fcm: z.object({
|
||||
privateKey: z.string().min(1, 'FCM_PRIVATE_KEY required'),
|
||||
projectId: z.string().min(1, 'FCM_PROJECT_ID required'),
|
||||
clientEmail: z.string().email(),
|
||||
}),
|
||||
apns: z.object({
|
||||
key: z.string().min(1, 'APNS_KEY required'),
|
||||
keyId: z.string().min(1, 'APNS_KEY_ID required'),
|
||||
teamId: z.string().min(1, 'APNS_TEAM_ID required'),
|
||||
bundleId: z.string().min(1, 'APNS_BUNDLE_ID required'),
|
||||
}),
|
||||
twilio: z.object({
|
||||
accountSid: z.string().min(1, 'TWILIO_ACCOUNT_SID required'),
|
||||
authToken: z.string().min(1, 'TWILIO_AUTH_TOKEN required'),
|
||||
messagingServiceSid: z.string().min(1, 'TWILIO_MESSAGING_SERVICE_SID required'),
|
||||
}),
|
||||
rateLimits: z.object({
|
||||
emailPerMinute: z.number().default(60),
|
||||
smsPerMinute: z.number().default(30),
|
||||
pushPerMinute: z.number().default(100),
|
||||
}),
|
||||
});
|
||||
|
||||
export type NotificationConfig = z.infer<typeof NotificationConfigSchema>;
|
||||
|
||||
export const loadNotificationConfig = (): NotificationConfig => ({
|
||||
resend: {
|
||||
apiKey: process.env.RESEND_API_KEY!,
|
||||
baseUrl: process.env.RESEND_BASE_URL || 'https://api.resend.com',
|
||||
},
|
||||
fcm: {
|
||||
privateKey: process.env.FCM_PRIVATE_KEY!,
|
||||
projectId: process.env.FCM_PROJECT_ID!,
|
||||
clientEmail: process.env.FCM_CLIENT_EMAIL!,
|
||||
},
|
||||
apns: {
|
||||
key: process.env.APNS_KEY!,
|
||||
keyId: process.env.APNS_KEY_ID!,
|
||||
teamId: process.env.APNS_TEAM_ID!,
|
||||
bundleId: process.env.APNS_BUNDLE_ID!,
|
||||
},
|
||||
twilio: {
|
||||
accountSid: process.env.TWILIO_ACCOUNT_SID!,
|
||||
authToken: process.env.TWILIO_AUTH_TOKEN!,
|
||||
messagingServiceSid: process.env.TWILIO_MESSAGING_SERVICE_SID!,
|
||||
},
|
||||
rateLimits: {
|
||||
emailPerMinute: parseInt(process.env.EMAIL_RATE_LIMIT || '60', 10),
|
||||
smsPerMinute: parseInt(process.env.SMS_RATE_LIMIT || '30', 10),
|
||||
pushPerMinute: parseInt(process.env.PUSH_RATE_LIMIT || '100', 10),
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user