238 lines · 9.2 KB
import { env } from "cloudflare:workers";
import { isArtifactsError } from "../git/artifacts";
import { loadRepoById } from "../repos";
import { all, first, run } from "../db";
import { hmacSha256Hex, randomId } from "../crypto";
import { issueParticipants } from "../issues";
import { CAPS } from "../limits";
import { indexRepoCode } from "../code/search-index";
import { deliverEmail, emailTemplates, queueEmail } from "../email";
import { expandRecipients, planDeliveries } from "../notification-prefs";
import type { NotifyMessage, QueueMessage, WebhookEvent } from "./emit";
import { triggerWorkflowsForEvent } from "../ci/event-triggers";
import { enqueueIntegrationDispatch } from "../integrations/queue";
import { publishRepoEvent } from "../mcp/events";

const DELIVERY_TIMEOUT_MS = 10_000;
const MAX_RESPONSE_BODY = 4096;
const BASE_RETRY_SECONDS = 15;
const MAX_RETRY_SECONDS = 3600;
const HTTP_OK_MIN = 200;
const HTTP_OK_MAX = 300;
const MENTION = /(?:^|[^\w@])@([A-Za-z0-9](?:[A-Za-z0-9-]{0,38}))/g;

const HTTP_REDIRECT_MIN = 300;
const HTTP_REDIRECT_MAX = 400;

function hostOf(url: string): string | null {
  try {
    return url ? new URL(url).hostname.toLowerCase() : null;
  } catch {
    return null;
  }
}

/** Webhooks may not target the forge's own hostnames (or subdomains), so deliveries cannot reflect internal pages. */
export function isForgeWebhookUrl(url: string): boolean {
  const host = hostOf(url);
  if (!host) return true;
  const forgeHosts = [hostOf(env.PUBLIC_URL), env.PAGES_DOMAIN ? env.PAGES_DOMAIN.toLowerCase() : null, "localhost"];
  return forgeHosts.some((forge) => forge && (host === forge || host.endsWith(`.${forge}`)));