diff --git a/src/server/routes/organizations.ts b/src/server/routes/organizations.ts index 946f990..06f3160 100644 --- a/src/server/routes/organizations.ts +++ b/src/server/routes/organizations.ts @@ -366,6 +366,9 @@ export async function organizationsRoutes(app: FastifyInstance) { await tx.container.deleteMany({ where: { serverId: { in: serverIds } }, }); + await tx.environment.deleteMany({ + where: { serverId: { in: serverIds } }, + }); await tx.network.deleteMany({ where: { serverId: { in: serverIds } }, }); @@ -378,6 +381,9 @@ export async function organizationsRoutes(app: FastifyInstance) { await tx.backup.deleteMany({ where: { serverId: { in: serverIds } }, }); + await tx.userStorageDestination.deleteMany({ + where: { serverId: { in: serverIds } }, + }); await tx.server.deleteMany({ where: { id: { in: serverIds } } }); } diff --git a/src/server/routes/servers.ts b/src/server/routes/servers.ts index 489aa16..c5bd3be 100644 --- a/src/server/routes/servers.ts +++ b/src/server/routes/servers.ts @@ -385,10 +385,12 @@ export async function serverRoutes(app: FastifyInstance) { await tx.userServerAccess.deleteMany({ where: { serverId: id } }); await tx.serverMetric.deleteMany({ where: { serverId: id } }); await tx.container.deleteMany({ where: { serverId: id } }); + await tx.environment.deleteMany({ where: { serverId: id } }); await tx.network.deleteMany({ where: { serverId: id } }); await tx.firewallRulePreset.deleteMany({ where: { serverId: id } }); await tx.appInstall.deleteMany({ where: { serverId: id } }); await tx.backup.deleteMany({ where: { serverId: id } }); + await tx.userStorageDestination.deleteMany({ where: { serverId: id } }); const invitations = await tx.userInvitation.findMany({ where: { diff --git a/tests/backend/server-delete-cleanup.test.ts b/tests/backend/server-delete-cleanup.test.ts new file mode 100644 index 0000000..e2473e5 --- /dev/null +++ b/tests/backend/server-delete-cleanup.test.ts @@ -0,0 +1,45 @@ +import assert from "node:assert/strict"; +import { readFileSync } from "node:fs"; +import test from "node:test"; + +function readSource(path: string) { + return readFileSync(path, "utf8"); +} + +function assertEnvironmentCleanupBeforeServerDelete(source: string) { + const environmentCleanup = source.indexOf("tx.environment.deleteMany"); + const serverDelete = source.indexOf("tx.server.delete"); + + assert.notEqual(environmentCleanup, -1); + assert.notEqual(serverDelete, -1); + assert.ok( + environmentCleanup < serverDelete, + "server deletion must remove environments before deleting servers", + ); +} + +function assertStorageDestinationCleanupBeforeServerDelete(source: string) { + const storageCleanup = source.indexOf("tx.userStorageDestination.deleteMany"); + const serverDelete = source.indexOf("tx.server.delete"); + + assert.notEqual(storageCleanup, -1); + assert.notEqual(serverDelete, -1); + assert.ok( + storageCleanup < serverDelete, + "server deletion must remove server-specific storage destinations before deleting servers", + ); +} + +test("server deletion removes dependent records before deleting the server", () => { + const source = readSource("src/server/routes/servers.ts"); + + assertEnvironmentCleanupBeforeServerDelete(source); + assertStorageDestinationCleanupBeforeServerDelete(source); +}); + +test("organization deletion removes dependent records before deleting servers", () => { + const source = readSource("src/server/routes/organizations.ts"); + + assertEnvironmentCleanupBeforeServerDelete(source); + assertStorageDestinationCleanupBeforeServerDelete(source); +});