206 lines · 8.0 KB
import { env } from "cloudflare:workers";
import { all, first, run } from "../db";
import { hmacSha256Hex, randomId, randomToken, sha256Hex } from "../crypto";
import { HttpError, badRequest } from "../errors";
import { enforceRateLimit, numberVar, requestIp } from "../limits";
import { DeployError, extractArtifact } from "../pages/collect";
import { cookieSecret, labelUrl, manifestKey, metaKey, publishMeta, SCRATCH_LIMITS, slotKey } from "./config";
import {
  activateSiteDeployment,
  assertLabelFree,
  failSiteDeployment,
  insertDeployment,
  loadSiteById,
  unpublishSite,
} from "./deploy";
import { claimedLabel, scratchLabel } from "./naming";
import { plain } from "./static";
import type { SiteMeta, SiteRow } from "./config";
import type { Viewer } from "~/lib/types";

const DAY = 24 * 60 * 60 * 1000;
const HTTP_TOO_MANY = 429;
const HTTP_TOO_LARGE = 413;
const SWEEP_BATCH = 50;
const CLAIM_PREFIX = "gitfor_claim";

/** Keyed so stored hashes can't be reversed by enumerating the IPv4 space. */
function ipHash(request: Request): Promise<string> {
  return hmacSha256Hex(cookieSecret(), `site-ip:${requestIp(request)}`);
}

async function readCapped(request: Request): Promise<Uint8Array<ArrayBuffer>> {
  const declared = Number(request.headers.get("content-length") ?? "0");
  if (declared > SCRATCH_LIMITS.maxUploadBytes) throw new HttpError(HTTP_TOO_LARGE, "Upload exceeds 10 MiB");
  if (!request.body) throw badRequest("Send a .tar.gz archive as the request body");
  const reader = request.body.getReader();
  const chunks: Uint8Array[] = [];
  let size = 0;
  for (;;) {
    const next = await reader.read();