144 lines · 5.1 KB
import { repoDb } from "../scoped-db";
import { accountsById } from "../people";
import { randomId } from "../crypto";
import { assertBelowCap } from "../limits";
import { HttpError, forbidden, notFoundError } from "../errors";

const MAX_LOCKS_PER_REPO = 1000;
const MAX_LOCK_PATH = 1024;
export const DEFAULT_LOCK_PAGE = 100;
export const MAX_LOCK_PAGE = 100;

export type LfsLock = { id: string; path: string; locked_at: string; owner: { name: string } };

type LockRow = { id: string; path: string; created_at: number; owner_id: string; owner_name: string };

const LOCK_SELECT = "SELECT l.id, l.path, l.created_at, l.owner_id FROM lfs_locks l";

/** Owner names come from the D1 directory; locks whose owner account is gone are dropped, as the old join did. */
async function withOwners(rows: Array<Omit<LockRow, "owner_name">>): Promise<LockRow[]> {
  const owners = await accountsById(rows.map((row) => row.owner_id));
  return rows.flatMap((row) => {
    const owner = owners.get(row.owner_id);
    return owner ? [{ ...row, owner_name: owner.name }] : [];
  });
}

async function findLock(repoId: string, where: string, value: string): Promise<LockRow | null> {
  const row = await repoDb(repoId).first<Omit<LockRow, "owner_name">>(
    `${LOCK_SELECT} WHERE l.repo_id = ? AND ${where}`,
    repoId,
    value,
  );
  return row ? ((await withOwners([row]))[0] ?? null) : null;
}

export function lockJson(row: LockRow): LfsLock {
  return {
    id: row.id,
    path: row.path,
    locked_at: new Date(row.created_at).toISOString(),