143 lines · 5.6 KB
import { all, chunks, first, placeholders } from "./db";
import { forbidden, notFoundError } from "./errors";
import type { Permission, RepoRow, RepoSummary, Viewer } from "~/lib/types";

const PERMISSION_RANK: Record<Permission, number> = { none: 0, read: 1, write: 2, admin: 3 };

export const REPO_SELECT = `SELECT r.*, a.name AS owner_name FROM repos r JOIN accounts a ON a.id = r.owner_id`;

/** Follows rename and transfer redirects so old URLs (web, raw and git) keep working. */
export async function loadRepo(owner: string, name: string): Promise<RepoRow | null> {
  return (
    (await first<RepoRow>(`${REPO_SELECT} WHERE a.name = ? AND r.name = ?`, owner, name)) ??
    (await first<RepoRow>(
      `${REPO_SELECT} WHERE r.id = COALESCE(
        (SELECT repo_id FROM repo_redirects WHERE owner_name = ?1 AND repo_name = ?2),
        (SELECT r2.id FROM account_redirects ar JOIN repos r2 ON r2.owner_id = ar.account_id
          WHERE ar.old_name = ?1 AND r2.name = ?2 COLLATE NOCASE))`,
      owner,
      name,
    ))
  );
}

export async function loadRepoById(id: string): Promise<RepoRow | null> {
  return first<RepoRow>(`${REPO_SELECT} WHERE r.id = ?`, id);
}

export async function loadRepoByArtifactsName(artifactsName: string): Promise<RepoRow | null> {
  return first<RepoRow>(`${REPO_SELECT} WHERE r.artifacts_name = ?`, artifactsName);
}

export function hasPermission(actual: Permission, needed: Permission): boolean {
  return PERMISSION_RANK[actual] >= PERMISSION_RANK[needed];
}

export async function repoPermission(repo: RepoRow, viewer: Viewer | null): Promise<Permission> {
  const base: Permission = repo.visibility === "public" ? "read" : "none";
  if (!viewer) return base;
  if (viewer.isAdmin || repo.owner_id === viewer.id) return "admin";