import { all, first, placeholders } from "../db";
import { accountDb, repoDb } from "../scoped-db";
import { CAPS } from "../limits";
import { repoReadableSql } from "../repos";
import type { DirectNotifyMessage } from "../events/emit";
type Access = DirectNotifyMessage["access"];
const READ_SQL = `SELECT 1 AS ok FROM repos r WHERE r.id = ?1 AND r.hidden_at IS NULL AND ${repoReadableSql("r", "?2")}`;
const ADMIN_SQL = `SELECT 1 AS ok FROM repos r WHERE r.id = ?1 AND (r.owner_id = ?2
OR EXISTS (SELECT 1 FROM collaborators c WHERE c.repo_id = r.id AND c.user_id = ?2 AND c.permission = 'admin')
OR EXISTS (SELECT 1 FROM org_members m WHERE m.org_id = r.owner_id AND m.user_id = ?2 AND m.role = 'owner'))`;
/**
* Org security managers: listed in the org's AccountState, and only while still members of the org (D1).
* Returns the subset of `userIds` (all managers when omitted) that qualify for the repository's owner.
*/
async function securityManagers(repoId: string, userIds?: string[]): Promise<string[]> {
const repo = await first<{ owner_id: string }>("SELECT owner_id FROM repos WHERE id = ?", repoId);
if (!repo) return [];
const listed = (
await accountDb(repo.owner_id).all<{ user_id: string }>(
"SELECT user_id FROM security_managers WHERE account_id = ?",
repo.owner_id,
)
)
.map((row) => row.user_id)
.filter((id) => !userIds || userIds.includes(id));
if (listed.length === 0) return [];
const members = await all<{ user_id: string }>(
`SELECT user_id FROM org_members WHERE org_id = ? AND user_id IN (${placeholders(listed.length)})`,
repo.owner_id,
...listed,
);
return members.map((row) => row.user_id);
}
async function hasSecurityAccess(repoId: string, userId: string): Promise<boolean> {
if ((await first(ADMIN_SQL, repoId, userId)) !== null) return true;