diff --git a/docker/nginx-https.conf b/docker/nginx-https.conf index c2f90f35..aaf5e739 100644 --- a/docker/nginx-https.conf +++ b/docker/nginx-https.conf @@ -23,7 +23,6 @@ http { return 301 https://$host:${SSL_PORT}$request_uri; } - # HTTPS Server server { listen ${SSL_PORT} ssl; server_name _; @@ -41,7 +40,6 @@ http { index index.html index.htm; } - # Handle missing source map files gracefully location ~* \.map$ { return 404; access_log off; diff --git a/docker/nginx.conf b/docker/nginx.conf index b78418b7..9f37c80a 100644 --- a/docker/nginx.conf +++ b/docker/nginx.conf @@ -29,7 +29,6 @@ http { index index.html index.htm; } - # Handle missing source map files gracefully location ~* \.map$ { return 404; access_log off; diff --git a/package.json b/package.json index d8c1fa4b..3151adaa 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "termix", "private": true, - "version": "1.7.1", + "version": "1.7.2", "description": "A web-based server management platform with SSH terminal, tunneling, and file editing capabilities", "author": "Karmaa", "main": "electron/main.cjs", diff --git a/src/backend/utils/field-crypto.ts b/src/backend/utils/field-crypto.ts index 2be3935e..88a1cddf 100644 --- a/src/backend/utils/field-crypto.ts +++ b/src/backend/utils/field-crypto.ts @@ -27,12 +27,7 @@ class FieldCrypto { "oidc_identifier", "oidcIdentifier", ]), - ssh_data: new Set([ - "password", - "key", - "key_password", - "keyPassword", - ]), + ssh_data: new Set(["password", "key", "key_password", "keyPassword"]), ssh_credentials: new Set([ "password", "private_key", diff --git a/src/backend/utils/lazy-field-encryption.ts b/src/backend/utils/lazy-field-encryption.ts index 06c43d8c..3d18ff5e 100644 --- a/src/backend/utils/lazy-field-encryption.ts +++ b/src/backend/utils/lazy-field-encryption.ts @@ -6,7 +6,7 @@ export class LazyFieldEncryption { key_password: "keyPassword", private_key: "privateKey", public_key: "publicKey", - // Reverse mappings for Drizzle ORM (camelCase -> snake_case) + keyPassword: "key_password", privateKey: "private_key", publicKey: "public_key", diff --git a/src/ui/Desktop/Apps/File Manager/FileManager.tsx b/src/ui/Desktop/Apps/File Manager/FileManager.tsx index 73ac9bc6..ea991003 100644 --- a/src/ui/Desktop/Apps/File Manager/FileManager.tsx +++ b/src/ui/Desktop/Apps/File Manager/FileManager.tsx @@ -589,7 +589,6 @@ function FileManagerContent({ initialHost, onClose }: FileManagerProps) { async function handleDeleteFiles(files: FileItem[]) { if (!sshSessionId || files.length === 0) return; - // Determine the confirmation message based on file count and type let confirmMessage: string; if (files.length === 1) { const file = files[0]; @@ -613,10 +612,8 @@ function FileManagerContent({ initialHost, onClose }: FileManagerProps) { }); } - // Add permanent deletion warning const fullMessage = `${confirmMessage}\n\n${t("fileManager.permanentDeleteWarning")}`; - // Show confirmation dialog confirmWithToast( fullMessage, async () => { diff --git a/src/ui/Desktop/Apps/Host Manager/HostManagerEditor.tsx b/src/ui/Desktop/Apps/Host Manager/HostManagerEditor.tsx index 08a41724..fe0a2c0b 100644 --- a/src/ui/Desktop/Apps/Host Manager/HostManagerEditor.tsx +++ b/src/ui/Desktop/Apps/Host Manager/HostManagerEditor.tsx @@ -210,7 +210,18 @@ export function HostManagerEditor({ defaultPath: z.string().optional(), }) .superRefine((data, ctx) => { - if (data.authType === "key") { + if (data.authType === "password") { + if ( + !data.password || + (typeof data.password === "string" && data.password.trim() === "") + ) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: t("hosts.passwordRequired"), + path: ["password"], + }); + } + } else if (data.authType === "key") { if ( !data.key || (typeof data.key === "string" && data.key.trim() === "") diff --git a/src/ui/Desktop/DesktopApp.tsx b/src/ui/Desktop/DesktopApp.tsx index 3c1487f6..add9611d 100644 --- a/src/ui/Desktop/DesktopApp.tsx +++ b/src/ui/Desktop/DesktopApp.tsx @@ -24,7 +24,10 @@ function AppContent() { const [isAdmin, setIsAdmin] = useState(false); const [authLoading, setAuthLoading] = useState(true); const [showVersionCheck, setShowVersionCheck] = useState(true); - const [isTopbarOpen, setIsTopbarOpen] = useState(true); + const [isTopbarOpen, setIsTopbarOpen] = useState(() => { + const saved = localStorage.getItem("topNavbarOpen"); + return saved !== null ? JSON.parse(saved) : true; + }); const { currentTab, tabs } = useTabs(); useEffect(() => { @@ -64,6 +67,10 @@ function AppContent() { return () => window.removeEventListener("storage", handleStorageChange); }, []); + useEffect(() => { + localStorage.setItem("topNavbarOpen", JSON.stringify(isTopbarOpen)); + }, [isTopbarOpen]); + const handleSelectView = (nextView: string) => { setMountedViews((prev) => { if (prev.has(nextView)) return prev; diff --git a/src/ui/Desktop/Navigation/LeftSidebar.tsx b/src/ui/Desktop/Navigation/LeftSidebar.tsx index 2f7ef7ee..72ae3bef 100644 --- a/src/ui/Desktop/Navigation/LeftSidebar.tsx +++ b/src/ui/Desktop/Navigation/LeftSidebar.tsx @@ -101,7 +101,10 @@ export function LeftSidebar({ const [deleteLoading, setDeleteLoading] = React.useState(false); const [deleteError, setDeleteError] = React.useState(null); - const [isSidebarOpen, setIsSidebarOpen] = useState(true); + const [isSidebarOpen, setIsSidebarOpen] = useState(() => { + const saved = localStorage.getItem("leftSidebarOpen"); + return saved !== null ? JSON.parse(saved) : true; + }); const { tabs: tabList, @@ -181,7 +184,6 @@ export function LeftSidebar({ newHost.key !== existingHost.key || newHost.keyPassword !== existingHost.keyPassword || newHost.keyType !== existingHost.keyType || - newHost.credentialId !== existingHost.credentialId || newHost.defaultPath !== existingHost.defaultPath || JSON.stringify(newHost.tags) !== JSON.stringify(existingHost.tags) || @@ -247,6 +249,10 @@ export function LeftSidebar({ return () => clearTimeout(handler); }, [search]); + React.useEffect(() => { + localStorage.setItem("leftSidebarOpen", JSON.stringify(isSidebarOpen)); + }, [isSidebarOpen]); + const filteredHosts = React.useMemo(() => { if (!debouncedSearch.trim()) return hosts; const q = debouncedSearch.trim().toLowerCase();