PeerCortex/src/features/hijack-alerts/webhook-client.ts
Rene Fichtmueller 5554c1a53e feat: BGP Hijack Alerting + Webhooks (Feature 1)
- Deterministic Classification: MOAS/HIJACK/LEAK type detection
- Severity scoring: CRITICAL/HIGH/MEDIUM/LOW based on prefix length
- Optional Ollama enrichment (qwen2.5:3b) for CRITICAL only (5s timeout)
- PostgreSQL backend: hijack_events, webhook_subscriptions, webhook_deliveries
- HMAC-SHA256 webhook signing with exponential backoff retry
- Retry scheduler: node-cron job every 5 minutes
- 6 API endpoints: POST/GET/DELETE webhooks, test delivery, list/resolve hijacks
- 22 comprehensive tests (80%+ coverage)
- Zero external API costs (deterministic + local Ollama only)
2026-04-29 07:45:15 +02:00

55 lines
1.7 KiB
TypeScript

import axios, { AxiosError } from 'axios'
import crypto from 'crypto'
import type { WebhookPayload, HijackEvent } from './types'
export class WebhookClient {
async sendWebhook(event: HijackEvent, endpoint_url: string, secret_key: string, timeout_ms: number = 10000): Promise<{ success: boolean; status?: number; error?: string; response_time_ms: number }> {
const startTime = Date.now()
const timestamp = new Date().toISOString()
const payload = {
event,
timestamp
}
const signature = this.generateSignature(JSON.stringify(payload), secret_key)
try {
const response = await axios.post(endpoint_url, payload, {
headers: {
'Content-Type': 'application/json',
'X-Webhook-Signature': signature,
'X-Webhook-Timestamp': timestamp
},
timeout: timeout_ms
})
return {
success: response.status >= 200 && response.status < 300,
status: response.status,
response_time_ms: Date.now() - startTime
}
} catch (error) {
const axiosError = error as AxiosError
return {
success: false,
status: axiosError.response?.status,
error: axiosError.message,
response_time_ms: Date.now() - startTime
}
}
}
calculateBackoffDelay(attemptNumber: number): number {
if (attemptNumber === 0) return 0.5
const baseDelay = 1000
const delayMs = baseDelay * Math.pow(2, attemptNumber - 1)
const maxDelay = 32000
return Math.min(delayMs, maxDelay)
}
private generateSignature(payload: string, secret: string): string {
return crypto.createHmac('sha256', secret).update(payload).digest('hex')
}
}