136 lines · 4.6 KB
import { DurableObject } from "cloudflare:workers";
import { repoDb } from "./scoped-db";

const RETENTION_MS = 30 * 24 * 60 * 60 * 1000;
const MAX_PER_HOOK = 500;
const PRUNE_EVERY_MS = 24 * 60 * 60 * 1000;
const MAX_LIST = 100;

export type DeliveryRecord = {
  id: string;
  webhook_id: string;
  event: string;
  status_code: number | null;
  error: string | null;
  duration_ms: number | null;
  attempt: number;
  created_at: number;
};

export type StoredDelivery = DeliveryRecord & { request_body: string };

/**
 * One per repository: the outbound webhook delivery log. Every delivery and retry appends here instead of
 * to D1, so log volume scales with repos rather than landing on the shared writer. Retention (30 days,
 * MAX_PER_HOOK rows per hook) runs on the object's own alarm, which also drops rows of deleted hooks.
 */
export class WebhookLog extends DurableObject<Env> {
  private ready = false;

  private init(): void {
    if (this.ready) return;
    this.ctx.storage.sql.exec(`CREATE TABLE IF NOT EXISTS deliveries (
      id TEXT PRIMARY KEY,
      webhook_id TEXT NOT NULL,
      event TEXT NOT NULL,
      status_code INTEGER,
      error TEXT,
      request_body TEXT NOT NULL,
      duration_ms INTEGER,
      attempt INTEGER NOT NULL,