import { env } from "cloudflare:workers"; import { repoDb } from "./scoped-db"; import type { DeliveryRecord, StoredDelivery } from "./webhook-log-do"; function log(repoId: string) { return env.WEBHOOK_LOG.get(env.WEBHOOK_LOG.idFromName(repoId)); } export function recordWebhookDelivery(repoId: string, delivery: StoredDelivery): Promise { return log(repoId).record(repoId, delivery); } /** * Newest deliveries for a repo (or one hook). Rows written to D1 before the log moved to the per-repo * object are merged in until the 30-day retention sweep removes them. */ export async function listWebhookDeliveries( repoId: string, hookId: string | null, limit: number, ): Promise { const [current, legacy] = await Promise.all([ log(repoId).list(hookId, limit), repoDb(repoId).all( `SELECT d.id, d.webhook_id, d.event, d.status_code, d.error, d.duration_ms, d.attempt, d.created_at FROM webhook_deliveries d JOIN webhooks w ON w.id = d.webhook_id WHERE w.repo_id = ?1 AND (?2 IS NULL OR d.webhook_id = ?2) ORDER BY d.created_at DESC LIMIT ?3`, repoId, hookId, limit, ), ]); return [...current, ...legacy].sort((left, right) => right.created_at - left.created_at).slice(0, limit); } /** A delivery with its stored request body, only if it belongs to one of the repo's current hooks. */ export async function findWebhookDelivery( repoId: string, deliveryId: string, hookId: string | null, ): Promise<(StoredDelivery & { url: string; secret: string | null }) | null> { const stored = (await log(repoId).get(deliveryId)) ?? (await repoDb(repoId).first( `SELECT id, webhook_id, event, status_code, error, request_body, duration_ms, attempt, created_at FROM webhook_deliveries WHERE id = ?`, deliveryId, )); if (!stored || (hookId !== null && stored.webhook_id !== hookId)) return null; const hook = await repoDb(repoId).first<{ url: string; secret: string | null }>( "SELECT url, secret FROM webhooks WHERE id = ? AND repo_id = ?", stored.webhook_id, repoId, ); return hook ? { ...stored, ...hook } : null; } export async function clearWebhookLog(repoId: string): Promise { try { await log(repoId).clear(); } catch (error) { console.error("Webhook log clear failed", repoId, error); } }