fix(org): clean up every site when deleting an org

deleteOrgById fetched the org's sites with .limit(1), so the per-site
cleanup loop ran for at most one site: the WireGuard deletePeer call,
the Newt terminate collection, and cleanupSiteAssociations were all
skipped for every other site in the org.

Drop the limit so every site is loaded, and mirror deleteSite.ts in the
loop: run cleanupSiteAssociations for newt sites so clients of other
orgs do not keep stale associations for the deleted sites.

Covered by a standalone regression test that seeds two wireguard and
two newt sites plus a stale association-cache row, runs deleteOrgById
against a stubbed exit node, and asserts all four sites were cleaned.
On the previous code the test fails with exactly the reported symptom:
one peer removal instead of two.

Fixes #3863
This commit is contained in:
choksi2212
2026-10-13 06:52:56 +05:30
parent 6bab69dccb
commit dc579acc44
3 changed files with 343 additions and 15 deletions
+19 -15
View File
@@ -23,6 +23,7 @@ import { sendToClient } from "#dynamic/routers/ws";
import { deletePeer } from "@server/routers/gerbil/peers";
import { OlmErrorCodes } from "@server/routers/olm/error";
import { sendTerminateClient } from "@server/routers/client/terminate";
import { cleanupSiteAssociations } from "@server/lib/rebuildClientAssociations";
import { usageService } from "./billing/usageService";
import { LimitId } from "./billing";
@@ -55,8 +56,7 @@ export async function deleteOrgById(
const orgSites = await db
.select()
.from(sites)
.where(eq(sites.orgId, orgId))
.limit(1);
.where(eq(sites.orgId, orgId));
const orgClients = await db
.select()
@@ -73,20 +73,24 @@ export async function deleteOrgById(
await db.transaction(async (trx) => {
for (const site of orgSites) {
if (site.pubKey) {
if (site.type == "wireguard") {
if (site.type == "wireguard") {
if (site.pubKey) {
await deletePeer(site.exitNodeId!, site.pubKey);
} else if (site.type == "newt") {
const [deletedNewt] = await trx
.delete(newts)
.where(eq(newts.siteId, site.siteId))
.returning();
if (deletedNewt) {
deletedNewtIds.push(deletedNewt.newtId);
await trx
.delete(newtSessions)
.where(eq(newtSessions.newtId, deletedNewt.newtId));
}
}
} else if (site.type == "newt") {
// Same per-site cleanup deleteSite.ts performs, so that
// removing an org does not leave other orgs' clients
// holding stale associations for these sites.
await cleanupSiteAssociations(site, trx);
const [deletedNewt] = await trx
.delete(newts)
.where(eq(newts.siteId, site.siteId))
.returning();
if (deletedNewt) {
deletedNewtIds.push(deletedNewt.newtId);
await trx
.delete(newtSessions)
.where(eq(newtSessions.newtId, deletedNewt.newtId));
}
}
logger.info(`Deleting site ${site.siteId}`);