fix: add host issue, screen flickering issue, 2fa issue, open terminal session icon missing issue

This commit is contained in:
LukeGus
2026-01-26 00:28:40 -06:00
parent 1c9d4560c7
commit 98b760ce64
7 changed files with 88 additions and 139 deletions
+48 -127
View File
@@ -49,6 +49,50 @@ function isValidPort(port: unknown): port is number {
return typeof port === "number" && port > 0 && port <= 65535; return typeof port === "number" && port > 0 && port <= 65535;
} }
function transformHostResponse(
host: Record<string, unknown>,
): Record<string, unknown> {
return {
...host,
tags:
typeof host.tags === "string"
? host.tags
? host.tags.split(",").filter(Boolean)
: []
: [],
pin: !!host.pin,
enableTerminal: !!host.enableTerminal,
enableTunnel: !!host.enableTunnel,
enableFileManager: !!host.enableFileManager,
enableDocker: !!host.enableDocker,
showTerminalInSidebar: !!host.showTerminalInSidebar,
showFileManagerInSidebar: !!host.showFileManagerInSidebar,
showTunnelInSidebar: !!host.showTunnelInSidebar,
showDockerInSidebar: !!host.showDockerInSidebar,
showServerStatsInSidebar: !!host.showServerStatsInSidebar,
tunnelConnections: host.tunnelConnections
? JSON.parse(host.tunnelConnections as string)
: [],
jumpHosts: host.jumpHosts ? JSON.parse(host.jumpHosts as string) : [],
quickActions: host.quickActions
? JSON.parse(host.quickActions as string)
: [],
statsConfig: host.statsConfig
? JSON.parse(host.statsConfig as string)
: undefined,
terminalConfig: host.terminalConfig
? JSON.parse(host.terminalConfig as string)
: undefined,
dockerConfig: host.dockerConfig
? JSON.parse(host.dockerConfig as string)
: undefined,
forceKeyboardInteractive: host.forceKeyboardInteractive === "true",
socks5ProxyChain: host.socks5ProxyChain
? JSON.parse(host.socks5ProxyChain as string)
: [],
};
}
const authManager = AuthManager.getInstance(); const authManager = AuthManager.getInstance();
const permissionManager = PermissionManager.getInstance(); const permissionManager = PermissionManager.getInstance();
const authenticateJWT = authManager.createAuthMiddleware(); const authenticateJWT = authManager.createAuthMiddleware();
@@ -461,34 +505,7 @@ router.post(
} }
const createdHost = result; const createdHost = result;
const baseHost = { const baseHost = transformHostResponse(createdHost);
...createdHost,
tags:
typeof createdHost.tags === "string"
? createdHost.tags
? createdHost.tags.split(",").filter(Boolean)
: []
: [],
pin: !!createdHost.pin,
enableTerminal: !!createdHost.enableTerminal,
enableTunnel: !!createdHost.enableTunnel,
tunnelConnections: createdHost.tunnelConnections
? JSON.parse(createdHost.tunnelConnections as string)
: [],
jumpHosts: createdHost.jumpHosts
? JSON.parse(createdHost.jumpHosts as string)
: [],
enableFileManager: !!createdHost.enableFileManager,
enableDocker: !!createdHost.enableDocker,
showTerminalInSidebar: !!createdHost.showTerminalInSidebar,
showFileManagerInSidebar: !!createdHost.showFileManagerInSidebar,
showTunnelInSidebar: !!createdHost.showTunnelInSidebar,
showDockerInSidebar: !!createdHost.showDockerInSidebar,
showServerStatsInSidebar: !!createdHost.showServerStatsInSidebar,
statsConfig: createdHost.statsConfig
? JSON.parse(createdHost.statsConfig as string)
: undefined,
};
const resolvedHost = const resolvedHost =
(await resolveHostCredentials(baseHost, userId)) || baseHost; (await resolveHostCredentials(baseHost, userId)) || baseHost;
@@ -1067,37 +1084,7 @@ router.put(
} }
const updatedHost = updatedHosts[0]; const updatedHost = updatedHosts[0];
const baseHost = { const baseHost = transformHostResponse(updatedHost);
...updatedHost,
tags:
typeof updatedHost.tags === "string"
? updatedHost.tags
? updatedHost.tags.split(",").filter(Boolean)
: []
: [],
pin: !!updatedHost.pin,
enableTerminal: !!updatedHost.enableTerminal,
enableTunnel: !!updatedHost.enableTunnel,
tunnelConnections: updatedHost.tunnelConnections
? JSON.parse(updatedHost.tunnelConnections as string)
: [],
jumpHosts: updatedHost.jumpHosts
? JSON.parse(updatedHost.jumpHosts as string)
: [],
enableFileManager: !!updatedHost.enableFileManager,
enableDocker: !!updatedHost.enableDocker,
showTerminalInSidebar: !!updatedHost.showTerminalInSidebar,
showFileManagerInSidebar: !!updatedHost.showFileManagerInSidebar,
showTunnelInSidebar: !!updatedHost.showTunnelInSidebar,
showDockerInSidebar: !!updatedHost.showDockerInSidebar,
showServerStatsInSidebar: !!updatedHost.showServerStatsInSidebar,
statsConfig: updatedHost.statsConfig
? JSON.parse(updatedHost.statsConfig as string)
: undefined,
dockerConfig: updatedHost.dockerConfig
? JSON.parse(updatedHost.dockerConfig as string)
: undefined,
};
const resolvedHost = const resolvedHost =
(await resolveHostCredentials(baseHost, userId)) || baseHost; (await resolveHostCredentials(baseHost, userId)) || baseHost;
@@ -1302,41 +1289,7 @@ router.get(
const result = await Promise.all( const result = await Promise.all(
data.map(async (row: Record<string, unknown>) => { data.map(async (row: Record<string, unknown>) => {
const baseHost = { const baseHost = {
...row, ...transformHostResponse(row),
tags:
typeof row.tags === "string"
? row.tags
? row.tags.split(",").filter(Boolean)
: []
: [],
pin: !!row.pin,
enableTerminal: !!row.enableTerminal,
enableTunnel: !!row.enableTunnel,
tunnelConnections: row.tunnelConnections
? JSON.parse(row.tunnelConnections as string)
: [],
jumpHosts: row.jumpHosts ? JSON.parse(row.jumpHosts as string) : [],
quickActions: row.quickActions
? JSON.parse(row.quickActions as string)
: [],
enableFileManager: !!row.enableFileManager,
enableDocker: !!row.enableDocker,
showTerminalInSidebar: !!row.showTerminalInSidebar,
showFileManagerInSidebar: !!row.showFileManagerInSidebar,
showTunnelInSidebar: !!row.showTunnelInSidebar,
showDockerInSidebar: !!row.showDockerInSidebar,
showServerStatsInSidebar: !!row.showServerStatsInSidebar,
statsConfig: row.statsConfig
? JSON.parse(row.statsConfig as string)
: undefined,
terminalConfig: row.terminalConfig
? JSON.parse(row.terminalConfig as string)
: undefined,
forceKeyboardInteractive: row.forceKeyboardInteractive === "true",
socks5ProxyChain: row.socks5ProxyChain
? JSON.parse(row.socks5ProxyChain as string)
: [],
isShared: !!row.isShared, isShared: !!row.isShared,
permissionLevel: row.permissionLevel || undefined, permissionLevel: row.permissionLevel || undefined,
sharedExpiresAt: row.expiresAt || undefined, sharedExpiresAt: row.expiresAt || undefined,
@@ -1417,39 +1370,7 @@ router.get(
} }
const host = data[0]; const host = data[0];
const result = { const result = transformHostResponse(host);
...host,
tags:
typeof host.tags === "string"
? host.tags
? host.tags.split(",").filter(Boolean)
: []
: [],
pin: !!host.pin,
enableTerminal: !!host.enableTerminal,
enableTunnel: !!host.enableTunnel,
tunnelConnections: host.tunnelConnections
? JSON.parse(host.tunnelConnections)
: [],
jumpHosts: host.jumpHosts ? JSON.parse(host.jumpHosts) : [],
quickActions: host.quickActions ? JSON.parse(host.quickActions) : [],
enableFileManager: !!host.enableFileManager,
showTerminalInSidebar: !!host.showTerminalInSidebar,
showFileManagerInSidebar: !!host.showFileManagerInSidebar,
showTunnelInSidebar: !!host.showTunnelInSidebar,
showDockerInSidebar: !!host.showDockerInSidebar,
showServerStatsInSidebar: !!host.showServerStatsInSidebar,
statsConfig: host.statsConfig
? JSON.parse(host.statsConfig)
: undefined,
terminalConfig: host.terminalConfig
? JSON.parse(host.terminalConfig)
: undefined,
forceKeyboardInteractive: host.forceKeyboardInteractive === "true",
socks5ProxyChain: host.socks5ProxyChain
? JSON.parse(host.socks5ProxyChain)
: [],
};
res.json((await resolveHostCredentials(result, userId)) || result); res.json((await resolveHostCredentials(result, userId)) || result);
} catch (err) { } catch (err) {
+2 -2
View File
@@ -1145,7 +1145,7 @@ router.get("/oidc/callback", async (req, res) => {
? 30 * 24 * 60 * 60 * 1000 ? 30 * 24 * 60 * 60 * 1000
: 7 * 24 * 60 * 60 * 1000; : 7 * 24 * 60 * 60 * 1000;
res.clearCookie("jwt", authManager.getSecureCookieOptions(req)); res.clearCookie("jwt", authManager.getClearCookieOptions(req));
return res return res
.cookie("jwt", token, authManager.getSecureCookieOptions(req, maxAge)) .cookie("jwt", token, authManager.getSecureCookieOptions(req, maxAge))
@@ -1439,7 +1439,7 @@ router.post("/logout", authenticateJWT, async (req, res) => {
} }
return res return res
.clearCookie("jwt", authManager.getSecureCookieOptions(req)) .clearCookie("jwt", authManager.getClearCookieOptions(req))
.json({ success: true, message: "Logged out successfully" }); .json({ success: true, message: "Logged out successfully" });
} catch (err) { } catch (err) {
authLogger.error("Logout failed", err); authLogger.error("Logout failed", err);
+4 -1
View File
@@ -1284,7 +1284,10 @@ wss.on("connection", async (ws: WebSocket, req) => {
return; return;
} }
connectConfig.password = resolvedCredentials.password; if (!hostConfig.forceKeyboardInteractive) {
connectConfig.password = resolvedCredentials.password;
}
sendLog("auth", "info", "Using password authentication"); sendLog("auth", "info", "Using password authentication");
} else if ( } else if (
resolvedCredentials.authType === "key" && resolvedCredentials.authType === "key" &&
+9
View File
@@ -518,6 +518,15 @@ class AuthManager {
}; };
} }
getClearCookieOptions(req: RequestWithHeaders) {
return {
httpOnly: false,
secure: req.secure || req.headers["x-forwarded-proto"] === "https",
sameSite: "strict" as const,
path: "/",
};
}
createAuthMiddleware() { createAuthMiddleware() {
return async (req: Request, res: Response, next: NextFunction) => { return async (req: Request, res: Response, next: NextFunction) => {
const authReq = req as AuthenticatedRequest; const authReq = req as AuthenticatedRequest;
-4
View File
@@ -255,10 +255,6 @@ function AppContent({
setTimeout(async () => { setTimeout(async () => {
try { try {
await logoutUser(); await logoutUser();
if (isElectron()) {
localStorage.removeItem("jwt");
}
} catch (error) { } catch (error) {
console.error("Logout failed:", error); console.error("Logout failed:", error);
} }
+1 -3
View File
@@ -309,7 +309,7 @@ export function Auth({
"*", "*",
); );
setWebviewAuthSuccess(true); setWebviewAuthSuccess(true);
setTimeout(() => window.location.reload(), 100); return;
} catch (e) { } catch (e) {
console.error("Error posting auth success message:", e); console.error("Error posting auth success message:", e);
} }
@@ -518,7 +518,6 @@ export function Auth({
"*", "*",
); );
setWebviewAuthSuccess(true); setWebviewAuthSuccess(true);
setTimeout(() => window.location.reload(), 100);
setTotpLoading(false); setTotpLoading(false);
return; return;
} catch (e) { } catch (e) {
@@ -655,7 +654,6 @@ export function Auth({
"*", "*",
); );
setWebviewAuthSuccess(true); setWebviewAuthSuccess(true);
setTimeout(() => window.location.reload(), 100);
setOidcLoading(false); setOidcLoading(false);
return; return;
} catch (e) { } catch (e) {
+24 -2
View File
@@ -1029,7 +1029,7 @@ export async function createSSHHost(hostData: SSHHostData): Promise<SSHHost> {
return response.data; return response.data;
} }
} catch (error) { } catch (error) {
handleApiError(error, "create SSH host"); throw handleApiError(error, "create SSH host");
} }
} }
@@ -1107,7 +1107,7 @@ export async function updateSSHHost(
return response.data; return response.data;
} }
} catch (error) { } catch (error) {
handleApiError(error, "update SSH host"); throw handleApiError(error, "update SSH host");
} }
} }
@@ -2381,8 +2381,30 @@ export async function logoutUser(): Promise<{
}> { }> {
try { try {
const response = await authApi.post("/users/logout"); const response = await authApi.post("/users/logout");
if (isElectron()) {
localStorage.removeItem("jwt");
electronSettingsCache.delete("jwt");
} else {
const isSecure = window.location.protocol === "https:";
const cookieString = isSecure
? "jwt=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/; Secure; SameSite=Strict"
: "jwt=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/; SameSite=Strict";
document.cookie = cookieString;
}
return response.data; return response.data;
} catch (error) { } catch (error) {
if (isElectron()) {
localStorage.removeItem("jwt");
electronSettingsCache.delete("jwt");
} else {
const isSecure = window.location.protocol === "https:";
const cookieString = isSecure
? "jwt=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/; Secure; SameSite=Strict"
: "jwt=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/; SameSite=Strict";
document.cookie = cookieString;
}
handleApiError(error, "logout user"); handleApiError(error, "logout user");
} }
} }