v1.7.3 #390
@@ -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;
|
||||
|
||||
@@ -29,7 +29,6 @@ http {
|
||||
index index.html index.htm;
|
||||
}
|
||||
|
||||
# Handle missing source map files gracefully
|
||||
location ~* \.map$ {
|
||||
return 404;
|
||||
access_log off;
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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 () => {
|
||||
|
||||
@@ -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() === "")
|
||||
|
||||
@@ -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<boolean>(true);
|
||||
const [isTopbarOpen, setIsTopbarOpen] = useState<boolean>(() => {
|
||||
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;
|
||||
|
||||
@@ -101,7 +101,10 @@ export function LeftSidebar({
|
||||
const [deleteLoading, setDeleteLoading] = React.useState(false);
|
||||
const [deleteError, setDeleteError] = React.useState<string | null>(null);
|
||||
|
||||
const [isSidebarOpen, setIsSidebarOpen] = useState<boolean>(true);
|
||||
const [isSidebarOpen, setIsSidebarOpen] = useState<boolean>(() => {
|
||||
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();
|
||||
|
||||
Reference in New Issue
Block a user
The check for
credentialIdchanges has been removed from the host comparison logic. This could lead to a bug where the UI does not update when a host's associated credential is changed, causing stale data to be displayed. Was the removal of this line intentional? If so, could you please clarify the reasoning? Otherwise, it should be restored to ensure UI consistency.