import { env } from "cloudflare:workers";
import { enforceRateLimit, rawRequestIp } from "../limits";
import { HttpError } from "../errors";
import { customHostKey, isForgeHostname, metaKey, sitesDomain, slotKey } from "./config";
import { hostLabel } from "./naming";
import { accessSubject, challenge, handleAccessRoute } from "./access";
import { runSiteWorker } from "./runtime";
import { loadManifest, plain } from "./static";
import { handleAbuseReport, handleScratchUpload } from "./scratch";
import { meteredResponse } from "./usage";
import { ownerWithinSiteQuota, scratchWithinQuota } from "./metering";
import { globalCeilingHit } from "./caps";
import { hardenScratchResponse } from "./harden";
import type { SiteMeta, SlotPointer } from "./config";
const POINTER_CACHE_SECONDS = 30;
const REPORT_PATH = "/__gitfor/report";
const UNMETERED = new WeakSet<Response>();
const OFF_MESSAGE = "Sites are temporarily unavailable";
/** Operator kill switch: SITES_ENABLED="false" stops every site, preview and scratch upload. */
function sitesSwitchedOff(): boolean {
return env.SITES_ENABLED === "false";
}
type Resolved = { label: string; pointer: SlotPointer; meta: SiteMeta };
function pointer(label: string): Promise<SlotPointer | null> {
return env.KV.get<SlotPointer>(slotKey(label), { type: "json", cacheTtl: POINTER_CACHE_SECONDS });
}
function meta(siteId: string): Promise<SiteMeta | null> {
return env.KV.get<SiteMeta>(metaKey(siteId), { type: "json", cacheTtl: POINTER_CACHE_SECONDS });
}
/** Maps a request host to the site and deployment it serves; custom domains only after verification wrote KV. */
export async function resolveSiteHost(hostname: string): Promise<Resolved | null> {
const domain = sitesDomain();
if (!domain) return null;