121 lines · 4.6 KB
import { env } from "cloudflare:workers";
import { all } from "../db";
import { isArtifactsError } from "./artifacts";
import { artifactsCall } from "./artifacts-call";
import { allShards, shardOfName } from "./artifacts-shards";

/** Artifacts repos that `createRepository` creates before inserting their row can be younger than this. */
const GRACE_MS = 24 * 60 * 60 * 1000;
const PAGE_SIZE = 200;
const PAGES_PER_RUN = 2;
const CURSOR_TTL_SECONDS = 7 * 24 * 60 * 60;
/** D1 binds at most 100 parameters per statement. */
const D1_MAX_PARAMS = 90;
const OWN_NAME = /^r\d*-[A-Za-z0-9_-]+$/;

type SweepMode = "off" | "report" | "delete";

function isLocalUrl(raw: string | undefined): boolean {
  try {
    const host = new URL(raw ?? "").hostname;
    return host === "localhost" || host === "127.0.0.1" || host === "[::1]" || host.endsWith(".localhost");
  } catch {
    return true;
  }
}

/**
 * `delete` also needs ARTIFACTS_NAMESPACE_EXCLUSIVE=true and a non-local PUBLIC_URL: a namespace shared with
 * another deployment (or a local dev database) would make every repository of the other side look orphaned.
 */
export function sweepMode(vars: { mode?: string; exclusive?: string; publicUrl?: string }): SweepMode {
  if (vars.mode === "off") return "off";
  if (vars.mode !== "delete") return "report";
  if (vars.exclusive !== "true" || isLocalUrl(vars.publicUrl)) {
    console.error(
      "ARTIFACTS_ORPHAN_SWEEP=delete ignored: it requires ARTIFACTS_NAMESPACE_EXCLUSIVE=true and a non-local PUBLIC_URL; reporting only",
    );
    return "report";
  }
  return "delete";