201 lines · 7.7 KB
import { env } from "cloudflare:workers";
import { batch, first, run, stmt } from "../db";
import { randomId } from "../crypto";
import { CAPS, enforceRateLimit, numberVar } from "../limits";
import { HttpError } from "../errors";
import type { JobTokenAccess } from "./job-token";

const DEFAULT_CACHE_BYTES_PER_REPO = 2 * 1024 * 1024 * 1024;
const MAX_ENTRY_BYTES = 1024 * 1024 * 1024;
const MAX_KEY_LENGTH = 512;
const MAX_RESTORE_KEYS = 10;
const EVICTION_BATCH = 100;
const IDLE_EVICTION_MS = 7 * 24 * 60 * 60 * 1000;
const VERSION = /^[0-9a-f]{1,64}$/;

type CacheRow = {
  id: string;
  repo_id: string;
  ref: string;
  key: string;
  version: string;
  size: number;
  r2_key: string;
  created_at: number;
  last_used_at: number;
};

type CacheScope = { repoId: string; ref: string; defaultRef: string };

export function cacheQuotaBytes(): number {
  return numberVar(env.CACHE_BYTES_PER_REPO, DEFAULT_CACHE_BYTES_PER_REPO);
}

function json(data: unknown, status: number): Response {
  return Response.json(data, { status });
}

async function scopeFor(access: JobTokenAccess): Promise<CacheScope | null> {
  const row = await first<{ ref: string; default_branch: string }>(
    `SELECT w.ref, r.default_branch FROM workflow_jobs j JOIN workflow_runs w ON w.id = j.run_id