diff --git a/src/backend/database/db/index.ts b/src/backend/database/db/index.ts index 84a3232c..5df19845 100644 --- a/src/backend/database/db/index.ts +++ b/src/backend/database/db/index.ts @@ -701,6 +701,23 @@ const migrateSchema = () => { } } + // Migration: Add sudo_password column to ssh_data table + try { + sqlite.prepare("SELECT sudo_password FROM ssh_data LIMIT 1").get(); + } catch { + try { + sqlite.exec("ALTER TABLE ssh_data ADD COLUMN sudo_password TEXT"); + databaseLogger.info("Added sudo_password column to ssh_data table", { + operation: "schema_migration", + }); + } catch (alterError) { + databaseLogger.warn("Failed to add sudo_password column", { + operation: "schema_migration", + error: alterError, + }); + } + } + // RBAC Phase 2: Roles tables try { sqlite.prepare("SELECT id FROM roles LIMIT 1").get(); diff --git a/src/backend/database/db/schema.ts b/src/backend/database/db/schema.ts index 1371057b..db531988 100644 --- a/src/backend/database/db/schema.ts +++ b/src/backend/database/db/schema.ts @@ -66,6 +66,7 @@ export const sshData = sqliteTable("ssh_data", { key: text("key", { length: 8192 }), key_password: text("key_password"), keyType: text("key_type"), + sudoPassword: text("sudo_password"), autostartPassword: text("autostart_password"), autostartKey: text("autostart_key", { length: 8192 }), diff --git a/src/backend/database/routes/ssh.ts b/src/backend/database/routes/ssh.ts index 25a6a39f..679ec722 100644 --- a/src/backend/database/routes/ssh.ts +++ b/src/backend/database/routes/ssh.ts @@ -243,6 +243,7 @@ router.post( key, keyPassword, keyType, + sudoPassword, pin, enableTerminal, enableTunnel, @@ -315,6 +316,7 @@ router.post( terminalConfig: terminalConfig ? JSON.stringify(terminalConfig) : null, forceKeyboardInteractive: forceKeyboardInteractive ? "true" : "false", notes: notes || null, + sudoPassword: sudoPassword || null, useSocks5: useSocks5 ? 1 : 0, socks5Host: socks5Host || null, socks5Port: socks5Port || null, @@ -493,6 +495,7 @@ router.put( key, keyPassword, keyType, + sudoPassword, pin, enableTerminal, enableTunnel, @@ -560,6 +563,7 @@ router.put( terminalConfig: terminalConfig ? JSON.stringify(terminalConfig) : null, forceKeyboardInteractive: forceKeyboardInteractive ? "true" : "false", notes: notes || null, + sudoPassword: sudoPassword || null, useSocks5: useSocks5 ? 1 : 0, socks5Host: socks5Host || null, socks5Port: socks5Port || null, diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index 6f366c5f..6daaadd9 100644 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -942,7 +942,9 @@ "quickActionsOrder": "Quick action buttons will appear in the order listed above on the Server Stats page", "advancedAuthSettings": "Advanced Authentication Settings", "sudoPasswordAutoFill": "Sudo Password Auto-Fill", - "sudoPasswordAutoFillDesc": "Automatically offer to insert SSH password when sudo prompts for password" + "sudoPasswordAutoFillDesc": "Automatically offer to insert SSH password when sudo prompts for password", + "sudoPassword": "Sudo Password", + "sudoPasswordDesc": "Optional password for sudo commands (useful with key authentication)" }, "terminal": { "title": "Terminal", @@ -1617,6 +1619,7 @@ "folder": "folder", "password": "password", "keyPassword": "key password", + "sudoPassword": "sudo password (optional)", "notes": "add notes about this host...", "expirationDate": "Select expiration date", "pastePrivateKey": "Paste your private key here...", diff --git a/src/types/index.ts b/src/types/index.ts index fd97146c..82605fec 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -28,6 +28,7 @@ export interface SSHHost { key?: string; keyPassword?: string; keyType?: string; + sudoPassword?: string; forceKeyboardInteractive?: boolean; autostartPassword?: string; @@ -95,6 +96,7 @@ export interface SSHHostData { key?: File | null; keyPassword?: string; keyType?: string; + sudoPassword?: string; credentialId?: number | null; overrideCredentialUsername?: boolean; enableTerminal?: boolean; diff --git a/src/ui/desktop/DesktopApp.tsx b/src/ui/desktop/DesktopApp.tsx index 99cfa6d9..54f28dd0 100644 --- a/src/ui/desktop/DesktopApp.tsx +++ b/src/ui/desktop/DesktopApp.tsx @@ -285,6 +285,16 @@ function AppContent() { className={`fixed inset-0 bg-background z-[20000] transition-opacity duration-700 ${ transitionPhase === "fadeOut" ? "opacity-100" : "opacity-0" }`} + style={{ + background: "#0e0e10", + backgroundImage: `repeating-linear-gradient( + 45deg, + transparent, + transparent 35px, + rgba(255, 255, 255, 0.03) 35px, + rgba(255, 255, 255, 0.03) 37px + )`, + }} > {transitionPhase === "fadeOut" && ( <> diff --git a/src/ui/desktop/apps/credentials/CredentialEditor.tsx b/src/ui/desktop/apps/credentials/CredentialEditor.tsx index 68307439..05df52bb 100644 --- a/src/ui/desktop/apps/credentials/CredentialEditor.tsx +++ b/src/ui/desktop/apps/credentials/CredentialEditor.tsx @@ -6,6 +6,7 @@ import { Button } from "@/components/ui/button"; import { Form, FormControl, + FormDescription, FormField, FormItem, FormLabel, diff --git a/src/ui/desktop/apps/host-manager/HostManagerEditor.tsx b/src/ui/desktop/apps/host-manager/HostManagerEditor.tsx index 9c2a7dcd..c5ccbbde 100644 --- a/src/ui/desktop/apps/host-manager/HostManagerEditor.tsx +++ b/src/ui/desktop/apps/host-manager/HostManagerEditor.tsx @@ -517,6 +517,7 @@ export function HostManagerEditor({ autoMosh: z.boolean(), moshCommand: z.string(), sudoPasswordAutoFill: z.boolean(), + sudoPassword: z.string().optional(), }) .optional(), forceKeyboardInteractive: z.boolean().optional(), @@ -2726,6 +2727,27 @@ export function HostManagerEditor({ )} /> + {form.watch("terminalConfig.sudoPasswordAutoFill") && ( + ( + + {t("hosts.sudoPassword")} + + + + + {t("hosts.sudoPasswordDesc")} + + + )} + /> + )} +