235 lines · 10.3 KB
import { all, chunks, first, placeholders } from "./db";
import { isScratchRepo } from "./sites/config";
import { HttpError, 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 };

/** What a plain org member gets on every repository the org owns; owners always get `admin`. */
export const ORG_DEFAULT_PERMISSIONS = ["none", "read", "write"] as const;
export type OrgDefaultPermission = (typeof ORG_DEFAULT_PERMISSIONS)[number];

function orgDefaultPermission(value: string | null | undefined): OrgDefaultPermission {
  return value === "none" || value === "write" ? value : "read";
}

/**
 * SQL predicate: the user `userExpr` can read the repo aliased `repoAlias` (public, owner, collaborator, org
 * owner, or org member when the org's default permission grants at least read). Site admins are not covered;
 * callers short-circuit them.
 */
export function repoReadableSql(repoAlias: string, userExpr: string): string {
  return `(${repoAlias}.visibility = 'public' OR ${repoAlias}.owner_id = ${userExpr}
  OR EXISTS (SELECT 1 FROM collaborators c_ WHERE c_.repo_id = ${repoAlias}.id AND c_.user_id = ${userExpr})
  OR EXISTS (SELECT 1 FROM org_members m_ JOIN accounts o_ ON o_.id = m_.org_id
    WHERE m_.org_id = ${repoAlias}.owner_id AND m_.user_id = ${userExpr}
      AND (m_.role = 'owner' OR o_.org_default_permission != 'none')))`;
}

/** SQL predicate: the user `userExpr` has at least write on repo `repoAlias` (owner, write/admin collaborator, org owner, or member of a write-by-default org). */
export function repoWritableSql(repoAlias: string, userExpr: string): string {
  return `(${repoAlias}.owner_id = ${userExpr}
  OR EXISTS (SELECT 1 FROM collaborators c_ WHERE c_.repo_id = ${repoAlias}.id AND c_.user_id = ${userExpr} AND c_.permission IN ('write', 'admin'))
  OR EXISTS (SELECT 1 FROM org_members m_ JOIN accounts o_ ON o_.id = m_.org_id
    WHERE m_.org_id = ${repoAlias}.owner_id AND m_.user_id = ${userExpr}
      AND (m_.role = 'owner' OR o_.org_default_permission = 'write')))`;
}

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. */