import { all, batch, first, stmt } from "./db";
import { badRequest, conflict, forbidden, notFoundError } from "./errors";
import { canAdministerAccount, loadRepoById } from "./repos";
import { deleteRepository } from "./repo-ops";
import { deleteRunnerRow } from "./ci/runner-registration";
import { disableSite } from "./sites/site";
import type { SiteRow } from "./sites/config";
import { cancelDomainAddon } from "./billing/domain-addon";
import type { Viewer } from "~/lib/types";
export const MAX_ORG_REPOS_DELETED = 100;
/**
* Deletes an organization. Owners only, with the org name typed as confirmation. Refuses while repositories
* remain unless `deleteRepos`, in which case each goes through `deleteRepository`. The org's current and
* previous names are retired so they can never be re-registered (GIT-9); everything keyed to the account
* (memberships, runners, registration tokens, secrets, sites, projects, LFS usage) cascades with the row.
*/
export async function deleteOrganization(input: {
org: string;
confirm: string;
deleteRepos: boolean;
viewer: Viewer;
}): Promise<{ deletedRepos: string[] }> {
const org = await first<{ id: string; name: string; disabled_at: number | null }>(
"SELECT id, name, disabled_at FROM accounts WHERE name = ? AND kind = 'org'",
input.org,
);
if (!org) throw notFoundError("Organization not found");
if (org.disabled_at && !input.viewer.isAdmin) throw forbidden("This organization is disabled and can't be deleted");
if (!(await canAdministerAccount(org.id, input.viewer))) {
const member = await first(
"SELECT 1 AS ok FROM org_members WHERE org_id = ? AND user_id = ?",
org.id,
input.viewer.id,
);
throw member ? forbidden("Only organization owners can delete it") : notFoundError("Organization not found");
}
if (input.confirm !== org.name) throw badRequest(`Type ${org.name} to confirm`);