From a4b61cc27fe94b57db78d785923bdbdfe0d5bcde Mon Sep 17 00:00:00 2001 From: ZacharyZcR Date: Sun, 23 Aug 2026 22:45:24 +0800 Subject: [PATCH] Fix Proxmox credential guest imports (#1300) --- .../database/routes/host-bulk-routes.ts | 24 ++++++++++++----- .../database/routes/host-bulk-routes.test.ts | 26 ++++++++++++++++++- 2 files changed, 43 insertions(+), 7 deletions(-) diff --git a/src/backend/database/routes/host-bulk-routes.ts b/src/backend/database/routes/host-bulk-routes.ts index a575dae0..c888f43a 100644 --- a/src/backend/database/routes/host-bulk-routes.ts +++ b/src/backend/database/routes/host-bulk-routes.ts @@ -105,6 +105,16 @@ export function parseSSHConfig(content: string): SSHConfigHost[] { return results; } +export function importedHostUsername( + connectionType: string, + authType: unknown, + username: unknown, +): string | null { + if (isNonEmptyString(username)) return username; + if (connectionType !== "ssh" || authType === "credential") return ""; + return null; +} + export function registerHostBulkRoutes( router: Router, authenticateJWT: RequestHandler, @@ -558,10 +568,12 @@ export function registerHostBulkRoutes( continue; } - if ( - effectiveConnectionType === "ssh" && - !isNonEmptyString(hostData.username) - ) { + const username = importedHostUsername( + effectiveConnectionType, + hostData.authType, + hostData.username, + ); + if (username === null) { results.failed++; results.errors.push( `Host ${i + 1}: Username required for SSH connections`, @@ -660,12 +672,12 @@ export function registerHostBulkRoutes( const sshDataObj: Record = { userId: userId, connectionType: effectiveConnectionType, - name: hostData.name || `${hostData.username || ""}@${hostData.ip}`, + name: hostData.name || `${username}@${hostData.ip}`, folder: hostData.folder || "Default", tags: Array.isArray(hostData.tags) ? hostData.tags.join(",") : "", ip: hostData.ip, port: hostData.port, - username: hostData.username || null, + username, pin: hostData.pin || false, enableTerminal: hostData.enableTerminal !== false, enableTunnel: hostData.enableTunnel !== false, diff --git a/src/backend/tests/database/routes/host-bulk-routes.test.ts b/src/backend/tests/database/routes/host-bulk-routes.test.ts index 5904258c..c4f4c4db 100644 --- a/src/backend/tests/database/routes/host-bulk-routes.test.ts +++ b/src/backend/tests/database/routes/host-bulk-routes.test.ts @@ -1,5 +1,8 @@ import { describe, it, expect } from "vitest"; -import { parseSSHConfig } from "../../../database/routes/host-bulk-routes.js"; +import { + importedHostUsername, + parseSSHConfig, +} from "../../../database/routes/host-bulk-routes.js"; describe("parseSSHConfig", () => { it("parses a basic Host block", () => { @@ -102,3 +105,24 @@ Host server expect(parseSSHConfig(" \n\n ")).toHaveLength(0); }); }); + +describe("importedHostUsername", () => { + it("lets credential-backed SSH hosts inherit the credential username", () => { + expect(importedHostUsername("ssh", "credential", "")).toBe(""); + }); + + it("keeps requiring usernames for other SSH authentication modes", () => { + expect(importedHostUsername("ssh", "password", "")).toBeNull(); + expect(importedHostUsername("ssh", "key", undefined)).toBeNull(); + }); + + it("always returns a non-null database value for non-SSH hosts", () => { + expect(importedHostUsername("rdp", "password", undefined)).toBe(""); + }); + + it("preserves an explicitly configured host username", () => { + expect(importedHostUsername("ssh", "credential", "guest-admin")).toBe( + "guest-admin", + ); + }); +});