import { createHmac } from 'crypto'; import config from '../config'; import { getInstanceSettings } from './settings'; export type WebhookEvent = 'secret.viewed' | 'secret.burned' | 'apikey.created'; interface SecretWebhookData { secretId: string; hasPassword: boolean; hasIpRestriction: boolean; viewsRemaining?: number; } interface ApiKeyWebhookData { apiKeyId: string; name: string; expiresAt: string | null; userId: string; } interface WebhookPayload { event: WebhookEvent; timestamp: string; data: SecretWebhookData | ApiKeyWebhookData; } function signPayload(payload: string, secret: string): string { return createHmac('sha256', secret).update(payload).digest('hex'); } async function sendWithRetry( url: string, headers: Record, body: string ): Promise { const maxRetries = 3; for (let attempt = 0; attempt < maxRetries; attempt++) { try { const response = await fetch(url, { method: 'POST', headers, body, signal: AbortSignal.timeout(5000), redirect: 'error', }); if (response.ok) return; if (response.status >= 400 && response.status < 500) { console.error(`Webhook delivery failed: ${response.status}`); return; } } catch (error) { if (attempt === maxRetries - 1) { console.error('Webhook delivery failed after retries:', error); return; } } await new Promise((resolve) => setTimeout(resolve, 1000 * Math.pow(2, attempt))); } } export function sendWebhook(event: WebhookEvent, data: WebhookPayload['data']): void { (async () => { try { const settings = config.isManaged() ? config.getManagedSettings() : await getInstanceSettings(); if (!settings?.webhookEnabled || !settings.webhookUrl) { return; } if (event === 'secret.viewed' && !settings.webhookOnView) { return; } if (event === 'secret.burned' && !settings.webhookOnBurn) { return; } const payload: WebhookPayload = { event, timestamp: new Date().toISOString(), data, }; const payloadString = JSON.stringify(payload); const headers: Record = { 'Content-Type': 'application/json', 'X-Paste-Event': event, 'User-Agent': 'Paste-ES-Webhook/1.0', }; if (settings.webhookSecret) { const signature = signPayload(payloadString, settings.webhookSecret); headers['X-Paste-Signature'] = `sha256=${signature}`; } await sendWithRetry(settings.webhookUrl, headers, payloadString); } catch (error) { console.error('Error preparing webhook:', error); } })(); }