diff --git a/server/src/db/schema.ts b/server/src/db/schema.ts index 779d2f8..49ff8ad 100644 --- a/server/src/db/schema.ts +++ b/server/src/db/schema.ts @@ -224,7 +224,9 @@ export const servers = sqliteTable("servers", { lastPortScan: text("last_port_scan"), // JSON string: summary of the most recent network scan from this app tags: text("tags"), // JSON string: string[] — free-form labels for grouping and filtering, normalized by services/serverTags - // Optional link to a Proxmox VM/LXC — set by an admin, not the agent. + // Optional link to a Proxmox VM/LXC — set by an admin, not the agent. The "set null" below is what this file asks for, + // but migration 0001 created the column without it, so the database itself has no ON DELETE rule here: deleting an + // integration clears these columns in routes/integrations.ts instead. (See DATABASE.md.) proxmoxIntegrationId: integer("proxmox_integration_id").references(() => integrations.id, { onDelete: "set null", }), diff --git a/server/src/routes/integrations.ts b/server/src/routes/integrations.ts index 5a54824..0339f8d 100644 --- a/server/src/routes/integrations.ts +++ b/server/src/routes/integrations.ts @@ -272,6 +272,14 @@ integrationsRouter.delete("/:id", requireRole("admin"), asyncHandler(async (req, return res.status(404).json({ error: "not_found" }); } + // servers.proxmox_integration_id was meant to clear itself, but the database only has a plain REFERENCES on it (see + // DATABASE.md), so a linked server would block the delete. Clear the whole link here — the four columns go together. + const unlinked = await db + .update(servers) + .set({ proxmoxIntegrationId: null, proxmoxNode: null, proxmoxGuestType: null, proxmoxVmid: null }) + .where(eq(servers.proxmoxIntegrationId, id)) + .returning({ id: servers.id }); + await db.delete(integrations).where(eq(integrations.id, id)); if (existing.credentialId) { await db.delete(integrationCredentials).where(eq(integrationCredentials.id, existing.credentialId)); @@ -283,7 +291,7 @@ integrationsRouter.delete("/:id", requireRole("admin"), asyncHandler(async (req, action: "delete", targetType: "integration", targetId: id, - detail: { name: existing.name }, + detail: { name: existing.name, ...(unlinked.length > 0 ? { unlinkedServers: unlinked.length } : {}) }, }); res.status(204).end();