122 lines · 4.4 KB
import { env } from "cloudflare:workers";
import { first } from "./db";
import { repoDb } from "./scoped-db";
import { sitesDomain } from "./sites/config";
import { hostHolder, loadSiteRow } from "./sites/store";
import { hostLabel } from "./sites/naming";
import type { AbuseTargetKind } from "~/lib/abuse";

export type AbuseTarget = { kind: AbuseTargetKind; id: string };

const COMMENT_ANCHOR = /^#(?:issue|review-|discussion-)?comment-([A-Za-z0-9_-]{1,64})$/;
const ISSUE_PATH = /^(issues|pull)$/;

function parseUrl(targetUrl: string): URL | null {
  try {
    return new URL(targetUrl);
  } catch {
    return null;
  }
}

/** Maps a reported URL to the hosted site behind it: site labels, preview labels, then verified custom domains. */
export async function siteIdForUrl(targetUrl: string): Promise<string | null> {
  const host = parseUrl(targetUrl)?.hostname.toLowerCase();
  if (!host) return null;
  const domain = sitesDomain();
  const label = domain ? hostLabel(host, domain) : null;
  // Production labels, preview labels and custom domains are all registered as site_host -> site id.
  const siteId = await hostHolder(label ?? host);
  if (!siteId) return null;
  return (await loadSiteRow(siteId)) ? siteId : null;
}

function isForgeUrl(url: URL): boolean {
  try {
    return url.hostname.toLowerCase() === new URL(env.PUBLIC_URL).hostname.toLowerCase();
  } catch {
    return false;
  }
}