289 lines · 13.2 KB
import { env } from "cloudflare:workers";
import { credentialCoversRepo, requestCredential, scopedRepoPermission } from "./token-scope";
import type { RequestCredential, TokenArea } from "./token-scope";
import { hasPermission, loadRepo, loadRepoById, repoPermission, repoReadOnlyReason } from "./repos";
import { accountDb, repoDb } from "./scoped-db";
import { forgetLocator, locate, registerLocator } from "./scoped-db/locators";
import { scopedId } from "./scoped-db/ids";
import { attachmentUsage, syncAttachmentBytes } from "./attachment-bytes";
import { limitBrowse, limitWrite } from "./limits";
import { HttpError } from "./errors";
import { paymentRequiredResponse, planBlock } from "./metering/quota";
import type { Permission, RepoRow } from "~/lib/types";
import { contentDisposition } from "~/lib/content-disposition";

const UPLOAD_PATH = /^\/([^/]+)\/([^/]+)\/attachments$/;
const SERVE_PATH = /^\/attachments\/([a-z0-9]+)\/([^/]+)$/;
const MB = 1024 * 1024;
const MAX_IMAGE_BYTES = 10 * MB;
const MAX_FILE_BYTES = 25 * MB;
const OWNER_QUOTA_BYTES = 1024 * MB;
const UPLOADER_QUOTA_BYTES = 250 * MB;
/** Readers without write access only ever spend their own, smaller budget; never the repository owner's. */
const GUEST_UPLOADER_QUOTA_BYTES = 50 * MB;
export const ATTACHMENT_ORPHAN_GRACE_MS = 7 * 24 * 60 * 60 * 1000;
const ORPHAN_GRACE_MS = ATTACHMENT_ORPHAN_GRACE_MS;
const ORPHAN_SWEEP_BATCH = 50;
const WIDTH_BUCKETS = [320, 640, 1280, 2560];
const MAX_IMAGE_WIDTH = WIDTH_BUCKETS[WIDTH_BUCKETS.length - 1];
const HTTP_FORBIDDEN = 403;
const HTTP_TOO_LARGE = 413;
const HTTP_UNSUPPORTED = 415;
const TRANSFORMABLE_IMAGES = new Set(["image/png", "image/jpeg", "image/webp", "image/avif"]);
const ALLOWED_TYPES = new Set([
  ...TRANSFORMABLE_IMAGES,
  "image/gif",
  "video/mp4",
  "video/webm",
  "application/pdf",
  "application/zip",
  "application/gzip",