From 0a9086fb79709612ede8aad31ee08f07c5b1db4c Mon Sep 17 00:00:00 2001 From: ZacharyZcR Date: Sun, 23 Aug 2026 22:57:37 +0800 Subject: [PATCH] Fix Proxmox sync jump host persistence (#1303) --- .../database/routes/proxmox-jump-hosts.ts | 17 ++++++++++++ src/backend/database/routes/proxmox.ts | 24 +++++------------ .../routes/proxmox-jump-hosts.test.ts | 26 +++++++++++++++++++ 3 files changed, 49 insertions(+), 18 deletions(-) create mode 100644 src/backend/database/routes/proxmox-jump-hosts.ts create mode 100644 src/backend/tests/database/routes/proxmox-jump-hosts.test.ts diff --git a/src/backend/database/routes/proxmox-jump-hosts.ts b/src/backend/database/routes/proxmox-jump-hosts.ts new file mode 100644 index 00000000..a7145312 --- /dev/null +++ b/src/backend/database/routes/proxmox-jump-hosts.ts @@ -0,0 +1,17 @@ +export function parseProxmoxJumpHosts(raw: unknown): unknown[] | null { + if (!raw) return null; + if (Array.isArray(raw)) return raw; + if (typeof raw !== "string") return null; + + try { + const parsed = JSON.parse(raw); + return Array.isArray(parsed) ? parsed : null; + } catch { + return null; + } +} + +export function serializeProxmoxJumpHosts(raw: unknown): string | null { + const parsed = parseProxmoxJumpHosts(raw); + return parsed ? JSON.stringify(parsed) : null; +} diff --git a/src/backend/database/routes/proxmox.ts b/src/backend/database/routes/proxmox.ts index 3cc7c70d..08cb9238 100644 --- a/src/backend/database/routes/proxmox.ts +++ b/src/backend/database/routes/proxmox.ts @@ -13,6 +13,10 @@ import { SSHHostKeyVerifier } from "../../hosts/host-key-verifier.js"; import { resolveHostById } from "../../hosts/host-resolver.js"; import { createJumpHostChain } from "../../hosts/jump-host-chain.js"; import { resolveProxmoxImportAuth } from "./proxmox-import-auth.js"; +import { + parseProxmoxJumpHosts, + serializeProxmoxJumpHosts, +} from "./proxmox-jump-hosts.js"; import { isSafeNodeName } from "../../hosts/proxmox-shared.js"; const router = express.Router(); @@ -183,20 +187,6 @@ type ProxmoxSyncResult = { errors: string[]; }; -function parseJumpHostsField(raw: unknown): unknown[] | null { - if (!raw) return null; - if (Array.isArray(raw)) return raw; - if (typeof raw === "string") { - try { - const parsed = JSON.parse(raw); - return Array.isArray(parsed) ? parsed : null; - } catch { - return null; - } - } - return null; -} - function parseJsonObject(value: unknown): Record { if (!value) return {}; if (typeof value === "object") return value as Record; @@ -599,7 +589,7 @@ async function discoverProxmoxGuestsForHost( guests, credentialId: hostCredentialId, defaultCredentialId: config.defaultCredentialId, - jumpHosts: parseJumpHostsField( + jumpHosts: parseProxmoxJumpHosts( (host as unknown as { jumpHosts?: unknown }).jumpHosts, ), config, @@ -770,9 +760,7 @@ async function syncProxmoxHost( telnetPort: null, defaultPath: "/", tunnelConnections: "[]", - jumpHosts: - (discovery.host as unknown as { jumpHosts?: string | null }) - .jumpHosts ?? null, + jumpHosts: serializeProxmoxJumpHosts(discovery.jumpHosts), quickActions: null, statsConfig: null, dockerConfig: null, diff --git a/src/backend/tests/database/routes/proxmox-jump-hosts.test.ts b/src/backend/tests/database/routes/proxmox-jump-hosts.test.ts new file mode 100644 index 00000000..0a103d50 --- /dev/null +++ b/src/backend/tests/database/routes/proxmox-jump-hosts.test.ts @@ -0,0 +1,26 @@ +import { describe, expect, it } from "vitest"; +import { + parseProxmoxJumpHosts, + serializeProxmoxJumpHosts, +} from "../../../database/routes/proxmox-jump-hosts.js"; + +describe("Proxmox jump-host persistence", () => { + const jumpHosts = [{ hostId: 7 }, { hostId: 9 }]; + + it("serializes resolved arrays before inserting a synced guest", () => { + expect(serializeProxmoxJumpHosts(jumpHosts)).toBe( + '[{"hostId":7},{"hostId":9}]', + ); + }); + + it("keeps stored JSON strings stable", () => { + const stored = '[{"hostId":7}]'; + expect(serializeProxmoxJumpHosts(stored)).toBe(stored); + expect(parseProxmoxJumpHosts(stored)).toEqual([{ hostId: 7 }]); + }); + + it("turns missing or malformed values into null", () => { + expect(serializeProxmoxJumpHosts(null)).toBeNull(); + expect(serializeProxmoxJumpHosts("invalid")).toBeNull(); + }); +});