Feature Sudo password auto-fill;
This commit is contained in:
@@ -705,6 +705,7 @@ export const DEFAULT_TERMINAL_CONFIG = {
|
|||||||
startupSnippetId: null as number | null,
|
startupSnippetId: null as number | null,
|
||||||
autoMosh: false,
|
autoMosh: false,
|
||||||
moshCommand: "mosh-server new -s -l LANG=en_US.UTF-8",
|
moshCommand: "mosh-server new -s -l LANG=en_US.UTF-8",
|
||||||
|
sudoPasswordAutoFill: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TerminalConfigType = typeof DEFAULT_TERMINAL_CONFIG;
|
export type TerminalConfigType = typeof DEFAULT_TERMINAL_CONFIG;
|
||||||
|
|||||||
@@ -799,7 +799,9 @@
|
|||||||
"searchServers": "Server durchsuchen...",
|
"searchServers": "Server durchsuchen...",
|
||||||
"noServerFound": "Kein Server gefunden",
|
"noServerFound": "Kein Server gefunden",
|
||||||
"jumpHostsOrder": "Verbindungen werden in dieser Reihenfolge hergestellt: Jump-Host 1 → Jump-Host 2 → ... → Ziel-Server",
|
"jumpHostsOrder": "Verbindungen werden in dieser Reihenfolge hergestellt: Jump-Host 1 → Jump-Host 2 → ... → Ziel-Server",
|
||||||
"advancedAuthSettings": "Erweiterte Authentifizierungseinstellungen"
|
"advancedAuthSettings": "Erweiterte Authentifizierungseinstellungen",
|
||||||
|
"sudoPasswordAutoFill": "Sudo-Passwort automatisch ausfüllen",
|
||||||
|
"sudoPasswordAutoFillDesc": "Popup anzeigen, um das Passwort bei sudo-Befehlen automatisch einzufügen"
|
||||||
},
|
},
|
||||||
"terminal": {
|
"terminal": {
|
||||||
"title": "Terminal",
|
"title": "Terminal",
|
||||||
@@ -833,7 +835,11 @@
|
|||||||
"connectionTimeout": "Zeitüberschreitung der Verbindung",
|
"connectionTimeout": "Zeitüberschreitung der Verbindung",
|
||||||
"terminalTitle": "Terminal - {{host}}",
|
"terminalTitle": "Terminal - {{host}}",
|
||||||
"terminalWithPath": "Terminal - {{host}} : {{path}}",
|
"terminalWithPath": "Terminal - {{host}} : {{path}}",
|
||||||
"runTitle": "Ausführen von {{command}} – {{host}}"
|
"runTitle": "Ausführen von {{command}} – {{host}}",
|
||||||
|
"sudoPasswordPopupTitle": "Passwort einfügen?",
|
||||||
|
"sudoPasswordPopupHint": "Drücken Sie Enter zum Einfügen, Esc zum Abbrechen",
|
||||||
|
"sudoPasswordPopupConfirm": "Einfügen",
|
||||||
|
"sudoPasswordPopupDismiss": "Abbrechen"
|
||||||
},
|
},
|
||||||
"fileManager": {
|
"fileManager": {
|
||||||
"title": "Dateimanager",
|
"title": "Dateimanager",
|
||||||
|
|||||||
@@ -908,7 +908,9 @@
|
|||||||
"quickActionName": "Action name",
|
"quickActionName": "Action name",
|
||||||
"noSnippetFound": "No snippet found",
|
"noSnippetFound": "No snippet found",
|
||||||
"quickActionsOrder": "Quick action buttons will appear in the order listed above on the Server Stats page",
|
"quickActionsOrder": "Quick action buttons will appear in the order listed above on the Server Stats page",
|
||||||
"advancedAuthSettings": "Advanced Authentication Settings"
|
"advancedAuthSettings": "Advanced Authentication Settings",
|
||||||
|
"sudoPasswordAutoFill": "Sudo Password Auto-Fill",
|
||||||
|
"sudoPasswordAutoFillDesc": "Automatically offer to insert SSH password when sudo prompts for password"
|
||||||
},
|
},
|
||||||
"terminal": {
|
"terminal": {
|
||||||
"title": "Terminal",
|
"title": "Terminal",
|
||||||
@@ -946,7 +948,11 @@
|
|||||||
"totpRequired": "Two-Factor Authentication Required",
|
"totpRequired": "Two-Factor Authentication Required",
|
||||||
"totpCodeLabel": "Verification Code",
|
"totpCodeLabel": "Verification Code",
|
||||||
"totpPlaceholder": "000000",
|
"totpPlaceholder": "000000",
|
||||||
"totpVerify": "Verify"
|
"totpVerify": "Verify",
|
||||||
|
"sudoPasswordPopupTitle": "Insert Password?",
|
||||||
|
"sudoPasswordPopupHint": "Press Enter to insert, Esc to dismiss",
|
||||||
|
"sudoPasswordPopupConfirm": "Insert",
|
||||||
|
"sudoPasswordPopupDismiss": "Dismiss"
|
||||||
},
|
},
|
||||||
"fileManager": {
|
"fileManager": {
|
||||||
"title": "File Manager",
|
"title": "File Manager",
|
||||||
|
|||||||
@@ -790,7 +790,9 @@
|
|||||||
"searchServers": "Rechercher des serveurs...",
|
"searchServers": "Rechercher des serveurs...",
|
||||||
"noServerFound": "Aucun serveur trouvé",
|
"noServerFound": "Aucun serveur trouvé",
|
||||||
"jumpHostsOrder": "Les connexions seront établies dans l'ordre : Serveur de rebond 1 → Serveur de rebond 2 → ... → Serveur cible",
|
"jumpHostsOrder": "Les connexions seront établies dans l'ordre : Serveur de rebond 1 → Serveur de rebond 2 → ... → Serveur cible",
|
||||||
"advancedAuthSettings": "Paramètres d'authentification avancés"
|
"advancedAuthSettings": "Paramètres d'authentification avancés",
|
||||||
|
"sudoPasswordAutoFill": "Remplissage automatique du mot de passe sudo",
|
||||||
|
"sudoPasswordAutoFillDesc": "Proposer automatiquement d’insérer le mot de passe SSH lorsque sudo demande un mot de passe"
|
||||||
},
|
},
|
||||||
"terminal": {
|
"terminal": {
|
||||||
"title": "Terminal",
|
"title": "Terminal",
|
||||||
@@ -828,7 +830,11 @@
|
|||||||
"totpRequired": "Authentification à deux facteurs requise",
|
"totpRequired": "Authentification à deux facteurs requise",
|
||||||
"totpCodeLabel": "Code de vérification",
|
"totpCodeLabel": "Code de vérification",
|
||||||
"totpPlaceholder": "000000",
|
"totpPlaceholder": "000000",
|
||||||
"totpVerify": "Vérifier"
|
"totpVerify": "Vérifier",
|
||||||
|
"sudoPasswordPopupTitle": "Insérer le mot de passe ?",
|
||||||
|
"sudoPasswordPopupHint": "Appuyez sur Entrée pour insérer, Échap pour annuler",
|
||||||
|
"sudoPasswordPopupConfirm": "Insérer",
|
||||||
|
"sudoPasswordPopupDismiss": "Annuler"
|
||||||
},
|
},
|
||||||
"fileManager": {
|
"fileManager": {
|
||||||
"title": "Gestionnaire de fichiers",
|
"title": "Gestionnaire de fichiers",
|
||||||
|
|||||||
@@ -745,7 +745,9 @@
|
|||||||
"searchServers": "Pesquisar servidores...",
|
"searchServers": "Pesquisar servidores...",
|
||||||
"noServerFound": "Nenhum servidor encontrado",
|
"noServerFound": "Nenhum servidor encontrado",
|
||||||
"jumpHostsOrder": "As conexões serão feitas na ordem: Host de Salto 1 → Host de Salto 2 → ... → Servidor de Destino",
|
"jumpHostsOrder": "As conexões serão feitas na ordem: Host de Salto 1 → Host de Salto 2 → ... → Servidor de Destino",
|
||||||
"advancedAuthSettings": "Configurações Avançadas de Autenticação"
|
"advancedAuthSettings": "Configurações Avançadas de Autenticação",
|
||||||
|
"sudoPasswordAutoFill": "Preenchimento automático da senha do sudo",
|
||||||
|
"sudoPasswordAutoFillDesc": "Oferecer automaticamente inserir a senha SSH quando o sudo solicitar uma senha"
|
||||||
},
|
},
|
||||||
"terminal": {
|
"terminal": {
|
||||||
"title": "Terminal",
|
"title": "Terminal",
|
||||||
@@ -779,7 +781,11 @@
|
|||||||
"connectionTimeout": "Tempo limite de conexão esgotado",
|
"connectionTimeout": "Tempo limite de conexão esgotado",
|
||||||
"terminalTitle": "Terminal - {{host}}",
|
"terminalTitle": "Terminal - {{host}}",
|
||||||
"terminalWithPath": "Terminal - {{host}}:{{path}}",
|
"terminalWithPath": "Terminal - {{host}}:{{path}}",
|
||||||
"runTitle": "Executando {{command}} - {{host}}"
|
"runTitle": "Executando {{command}} - {{host}}",
|
||||||
|
"sudoPasswordPopupTitle": "Inserir Senha?",
|
||||||
|
"sudoPasswordPopupHint": "Pressione Enter para inserir, Esc para cancelar",
|
||||||
|
"sudoPasswordPopupConfirm": "Inserir",
|
||||||
|
"sudoPasswordPopupDismiss": "Cancelar"
|
||||||
},
|
},
|
||||||
"fileManager": {
|
"fileManager": {
|
||||||
"title": "Gerenciador de Arquivos",
|
"title": "Gerenciador de Arquivos",
|
||||||
@@ -1034,7 +1040,6 @@
|
|||||||
"fileSavedSuccessfully": "Arquivo salvo com sucesso",
|
"fileSavedSuccessfully": "Arquivo salvo com sucesso",
|
||||||
"autoSaveFailed": "Falha no salvamento automático",
|
"autoSaveFailed": "Falha no salvamento automático",
|
||||||
"fileAutoSaved": "Arquivo salvo automaticamente",
|
"fileAutoSaved": "Arquivo salvo automaticamente",
|
||||||
|
|
||||||
"moveFileFailed": "Falha ao mover {{name}}",
|
"moveFileFailed": "Falha ao mover {{name}}",
|
||||||
"moveOperationFailed": "Falha na operação de mover",
|
"moveOperationFailed": "Falha na operação de mover",
|
||||||
"canOnlyCompareFiles": "Só é possível comparar dois arquivos",
|
"canOnlyCompareFiles": "Só é possível comparar dois arquivos",
|
||||||
|
|||||||
@@ -858,7 +858,9 @@
|
|||||||
"searchServers": "Поиск серверов...",
|
"searchServers": "Поиск серверов...",
|
||||||
"noServerFound": "Сервер не найден",
|
"noServerFound": "Сервер не найден",
|
||||||
"jumpHostsOrder": "Подключения будут выполнены в порядке: Промежуточный хост 1 → Промежуточный хост 2 → ... → Целевой сервер",
|
"jumpHostsOrder": "Подключения будут выполнены в порядке: Промежуточный хост 1 → Промежуточный хост 2 → ... → Целевой сервер",
|
||||||
"advancedAuthSettings": "Расширенные настройки аутентификации"
|
"advancedAuthSettings": "Расширенные настройки аутентификации",
|
||||||
|
"sudoPasswordAutoFill": "Автозаполнение пароля sudo",
|
||||||
|
"sudoPasswordAutoFillDesc": "Показывать всплывающее окно для автоматического ввода пароля при выполнении команд sudo",
|
||||||
},
|
},
|
||||||
"terminal": {
|
"terminal": {
|
||||||
"title": "Терминал",
|
"title": "Терминал",
|
||||||
@@ -896,7 +898,11 @@
|
|||||||
"totpRequired": "Требуется двухфакторная аутентификация",
|
"totpRequired": "Требуется двухфакторная аутентификация",
|
||||||
"totpCodeLabel": "Код проверки",
|
"totpCodeLabel": "Код проверки",
|
||||||
"totpPlaceholder": "000000",
|
"totpPlaceholder": "000000",
|
||||||
"totpVerify": "Проверить"
|
"totpVerify": "Проверить",
|
||||||
|
"sudoPasswordPopupTitle": "Вставить пароль?",
|
||||||
|
"sudoPasswordPopupHint": "Нажмите Enter для вставки, Esc для отмены",
|
||||||
|
"sudoPasswordPopupConfirm": "Вставить",
|
||||||
|
"sudoPasswordPopupDismiss": "Отмена"
|
||||||
},
|
},
|
||||||
"fileManager": {
|
"fileManager": {
|
||||||
"title": "Файловый менеджер",
|
"title": "Файловый менеджер",
|
||||||
|
|||||||
@@ -893,7 +893,9 @@
|
|||||||
"searchServers": "搜索服务器...",
|
"searchServers": "搜索服务器...",
|
||||||
"noServerFound": "未找到服务器",
|
"noServerFound": "未找到服务器",
|
||||||
"jumpHostsOrder": "连接将按顺序进行:跳板主机 1 → 跳板主机 2 → ... → 目标服务器",
|
"jumpHostsOrder": "连接将按顺序进行:跳板主机 1 → 跳板主机 2 → ... → 目标服务器",
|
||||||
"advancedAuthSettings": "高级身份验证设置"
|
"advancedAuthSettings": "高级身份验证设置",
|
||||||
|
"sudoPasswordAutoFill": "Sudo 密码自动填充",
|
||||||
|
"sudoPasswordAutoFillDesc": "在 sudo 命令时显示弹窗以自动输入密码"
|
||||||
},
|
},
|
||||||
"terminal": {
|
"terminal": {
|
||||||
"title": "终端",
|
"title": "终端",
|
||||||
@@ -931,7 +933,11 @@
|
|||||||
"reconnecting": "重新连接中... ({{attempt}}/{{max}})",
|
"reconnecting": "重新连接中... ({{attempt}}/{{max}})",
|
||||||
"reconnected": "重新连接成功",
|
"reconnected": "重新连接成功",
|
||||||
"maxReconnectAttemptsReached": "已达到最大重连尝试次数",
|
"maxReconnectAttemptsReached": "已达到最大重连尝试次数",
|
||||||
"connectionTimeout": "连接超时"
|
"connectionTimeout": "连接超时",
|
||||||
|
"sudoPasswordPopupTitle": "插入密码?",
|
||||||
|
"sudoPasswordPopupHint": "按 Enter 插入,Esc 取消",
|
||||||
|
"sudoPasswordPopupConfirm": "插入",
|
||||||
|
"sudoPasswordPopupDismiss": "取消"
|
||||||
},
|
},
|
||||||
"fileManager": {
|
"fileManager": {
|
||||||
"title": "文件管理器",
|
"title": "文件管理器",
|
||||||
|
|||||||
@@ -307,6 +307,7 @@ export interface TerminalConfig {
|
|||||||
startupSnippetId: number | null;
|
startupSnippetId: number | null;
|
||||||
autoMosh: boolean;
|
autoMosh: boolean;
|
||||||
moshCommand: string;
|
moshCommand: string;
|
||||||
|
sudoPasswordAutoFill: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|||||||
@@ -547,6 +547,7 @@ export function HostManagerEditor({
|
|||||||
startupSnippetId: z.number().nullable(),
|
startupSnippetId: z.number().nullable(),
|
||||||
autoMosh: z.boolean(),
|
autoMosh: z.boolean(),
|
||||||
moshCommand: z.string(),
|
moshCommand: z.string(),
|
||||||
|
sudoPasswordAutoFill: z.boolean(),
|
||||||
})
|
})
|
||||||
.optional(),
|
.optional(),
|
||||||
forceKeyboardInteractive: z.boolean().optional(),
|
forceKeyboardInteractive: z.boolean().optional(),
|
||||||
@@ -631,7 +632,7 @@ export function HostManagerEditor({
|
|||||||
type FormData = z.infer<typeof formSchema>;
|
type FormData = z.infer<typeof formSchema>;
|
||||||
|
|
||||||
const form = useForm<FormData>({
|
const form = useForm<FormData>({
|
||||||
resolver: zodResolver(formSchema),
|
resolver: zodResolver(formSchema) as any,
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
name: "",
|
name: "",
|
||||||
ip: "",
|
ip: "",
|
||||||
@@ -2443,6 +2444,29 @@ export function HostManagerEditor({
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
<FormField
|
||||||
|
control={form.control}
|
||||||
|
name="terminalConfig.sudoPasswordAutoFill"
|
||||||
|
render={({ field }) => (
|
||||||
|
<FormItem className="flex flex-row items-center justify-between rounded-lg border p-3">
|
||||||
|
<div className="space-y-0.5">
|
||||||
|
<FormLabel>
|
||||||
|
{t("hosts.sudoPasswordAutoFill")}
|
||||||
|
</FormLabel>
|
||||||
|
<FormDescription>
|
||||||
|
{t("hosts.sudoPasswordAutoFillDesc")}
|
||||||
|
</FormDescription>
|
||||||
|
</div>
|
||||||
|
<FormControl>
|
||||||
|
<Switch
|
||||||
|
checked={field.value}
|
||||||
|
onCheckedChange={field.onChange}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
</FormItem>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<label className="text-sm font-medium">
|
<label className="text-sm font-medium">
|
||||||
Environment Variables
|
Environment Variables
|
||||||
|
|||||||
82
src/ui/desktop/apps/terminal/SudoPasswordPopup.tsx
Normal file
82
src/ui/desktop/apps/terminal/SudoPasswordPopup.tsx
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
import { useEffect } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { KeyRound } from "lucide-react";
|
||||||
|
import { Button } from "@/components/ui/button.tsx";
|
||||||
|
|
||||||
|
interface SudoPasswordPopupProps {
|
||||||
|
isOpen: boolean;
|
||||||
|
hostPassword: string;
|
||||||
|
backgroundColor: string;
|
||||||
|
onConfirm: (password: string) => void;
|
||||||
|
onDismiss: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SudoPasswordPopup({
|
||||||
|
isOpen,
|
||||||
|
hostPassword,
|
||||||
|
backgroundColor,
|
||||||
|
onConfirm,
|
||||||
|
onDismiss
|
||||||
|
}: SudoPasswordPopupProps) {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isOpen) return;
|
||||||
|
|
||||||
|
const handleKeyDown = (e: KeyboardEvent) => {
|
||||||
|
if (e.key === "Enter") {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
e.stopImmediatePropagation();
|
||||||
|
onConfirm(hostPassword);
|
||||||
|
} else if (e.key === "Escape") {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
e.stopImmediatePropagation();
|
||||||
|
onDismiss();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener("keydown", handleKeyDown, true);
|
||||||
|
return () => window.removeEventListener("keydown", handleKeyDown, true);
|
||||||
|
}, [isOpen, onConfirm, onDismiss, hostPassword]);
|
||||||
|
|
||||||
|
if (!isOpen) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="absolute bottom-4 right-4 z-50 backdrop-blur-sm border border-border rounded-lg shadow-lg p-4 min-w-[280px]"
|
||||||
|
style={{ backgroundColor: backgroundColor }}
|
||||||
|
>
|
||||||
|
<div className="flex items-center gap-3 mb-3">
|
||||||
|
<div className="p-2 bg-primary/10 rounded-full">
|
||||||
|
<KeyRound className="h-5 w-5 text-primary" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h4 className="font-medium text-sm">
|
||||||
|
{t("terminal.sudoPasswordPopupTitle", "Insert password?")}
|
||||||
|
</h4>
|
||||||
|
<p className="text-xs text-muted-foreground">
|
||||||
|
{t("terminal.sudoPasswordPopupHint", "Press Enter to insert, Esc to dismiss")}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex gap-2 justify-end">
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="sm"
|
||||||
|
onClick={onDismiss}
|
||||||
|
>
|
||||||
|
{t("terminal.sudoPasswordPopupDismiss", "Dismiss")}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="default"
|
||||||
|
size="sm"
|
||||||
|
onClick={() => onConfirm(hostPassword)}
|
||||||
|
>
|
||||||
|
{t("terminal.sudoPasswordPopupConfirm", "Insert")}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -32,6 +32,7 @@ import { useCommandHistory as useCommandHistoryHook } from "@/ui/hooks/useComman
|
|||||||
import { useCommandHistory } from "@/ui/desktop/apps/terminal/command-history/CommandHistoryContext.tsx";
|
import { useCommandHistory } from "@/ui/desktop/apps/terminal/command-history/CommandHistoryContext.tsx";
|
||||||
import { CommandAutocomplete } from "./command-history/CommandAutocomplete.tsx";
|
import { CommandAutocomplete } from "./command-history/CommandAutocomplete.tsx";
|
||||||
import { SimpleLoader } from "@/ui/desktop/navigation/animations/SimpleLoader.tsx";
|
import { SimpleLoader } from "@/ui/desktop/navigation/animations/SimpleLoader.tsx";
|
||||||
|
import { SudoPasswordPopup } from "./SudoPasswordPopup.tsx";
|
||||||
|
|
||||||
interface HostConfig {
|
interface HostConfig {
|
||||||
id?: number;
|
id?: number;
|
||||||
@@ -173,6 +174,7 @@ export const Terminal = forwardRef<TerminalHandle, SSHTerminalProps>(
|
|||||||
const [showHistoryDialog, setShowHistoryDialog] = useState(false);
|
const [showHistoryDialog, setShowHistoryDialog] = useState(false);
|
||||||
const [commandHistory, setCommandHistory] = useState<string[]>([]);
|
const [commandHistory, setCommandHistory] = useState<string[]>([]);
|
||||||
const [isLoadingHistory, setIsLoadingHistory] = useState(false);
|
const [isLoadingHistory, setIsLoadingHistory] = useState(false);
|
||||||
|
const [showSudoPasswordPopup, setShowSudoPasswordPopup] = useState(false);
|
||||||
|
|
||||||
const setIsLoadingRef = useRef(commandHistoryContext.setIsLoading);
|
const setIsLoadingRef = useRef(commandHistoryContext.setIsLoading);
|
||||||
const setCommandHistoryContextRef = useRef(
|
const setCommandHistoryContextRef = useRef(
|
||||||
@@ -660,6 +662,11 @@ export const Terminal = forwardRef<TerminalHandle, SSHTerminalProps>(
|
|||||||
if (msg.type === "data") {
|
if (msg.type === "data") {
|
||||||
if (typeof msg.data === "string") {
|
if (typeof msg.data === "string") {
|
||||||
terminal.write(msg.data);
|
terminal.write(msg.data);
|
||||||
|
// Sudo password prompt detection
|
||||||
|
const sudoPasswordPattern = /(?:\[sudo\] password for \S+:|sudo: a password is required)/;
|
||||||
|
if (config.sudoPasswordAutoFill && sudoPasswordPattern.test(msg.data)) {
|
||||||
|
setShowSudoPasswordPopup(true);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
terminal.write(String(msg.data));
|
terminal.write(String(msg.data));
|
||||||
}
|
}
|
||||||
@@ -1500,6 +1507,19 @@ export const Terminal = forwardRef<TerminalHandle, SSHTerminalProps>(
|
|||||||
onSelect={handleAutocompleteSelect}
|
onSelect={handleAutocompleteSelect}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<SudoPasswordPopup
|
||||||
|
isOpen={showSudoPasswordPopup}
|
||||||
|
hostPassword={hostConfig.password || ""}
|
||||||
|
backgroundColor={backgroundColor}
|
||||||
|
onConfirm={(password) => {
|
||||||
|
setShowSudoPasswordPopup(false);
|
||||||
|
if (webSocketRef.current && webSocketRef.current.readyState === WebSocket.OPEN) {
|
||||||
|
webSocketRef.current.send(JSON.stringify({ type: "input", data: password + "\n" }));
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onDismiss={() => setShowSudoPasswordPopup(false)}
|
||||||
|
/>
|
||||||
|
|
||||||
<SimpleLoader
|
<SimpleLoader
|
||||||
visible={isConnecting}
|
visible={isConnecting}
|
||||||
message={t("terminal.connecting")}
|
message={t("terminal.connecting")}
|
||||||
|
|||||||
Reference in New Issue
Block a user