import { first } from "./db";
import { HttpError } from "./errors";
import {
EMAIL_UNVERIFIED,
EMAIL_UNVERIFIED_MESSAGE,
VERIFY_EMAIL_PATH,
canWriteAsViewer,
} from "~/lib/email-verification";
import type { Viewer } from "~/lib/types";
/**
* The one gate for writing and paying (AUTH-B1). It passes for a verified primary email, a linked GitHub
* account, site admins and instances without email (all folded into `viewer.emailVerified` by toViewer, the
* same rule as metering's TRUSTED_USER). Account self-service (profile, emails, password, 2FA, sessions,
* deletion) and starring/following stay open so the holder can always fix or leave the account.
*/
export function assertVerifiedForWrites(viewer: Pick<Viewer, "isAdmin" | "emailVerified">): void {
if (canWriteAsViewer(viewer)) return;
throw new HttpError(403, EMAIL_UNVERIFIED_MESSAGE, { code: EMAIL_UNVERIFIED, verifyPath: VERIFY_EMAIL_PATH });
}
/** Deleting a repository is self-service only for its owning user; deleting one as a collaborator or org admin is a write. */
export function assertMayDeleteRepo(
repo: { owner_id: string },
viewer: Pick<Viewer, "id" | "isAdmin" | "emailVerified">,
): void {
if (repo.owner_id !== viewer.id) assertVerifiedForWrites(viewer);
}
/** Deleting an organization is self-service only when the viewer is its sole member. */
export async function assertMayDeleteOrg(
orgId: string,
viewer: Pick<Viewer, "id" | "isAdmin" | "emailVerified">,
): Promise<void> {
if (canWriteAsViewer(viewer)) return;
const others = await first<{ ok: number }>(
"SELECT 1 AS ok FROM org_members WHERE org_id = ? AND user_id != ? LIMIT 1",
orgId,
viewer.id,
);