132 lines · 5.1 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`;

export async function loadRepo(owner: string, name: string): Promise<RepoRow | null> {
  return first<RepoRow>(`${REPO_SELECT} WHERE a.name = ? AND r.name = ?`, 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";

  const [membership, collaborator] = await Promise.all([
    first<{ role: "owner" | "member" }>(
      "SELECT role FROM org_members WHERE org_id = ? AND user_id = ?",
      repo.owner_id,
      viewer.id,
    ),
    first<{ permission: Permission }>(
      "SELECT permission FROM collaborators WHERE repo_id = ? AND user_id = ?",
      repo.id,
      viewer.id,
    ),