52 lines · 1.9 KB
import { first, run } from "./db";
import { notFoundError } from "./errors";
import { assertBelowCap, CAPS } from "./limits";
import type { RepoRow } from "~/lib/types";

export async function ownerAccount(name: string): Promise<{ id: string; name: string; kind: string }> {
  const account = await first<{ id: string; name: string; kind: string }>(
    "SELECT id, name, kind FROM accounts WHERE name = ?",
    name,
  );
  if (!account) throw notFoundError("Owner not found");
  return account;
}

export async function findCollaboratorUser(username: string): Promise<{ id: string }> {
  const user = await first<{ id: string }>(
    "SELECT a.id FROM accounts a JOIN users u ON u.account_id = a.id WHERE a.name = ?",
    username,
  );
  if (!user) throw notFoundError("User not found");
  return user;
}

/** Grants, changes or (with "none") removes a user's collaborator permission; callers check admin access. */
export async function setCollaborator(
  repo: Pick<RepoRow, "id">,
  username: string,
  permission: "read" | "write" | "admin" | "none",
): Promise<void> {
  const user = await findCollaboratorUser(username);
  const existing = await first("SELECT 1 AS ok FROM collaborators WHERE repo_id = ? AND user_id = ?", repo.id, user.id);
  if (!existing && permission !== "none") {
    const count = await first<{ count: number }>(
      "SELECT COUNT(*) AS count FROM collaborators WHERE repo_id = ?",
      repo.id,
    );
    assertBelowCap(count?.count ?? 0, CAPS.collaboratorsPerRepo, "collaborators");
  }
  if (permission === "none") {
    await run("DELETE FROM collaborators WHERE repo_id = ? AND user_id = ?", repo.id, user.id);