65 lines · 2.5 KB
import { env } from "cloudflare:workers";
import { first } from "../db";
import { loadRepoById } from "../repos";
import { notifyQuietly, quiet } from "./deliver";
import { accountAdmins, repoAdmins } from "./recipients";

const DEDUPE_TTL_SECONDS = 60 * 60;

type IntegrationInfo = { id: string; scope_type: "repo" | "org"; scope_id: string; kind: string; name: string };

/** Tells integration admins (repo admins or org owners) that a delivery or job died; at most once an hour each. */
async function notifyIntegrationDeadUnsafe(
  source: { deliveryId: string } | { jobId: string },
  what: string,
): Promise<void> {
  const integration =
    "deliveryId" in source
      ? await first<IntegrationInfo>(
          `SELECT i.id, i.scope_type, i.scope_id, i.kind, i.name FROM integration_deliveries d
           JOIN integrations i ON i.id = d.integration_id WHERE d.id = ?`,
          source.deliveryId,
        )
      : await first<IntegrationInfo>(
          `SELECT i.id, i.scope_type, i.scope_id, i.kind, i.name FROM integration_jobs j
           JOIN integrations i ON i.id = j.integration_id WHERE j.id = ?`,
          source.jobId,
        );
  if (!integration) return;
  const dedupeKey = `notify:integration-dead:${integration.id}`;
  if (await env.KV.get(dedupeKey)) return;
  await env.KV.put(dedupeKey, "1", { expirationTtl: DEDUPE_TTL_SECONDS });
  const details = [
    `${what} for the ${integration.kind.replace(/_/g, " ")} integration "${integration.name}" gave up after repeated failures.`,
  ];
  if (integration.scope_type === "repo") {
    const repo = await loadRepoById(integration.scope_id);
    if (!repo) return;
    await notifyQuietly({
      category: "integration",
      repoId: repo.id,