import { env } from "cloudflare:workers";
import { forgetLocator, registerLocator } from "../scoped-db/locators";
import { registerSite, siteDb, siteScope } from "./store";
import { hmacSha256Hex, randomId, randomToken, sha256Hex } from "../crypto";
import { HttpError, badRequest } from "../errors";
import { enforceRateLimit, numberVar } from "../limits";
import { DeployError } from "./collect";
import { cookieSecret, labelUrl, SCRATCH_LIMITS } from "./config";
import { activateSiteDeployment, failSiteDeployment, insertDeployment } from "./deploy";
import { scratchLabel } from "./naming";
import { ipBucket } from "~/lib/ip";
import type { CollectedFiles } from "./collect";
import type { SiteRow } from "./config";
const DAY = 24 * 60 * 60 * 1000;
const HTTP_TOO_MANY = 429;
export const CLAIM_PREFIX = "gitfor_claim";
export type ScratchResult = {
url: string | null;
claimToken: string;
claimUrl: string;
expiresAt: number;
repo?: string;
};
/** Orders unclaimed scratch sites by expiry, so the sweep reads only expired ones (`scratch_expiry`). */
export function scratchExpiryKey(expiresAt: number, siteId: string): string {
return `${String(expiresAt).padStart(15, "0")}:${siteId}`;
}
export async function registerScratchExpiry(site: Pick<SiteRow, "id" | "repo_id">, expiresAt: number): Promise<void> {
await registerLocator("scratch_expiry", scratchExpiryKey(expiresAt, site.id), siteScope(site));
}
export async function forgetScratchExpiry(siteId: string, expiresAt: number): Promise<void> {
await forgetLocator("scratch_expiry", scratchExpiryKey(expiresAt, siteId));
}
/** Keyed so stored hashes can't be reversed by enumerating the IPv4 space. */