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, username: string, permission: "read" | "write" | "admin" | "none", ): Promise { 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); } else { await run( `INSERT INTO collaborators (repo_id, user_id, permission, created_at) VALUES (?, ?, ?, ?) ON CONFLICT (repo_id, user_id) DO UPDATE SET permission = excluded.permission`, repo.id, user.id, permission, Date.now(), ); } }