import type { ActiveWebhookEndpoint } from '@admin/actions/webhooks/internal-get-active-endpoints' import { recordWebhookDelivery } from '@admin/actions/webhooks/record-webhook-delivery' import { signWebhookPayload } from '@admin/utils/webhook/sign-webhook-payload' const MAX_ATTEMPTS = 3 const RETRY_DELAYS_MS = [1_000, 5_000] const REQUEST_TIMEOUT_MS = 10_000 export async function deliverToEndpoint( endpoint: ActiveWebhookEndpoint, eventId: string, event: string, body: string ): Promise { for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) { const startedAt = Date.now() let statusCode: number | null = null let errorMessage: string | null = null try { const timestamp = Math.floor(Date.now() / 1000).toString() const response = await fetch(endpoint.url, { method: 'POST', headers: { 'content-type': 'application/json', 'x-webhook-id': eventId, 'x-webhook-timestamp': timestamp, 'x-webhook-signature': signWebhookPayload(endpoint.secret, timestamp, body) }, body, signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS) }) statusCode = response.status if (!response.ok) errorMessage = `Webhook returned status ${response.status}` } catch (error) { errorMessage = error instanceof Error ? error.message : 'Webhook request failed' } await recordWebhookDelivery({ webhookId: endpoint.id, event, eventId, attempt, success: errorMessage === null, statusCode, durationMs: Date.now() - startedAt, error: errorMessage }) if (errorMessage === null) return if (attempt < MAX_ATTEMPTS) { await new Promise((resolve) => setTimeout(resolve, RETRY_DELAYS_MS[attempt - 1])) } } }