mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-07-15 04:43:36 +00:00
fix: add host issue, screen flickering issue, 2fa issue, open terminal session icon missing issue
This commit is contained in:
@@ -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) {
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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" &&
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
@@ -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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user