58 lines · 2.2 KB
import { HttpError } from "../errors";
import { findObject, openObject } from "./objects";
import { parseLfsPointer } from "./pointer";
import { chargeBandwidth } from "./quota";
import type { RepoRow } from "~/lib/types";

const IMAGE_TYPES_BY_EXTENSION: Record<string, string> = {
  png: "image/png",
  jpg: "image/jpeg",
  jpeg: "image/jpeg",
  gif: "image/gif",
  webp: "image/webp",
  avif: "image/avif",
  ico: "image/x-icon",
};

/** LFS content has no sniffed type, so images are typed by extension to stay previewable. */
export function lfsImageType(path: string): string {
  const extension = path.split(".").pop()?.toLowerCase() ?? "";
  return IMAGE_TYPES_BY_EXTENSION[extension] ?? "application/octet-stream";
}

/** Swaps an LFS pointer for its stored content, charging the owner's bandwidth; null when bytes are not a pointer. */
export async function lfsContentFor(
  repo: Pick<RepoRow, "id" | "fork_of_id" | "owner_id" | "visibility">,
  bytes: Uint8Array,
): Promise<R2ObjectBody | null> {
  const pointer = parseLfsPointer(bytes);
  if (!pointer) return null;
  const object = await openObject(repo, pointer.oid);
  if (!object) return null;
  try {
    await chargeBandwidth(repo.owner_id, object.size, repo.id);
  } catch (error) {
    await object.body.cancel();
    if (error instanceof HttpError) return null;
    throw error;
  }
  return object;
}