Run code cleanup, add sidebar persistence, fix OIDC credentials, force SSH password.
This commit is contained in:
@@ -23,7 +23,6 @@ http {
|
|||||||
return 301 https://$host:${SSL_PORT}$request_uri;
|
return 301 https://$host:${SSL_PORT}$request_uri;
|
||||||
}
|
}
|
||||||
|
|
||||||
# HTTPS Server
|
|
||||||
server {
|
server {
|
||||||
listen ${SSL_PORT} ssl;
|
listen ${SSL_PORT} ssl;
|
||||||
server_name _;
|
server_name _;
|
||||||
@@ -41,7 +40,6 @@ http {
|
|||||||
index index.html index.htm;
|
index index.html index.htm;
|
||||||
}
|
}
|
||||||
|
|
||||||
# Handle missing source map files gracefully
|
|
||||||
location ~* \.map$ {
|
location ~* \.map$ {
|
||||||
return 404;
|
return 404;
|
||||||
access_log off;
|
access_log off;
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ http {
|
|||||||
index index.html index.htm;
|
index index.html index.htm;
|
||||||
}
|
}
|
||||||
|
|
||||||
# Handle missing source map files gracefully
|
|
||||||
location ~* \.map$ {
|
location ~* \.map$ {
|
||||||
return 404;
|
return 404;
|
||||||
access_log off;
|
access_log off;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "termix",
|
"name": "termix",
|
||||||
"private": true,
|
"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",
|
"description": "A web-based server management platform with SSH terminal, tunneling, and file editing capabilities",
|
||||||
"author": "Karmaa",
|
"author": "Karmaa",
|
||||||
"main": "electron/main.cjs",
|
"main": "electron/main.cjs",
|
||||||
|
|||||||
@@ -27,12 +27,7 @@ class FieldCrypto {
|
|||||||
"oidc_identifier",
|
"oidc_identifier",
|
||||||
"oidcIdentifier",
|
"oidcIdentifier",
|
||||||
]),
|
]),
|
||||||
ssh_data: new Set([
|
ssh_data: new Set(["password", "key", "key_password", "keyPassword"]),
|
||||||
"password",
|
|
||||||
"key",
|
|
||||||
"key_password",
|
|
||||||
"keyPassword",
|
|
||||||
]),
|
|
||||||
ssh_credentials: new Set([
|
ssh_credentials: new Set([
|
||||||
"password",
|
"password",
|
||||||
"private_key",
|
"private_key",
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ export class LazyFieldEncryption {
|
|||||||
key_password: "keyPassword",
|
key_password: "keyPassword",
|
||||||
private_key: "privateKey",
|
private_key: "privateKey",
|
||||||
public_key: "publicKey",
|
public_key: "publicKey",
|
||||||
// Reverse mappings for Drizzle ORM (camelCase -> snake_case)
|
|
||||||
keyPassword: "key_password",
|
keyPassword: "key_password",
|
||||||
privateKey: "private_key",
|
privateKey: "private_key",
|
||||||
publicKey: "public_key",
|
publicKey: "public_key",
|
||||||
|
|||||||
@@ -589,7 +589,6 @@ function FileManagerContent({ initialHost, onClose }: FileManagerProps) {
|
|||||||
async function handleDeleteFiles(files: FileItem[]) {
|
async function handleDeleteFiles(files: FileItem[]) {
|
||||||
if (!sshSessionId || files.length === 0) return;
|
if (!sshSessionId || files.length === 0) return;
|
||||||
|
|
||||||
// Determine the confirmation message based on file count and type
|
|
||||||
let confirmMessage: string;
|
let confirmMessage: string;
|
||||||
if (files.length === 1) {
|
if (files.length === 1) {
|
||||||
const file = files[0];
|
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")}`;
|
const fullMessage = `${confirmMessage}\n\n${t("fileManager.permanentDeleteWarning")}`;
|
||||||
|
|
||||||
// Show confirmation dialog
|
|
||||||
confirmWithToast(
|
confirmWithToast(
|
||||||
fullMessage,
|
fullMessage,
|
||||||
async () => {
|
async () => {
|
||||||
|
|||||||
@@ -210,7 +210,18 @@ export function HostManagerEditor({
|
|||||||
defaultPath: z.string().optional(),
|
defaultPath: z.string().optional(),
|
||||||
})
|
})
|
||||||
.superRefine((data, ctx) => {
|
.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 (
|
if (
|
||||||
!data.key ||
|
!data.key ||
|
||||||
(typeof data.key === "string" && data.key.trim() === "")
|
(typeof data.key === "string" && data.key.trim() === "")
|
||||||
|
|||||||
@@ -24,7 +24,10 @@ function AppContent() {
|
|||||||
const [isAdmin, setIsAdmin] = useState(false);
|
const [isAdmin, setIsAdmin] = useState(false);
|
||||||
const [authLoading, setAuthLoading] = useState(true);
|
const [authLoading, setAuthLoading] = useState(true);
|
||||||
const [showVersionCheck, setShowVersionCheck] = 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();
|
const { currentTab, tabs } = useTabs();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -64,6 +67,10 @@ function AppContent() {
|
|||||||
return () => window.removeEventListener("storage", handleStorageChange);
|
return () => window.removeEventListener("storage", handleStorageChange);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
localStorage.setItem("topNavbarOpen", JSON.stringify(isTopbarOpen));
|
||||||
|
}, [isTopbarOpen]);
|
||||||
|
|
||||||
const handleSelectView = (nextView: string) => {
|
const handleSelectView = (nextView: string) => {
|
||||||
setMountedViews((prev) => {
|
setMountedViews((prev) => {
|
||||||
if (prev.has(nextView)) return prev;
|
if (prev.has(nextView)) return prev;
|
||||||
|
|||||||
@@ -101,7 +101,10 @@ export function LeftSidebar({
|
|||||||
const [deleteLoading, setDeleteLoading] = React.useState(false);
|
const [deleteLoading, setDeleteLoading] = React.useState(false);
|
||||||
const [deleteError, setDeleteError] = React.useState<string | null>(null);
|
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 {
|
const {
|
||||||
tabs: tabList,
|
tabs: tabList,
|
||||||
@@ -181,7 +184,6 @@ export function LeftSidebar({
|
|||||||
newHost.key !== existingHost.key ||
|
newHost.key !== existingHost.key ||
|
||||||
newHost.keyPassword !== existingHost.keyPassword ||
|
newHost.keyPassword !== existingHost.keyPassword ||
|
||||||
newHost.keyType !== existingHost.keyType ||
|
newHost.keyType !== existingHost.keyType ||
|
||||||
newHost.credentialId !== existingHost.credentialId ||
|
|
||||||
newHost.defaultPath !== existingHost.defaultPath ||
|
newHost.defaultPath !== existingHost.defaultPath ||
|
||||||
JSON.stringify(newHost.tags) !==
|
JSON.stringify(newHost.tags) !==
|
||||||
JSON.stringify(existingHost.tags) ||
|
JSON.stringify(existingHost.tags) ||
|
||||||
@@ -247,6 +249,10 @@ export function LeftSidebar({
|
|||||||
return () => clearTimeout(handler);
|
return () => clearTimeout(handler);
|
||||||
}, [search]);
|
}, [search]);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
localStorage.setItem("leftSidebarOpen", JSON.stringify(isSidebarOpen));
|
||||||
|
}, [isSidebarOpen]);
|
||||||
|
|
||||||
const filteredHosts = React.useMemo(() => {
|
const filteredHosts = React.useMemo(() => {
|
||||||
if (!debouncedSearch.trim()) return hosts;
|
if (!debouncedSearch.trim()) return hosts;
|
||||||
const q = debouncedSearch.trim().toLowerCase();
|
const q = debouncedSearch.trim().toLowerCase();
|
||||||
|
|||||||
Reference in New Issue
Block a user