v1.8.0 #429
No files matched your search
@@ -403,7 +403,6 @@ jobs:
|
||||
- name: Build macOS DMG
|
||||
env:
|
||||
ELECTRON_BUILDER_ALLOW_UNRESOLVED_DEPENDENCIES: true
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
APPLE_ID: ${{ secrets.APPLE_ID }}
|
||||
APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
|
||||
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
|
||||
@@ -412,6 +411,7 @@ jobs:
|
||||
if [ "${{ steps.check_certs.outputs.has_certs }}" != "true" ]; then
|
||||
npm run build
|
||||
fi
|
||||
export GH_TOKEN="${{ secrets.GITHUB_TOKEN }}"
|
||||
npx electron-builder --mac dmg --universal --x64 --arm64 --publish never
|
||||
|
||||
- name: List release directory
|
||||
|
||||
@@ -1039,26 +1039,19 @@ router.post("/logout", authenticateJWT, async (req, res) => {
|
||||
// Route: Get current user's info using JWT
|
||||
// GET /users/me
|
||||
router.get("/me", authenticateJWT, async (req: Request, res: Response) => {
|
||||
console.log("=== /users/me CALLED ===");
|
||||
const userId = (req as AuthenticatedRequest).userId;
|
||||
console.log("User ID from JWT:", userId);
|
||||
|
||||
if (!isNonEmptyString(userId)) {
|
||||
console.log("ERROR: Invalid userId");
|
||||
authLogger.warn("Invalid userId in JWT for /users/me");
|
||||
return res.status(401).json({ error: "Invalid userId" });
|
||||
}
|
||||
try {
|
||||
const user = await db.select().from(users).where(eq(users.id, userId));
|
||||
console.log("User found:", user.length > 0 ? "YES" : "NO");
|
||||
|
||||
if (!user || user.length === 0) {
|
||||
console.log("ERROR: User not found in database");
|
||||
authLogger.warn(`User not found for /users/me: ${userId}`);
|
||||
return res.status(401).json({ error: "User not found" });
|
||||
}
|
||||
|
||||
console.log("SUCCESS: Returning user info");
|
||||
res.json({
|
||||
userId: user[0].id,
|
||||
username: user[0].username,
|
||||
@@ -1067,7 +1060,6 @@ router.get("/me", authenticateJWT, async (req: Request, res: Response) => {
|
||||
totp_enabled: !!user[0].totp_enabled,
|
||||
});
|
||||
} catch (err) {
|
||||
console.log("ERROR: Exception thrown:", err);
|
||||
authLogger.error("Failed to get username", err);
|
||||
res.status(500).json({ error: "Failed to get username" });
|
||||
}
|
||||
|
||||
@@ -459,6 +459,18 @@ app.post("/ssh/file_manager/ssh/connect", async (req, res) => {
|
||||
"The server does not support keyboard-interactive authentication. Please provide credentials.",
|
||||
reason: "no_keyboard",
|
||||
});
|
||||
} else if (
|
||||
resolvedCredentials.authType === "none" &&
|
||||
(err.message.includes("All configured authentication methods failed") ||
|
||||
err.message.includes("No supported authentication methods available") ||
|
||||
err.message.includes("authentication methods failed"))
|
||||
) {
|
||||
res.status(200).json({
|
||||
status: "auth_required",
|
||||
message:
|
||||
"The server does not support keyboard-interactive authentication. Please provide credentials.",
|
||||
reason: "no_keyboard",
|
||||
});
|
||||
} else {
|
||||
fileLogger.error("SSH connection failed for file manager", {
|
||||
operation: "file_connect",
|
||||
|
||||
@@ -485,6 +485,7 @@ wss.on("connection", async (ws: WebSocket, req) => {
|
||||
}, 60000);
|
||||
|
||||
let resolvedCredentials = { password, key, keyPassword, keyType, authType };
|
||||
let authMethodNotAvailable = false;
|
||||
if (credentialId && id && hostConfig.userId) {
|
||||
try {
|
||||
const credentials = await SimpleDBOps.select(
|
||||
@@ -707,6 +708,23 @@ wss.on("connection", async (ws: WebSocket, req) => {
|
||||
|
||||
sshConn.on("error", (err: Error) => {
|
||||
clearTimeout(connectionTimeout);
|
||||
|
||||
if (
|
||||
(authMethodNotAvailable && resolvedCredentials.authType === "none") ||
|
||||
(resolvedCredentials.authType === "none" &&
|
||||
err.message.includes("All configured authentication methods failed"))
|
||||
) {
|
||||
ws.send(
|
||||
JSON.stringify({
|
||||
type: "auth_method_not_available",
|
||||
message:
|
||||
"The server does not support keyboard-interactive authentication. Please provide credentials.",
|
||||
}),
|
||||
);
|
||||
cleanupSSH(connectionTimeout);
|
||||
return;
|
||||
}
|
||||
|
||||
sshLogger.error("SSH connection error", err, {
|
||||
operation: "ssh_connect",
|
||||
hostId: id,
|
||||
@@ -880,7 +898,7 @@ wss.on("connection", async (ws: WebSocket, req) => {
|
||||
host: ip,
|
||||
port,
|
||||
username,
|
||||
tryKeyboard: true,
|
||||
tryKeyboard: resolvedCredentials.authType === "none",
|
||||
keepaliveInterval: 30000,
|
||||
keepaliveCountMax: 3,
|
||||
readyTimeout: 60000,
|
||||
@@ -955,14 +973,7 @@ wss.on("connection", async (ws: WebSocket, req) => {
|
||||
if (methodsLeft.includes("keyboard-interactive")) {
|
||||
callback("keyboard-interactive");
|
||||
} else {
|
||||
ws.send(
|
||||
JSON.stringify({
|
||||
type: "auth_method_not_available",
|
||||
message:
|
||||
"The server does not support keyboard-interactive authentication. Please provide credentials.",
|
||||
methodsAvailable: methodsLeft,
|
||||
}),
|
||||
);
|
||||
authMethodNotAvailable = true;
|
||||
callback(false);
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -311,6 +311,8 @@
|
||||
"next": "Weiter",
|
||||
"previous": "Vorherige",
|
||||
"refresh": "Aktualisieren",
|
||||
"connect": "Verbinden",
|
||||
"connecting": "Verbinde...",
|
||||
"settings": "Einstellungen",
|
||||
"profile": "Profil",
|
||||
"help": "Hilfe",
|
||||
@@ -1173,7 +1175,17 @@
|
||||
"passwordResetSuccess": "Erfolgreich!",
|
||||
"passwordResetSuccessDesc": "Ihr Passwort wurde erfolgreich zurückgesetzt! Sie können sich jetzt mit Ihrem neuen Passwort anmelden.",
|
||||
"signUp": "Registrierung",
|
||||
"dataLossWarning": "Wenn Sie Ihr Passwort auf diese Weise zurücksetzen, werden alle Ihre gespeicherten SSH-Hosts, Anmeldeinformationen und andere verschlüsselte Daten gelöscht. Diese Aktion kann nicht rückgängig gemacht werden. Verwenden Sie diese Option nur, wenn Sie Ihr Passwort vergessen haben und nicht angemeldet sind."
|
||||
"dataLossWarning": "Wenn Sie Ihr Passwort auf diese Weise zurücksetzen, werden alle Ihre gespeicherten SSH-Hosts, Anmeldeinformationen und andere verschlüsselte Daten gelöscht. Diese Aktion kann nicht rückgängig gemacht werden. Verwenden Sie diese Option nur, wenn Sie Ihr Passwort vergessen haben und nicht angemeldet sind.",
|
||||
"sshAuthenticationRequired": "SSH-Authentifizierung erforderlich",
|
||||
"sshNoKeyboardInteractive": "Keyboard-Interactive-Authentifizierung nicht verfügbar",
|
||||
"sshAuthenticationFailed": "Authentifizierung fehlgeschlagen",
|
||||
"sshAuthenticationTimeout": "Authentifizierungs-Timeout",
|
||||
"sshNoKeyboardInteractiveDescription": "Der Server unterstützt keine Keyboard-Interactive-Authentifizierung. Bitte geben Sie Ihr Passwort oder Ihren SSH-Schlüssel ein.",
|
||||
"sshAuthFailedDescription": "Die angegebenen Anmeldeinformationen waren falsch. Bitte versuchen Sie es erneut mit gültigen Anmeldeinformationen.",
|
||||
"sshTimeoutDescription": "Der Authentifizierungsversuch ist abgelaufen. Bitte versuchen Sie es erneut.",
|
||||
"sshProvideCredentialsDescription": "Bitte geben Sie Ihre SSH-Anmeldeinformationen ein, um eine Verbindung zu diesem Server herzustellen.",
|
||||
"sshPasswordDescription": "Geben Sie das Passwort für diese SSH-Verbindung ein.",
|
||||
"sshKeyPasswordDescription": "Wenn Ihr SSH-Schlüssel verschlüsselt ist, geben Sie hier die Passphrase ein."
|
||||
},
|
||||
"errors": {
|
||||
"notFound": "Seite nicht gefunden",
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
"sshPrivateKey": "SSH Private Key",
|
||||
"upload": "Upload",
|
||||
"updateKey": "Update Key",
|
||||
"keyPassword": "Key Password (optional)",
|
||||
"keyPassword": "Key Password",
|
||||
"keyType": "Key Type",
|
||||
"keyTypeRSA": "RSA",
|
||||
"keyTypeECDSA": "ECDSA",
|
||||
@@ -287,6 +287,8 @@
|
||||
"loading": "Loading",
|
||||
"required": "Required",
|
||||
"optional": "Optional",
|
||||
"connect": "Connect",
|
||||
"connecting": "Connecting...",
|
||||
"clear": "Clear",
|
||||
"toggleSidebar": "Toggle Sidebar",
|
||||
"sidebar": "Sidebar",
|
||||
@@ -778,7 +780,7 @@
|
||||
"terminalCustomizationNotice": "Note: Terminal customizations only work on desktop (website and Electron app). Mobile apps and mobile website use system default terminal settings.",
|
||||
"noneAuthTitle": "Keyboard-Interactive Authentication",
|
||||
"noneAuthDescription": "This authentication method will use keyboard-interactive authentication when connecting to the SSH server.",
|
||||
"noneAuthDetails": "Keyboard-interactive authentication allows the server to prompt you for credentials during connection. This is useful for servers that require multi-factor authentication or dynamic password entry."
|
||||
"noneAuthDetails": "Keyboard-interactive authentication allows the server to prompt you for credentials during connection. This is useful for servers that require multi-factor authentication or if you do not want to save credentials locally."
|
||||
},
|
||||
"terminal": {
|
||||
"title": "Terminal",
|
||||
@@ -1275,6 +1277,16 @@
|
||||
"yourBackupCodes": "Your Backup Codes",
|
||||
"download": "Download",
|
||||
"setupTwoFactorTitle": "Set Up Two-Factor Authentication",
|
||||
"sshAuthenticationRequired": "SSH Authentication Required",
|
||||
"sshNoKeyboardInteractive": "Keyboard-Interactive Authentication Unavailable",
|
||||
"sshAuthenticationFailed": "Authentication Failed",
|
||||
"sshAuthenticationTimeout": "Authentication Timeout",
|
||||
"sshNoKeyboardInteractiveDescription": "The server does not support keyboard-interactive authentication. Please provide your password or SSH key.",
|
||||
"sshAuthFailedDescription": "The provided credentials were incorrect. Please try again with valid credentials.",
|
||||
"sshTimeoutDescription": "The authentication attempt timed out. Please try again.",
|
||||
"sshProvideCredentialsDescription": "Please provide your SSH credentials to connect to this server.",
|
||||
"sshPasswordDescription": "Enter the password for this SSH connection.",
|
||||
"sshKeyPasswordDescription": "If your SSH key is encrypted, enter the passphrase here.",
|
||||
"step1ScanQR": "Step 1: Scan the QR code with your authenticator app",
|
||||
"manualEntryCode": "Manual Entry Code",
|
||||
"cannotScanQRText": "If you can't scan the QR code, enter this code manually in your authenticator app",
|
||||
|
||||
@@ -326,6 +326,8 @@
|
||||
"next": "Próximo",
|
||||
"previous": "Anterior",
|
||||
"refresh": "Atualizar",
|
||||
"connect": "Conectar",
|
||||
"connecting": "Conectando...",
|
||||
"settings": "Configurações",
|
||||
"profile": "Perfil",
|
||||
"help": "Ajuda",
|
||||
@@ -1220,7 +1222,17 @@
|
||||
"enterNewPassword": "Digite sua nova senha para o usuário:",
|
||||
"passwordResetSuccess": "Sucesso!",
|
||||
"signUp": "Cadastrar",
|
||||
"dataLossWarning": "Redefinir sua senha desta forma excluirá todos os seus hosts SSH salvos, credenciais e outros dados criptografados. Esta ação não pode ser desfeita. Use isso apenas se você esqueceu sua senha e não está logado."
|
||||
"dataLossWarning": "Redefinir sua senha desta forma excluirá todos os seus hosts SSH salvos, credenciais e outros dados criptografados. Esta ação não pode ser desfeita. Use isso apenas se você esqueceu sua senha e não está logado.",
|
||||
"sshAuthenticationRequired": "Autenticação SSH Necessária",
|
||||
"sshNoKeyboardInteractive": "Autenticação Interativa por Teclado Indisponível",
|
||||
"sshAuthenticationFailed": "Falha na Autenticação",
|
||||
"sshAuthenticationTimeout": "Tempo Limite de Autenticação",
|
||||
"sshNoKeyboardInteractiveDescription": "O servidor não suporta autenticação interativa por teclado. Por favor, forneça sua senha ou chave SSH.",
|
||||
"sshAuthFailedDescription": "As credenciais fornecidas estavam incorretas. Por favor, tente novamente com credenciais válidas.",
|
||||
"sshTimeoutDescription": "A tentativa de autenticação expirou. Por favor, tente novamente.",
|
||||
"sshProvideCredentialsDescription": "Por favor, forneça suas credenciais SSH para conectar a este servidor.",
|
||||
"sshPasswordDescription": "Digite a senha para esta conexão SSH.",
|
||||
"sshKeyPasswordDescription": "Se sua chave SSH estiver criptografada, digite a senha aqui."
|
||||
},
|
||||
"errors": {
|
||||
"notFound": "Página não encontrada",
|
||||
|
||||
@@ -345,6 +345,8 @@
|
||||
"settingUp": "设置中...",
|
||||
"next": "下一步",
|
||||
"previous": "上一步",
|
||||
"connect": "连接",
|
||||
"connecting": "连接中...",
|
||||
"refresh": "刷新",
|
||||
"settings": "设置",
|
||||
"profile": "个人资料",
|
||||
@@ -1280,7 +1282,17 @@
|
||||
"passwordResetSuccess": "成功!",
|
||||
"passwordResetSuccessDesc": "您的密码已成功重置!您现在可以使用新密码登录。",
|
||||
"signUp": "注册",
|
||||
"dataLossWarning": "以这种方式重置密码将删除所有已保存的 SSH 主机、凭据和其他加密数据。此操作无法撤销。仅当您忘记密码且未登录时才使用此功能。"
|
||||
"dataLossWarning": "以这种方式重置密码将删除所有已保存的 SSH 主机、凭据和其他加密数据。此操作无法撤销。仅当您忘记密码且未登录时才使用此功能。",
|
||||
"sshAuthenticationRequired": "需要 SSH 身份验证",
|
||||
"sshNoKeyboardInteractive": "键盘交互式身份验证不可用",
|
||||
"sshAuthenticationFailed": "身份验证失败",
|
||||
"sshAuthenticationTimeout": "身份验证超时",
|
||||
"sshNoKeyboardInteractiveDescription": "服务器不支持键盘交互式身份验证。请提供您的密码或 SSH 密钥。",
|
||||
"sshAuthFailedDescription": "提供的凭据不正确。请使用有效凭据重试。",
|
||||
"sshTimeoutDescription": "身份验证尝试超时。请重试。",
|
||||
"sshProvideCredentialsDescription": "请提供您的 SSH 凭据以连接到此服务器。",
|
||||
"sshPasswordDescription": "输入此 SSH 连接的密码。",
|
||||
"sshKeyPasswordDescription": "如果您的 SSH 密钥已加密,请在此处输入密码。"
|
||||
},
|
||||
"errors": {
|
||||
"notFound": "页面未找到",
|
||||
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -250,6 +250,9 @@ export const Terminal = forwardRef<TerminalHandle, SSHTerminalProps>(
|
||||
data: {
|
||||
cols: terminal.cols,
|
||||
rows: terminal.rows,
|
||||
password: credentials.password,
|
||||
sshKey: credentials.sshKey,
|
||||
keyPassword: credentials.keyPassword,
|
||||
hostConfig: {
|
||||
...hostConfig,
|
||||
password: credentials.password,
|
||||
@@ -525,26 +528,6 @@ export const Terminal = forwardRef<TerminalHandle, SSHTerminalProps>(
|
||||
} else if (msg.type === "error") {
|
||||
const errorMessage = msg.message || t("terminal.unknownError");
|
||||
|
||||
if (
|
||||
errorMessage.toLowerCase().includes("auth") ||
|
||||
errorMessage.toLowerCase().includes("password") ||
|
||||
errorMessage.toLowerCase().includes("permission") ||
|
||||
errorMessage.toLowerCase().includes("denied") ||
|
||||
errorMessage.toLowerCase().includes("invalid") ||
|
||||
errorMessage.toLowerCase().includes("failed") ||
|
||||
errorMessage.toLowerCase().includes("incorrect")
|
||||
) {
|
||||
toast.error(t("terminal.authError", { message: errorMessage }));
|
||||
shouldNotReconnectRef.current = true;
|
||||
if (webSocketRef.current) {
|
||||
webSocketRef.current.close();
|
||||
}
|
||||
if (onClose) {
|
||||
onClose();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
errorMessage.toLowerCase().includes("connection") ||
|
||||
errorMessage.toLowerCase().includes("timeout") ||
|
||||
@@ -563,6 +546,26 @@ export const Terminal = forwardRef<TerminalHandle, SSHTerminalProps>(
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
(errorMessage.toLowerCase().includes("auth") &&
|
||||
errorMessage.toLowerCase().includes("failed")) ||
|
||||
errorMessage.toLowerCase().includes("permission denied") ||
|
||||
(errorMessage.toLowerCase().includes("invalid") &&
|
||||
(errorMessage.toLowerCase().includes("password") ||
|
||||
errorMessage.toLowerCase().includes("key"))) ||
|
||||
errorMessage.toLowerCase().includes("incorrect password")
|
||||
) {
|
||||
toast.error(t("terminal.authError", { message: errorMessage }));
|
||||
shouldNotReconnectRef.current = true;
|
||||
if (webSocketRef.current) {
|
||||
webSocketRef.current.close();
|
||||
}
|
||||
if (onClose) {
|
||||
onClose();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
toast.error(t("terminal.error", { message: errorMessage }));
|
||||
} else if (msg.type === "connected") {
|
||||
setIsConnected(true);
|
||||
|
||||
+1
-1
@@ -25,7 +25,7 @@ interface SshToolsSidebarProps {
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
export function SshToolsSidebar({
|
||||
export function SSHToolsSidebar({
|
||||
isOpen,
|
||||
onClose,
|
||||
}: SshToolsSidebarProps): React.ReactElement | null {
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { useEffect, useRef, useState, useMemo } from "react";
|
||||
import { Terminal } from "@/ui/desktop/apps/terminal/Terminal.tsx";
|
||||
import { Server as ServerView } from "@/ui/desktop/apps/server/Server.tsx";
|
||||
import { FileManager } from "@/ui/desktop/apps/file manager/FileManager.tsx";
|
||||
import { FileManager } from "@/ui/desktop/apps/file-manager/FileManager.tsx";
|
||||
import { useTabs } from "@/ui/desktop/navigation/tabs/TabContext.tsx";
|
||||
import {
|
||||
ResizablePanelGroup,
|
||||
|
||||
@@ -137,7 +137,7 @@ export function SSHAuthDialog({
|
||||
|
||||
return (
|
||||
<div
|
||||
className="fixed inset-0 z-50 flex items-center justify-center bg-black/50 backdrop-blur-sm"
|
||||
className="absolute inset-0 z-50 flex items-center justify-center bg-black/50 backdrop-blur-sm"
|
||||
style={{ backgroundColor: `${backgroundColor}dd` }}
|
||||
>
|
||||
<Card className="w-full max-w-2xl mx-4 shadow-2xl">
|
||||
|
||||
@@ -8,7 +8,7 @@ import { useTabs } from "@/ui/desktop/navigation/tabs/TabContext.tsx";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { TabDropdown } from "@/ui/desktop/navigation/tabs/TabDropdown.tsx";
|
||||
import { SnippetsSidebar } from "@/ui/desktop/apps/terminal/SnippetsSidebar.tsx";
|
||||
import { SshToolsSidebar } from "@/ui/desktop/apps/tools/SshToolsSidebar.tsx";
|
||||
import { SSHToolsSidebar } from "@/ui/desktop/apps/tools/SSHToolsSidebar.tsx";
|
||||
import { ToolsMenu } from "@/ui/desktop/apps/tools/ToolsMenu.tsx";
|
||||
|
||||
interface TabData {
|
||||
@@ -497,7 +497,7 @@ export function TopNavbar({
|
||||
</div>
|
||||
)}
|
||||
|
||||
<SshToolsSidebar
|
||||
<SSHToolsSidebar
|
||||
isOpen={toolsSheetOpen}
|
||||
onClose={() => setToolsSheetOpen(false)}
|
||||
/>
|
||||
|
||||
Reference in new issue
Block a user