Improved and fixed editing and adding host UI
This commit is contained in:
84
src/App.jsx
84
src/App.jsx
@@ -222,13 +222,12 @@ function App() {
|
||||
}, []);
|
||||
|
||||
const handleAddHost = () => {
|
||||
if (addHostForm.ip && addHostForm.user && addHostForm.port) {
|
||||
if (!addHostForm.rememberHost) {
|
||||
connectToHost();
|
||||
setIsAddHostHidden(true);
|
||||
return;
|
||||
}
|
||||
if (!addHostForm.ip?.trim() || !addHostForm.user?.trim() || !addHostForm.port) {
|
||||
alert("Please fill out all required fields (IP, User, Port).");
|
||||
return;
|
||||
}
|
||||
|
||||
if (addHostForm.rememberHost) {
|
||||
if (addHostForm.authMethod === 'Select Auth') {
|
||||
alert("Please select an authentication method.");
|
||||
return;
|
||||
@@ -237,33 +236,42 @@ function App() {
|
||||
setIsNoAuthHidden(false);
|
||||
return;
|
||||
}
|
||||
if (addHostForm.authMethod === 'rsaKey' && !addHostForm.rsaKey) {
|
||||
if (addHostForm.authMethod === 'key' && !addHostForm.privateKey) {
|
||||
setIsNoAuthHidden(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
connectToHost();
|
||||
connectToHost();
|
||||
if (addHostForm.rememberHost) {
|
||||
if (!addHostForm.storePassword) {
|
||||
addHostForm.password = '';
|
||||
addHostForm.privateKey = '';
|
||||
}
|
||||
handleSaveHost();
|
||||
setIsAddHostHidden(true);
|
||||
} else {
|
||||
alert("Please fill out all required fields (IP, User, Port).");
|
||||
}
|
||||
setIsAddHostHidden(true);
|
||||
};
|
||||
|
||||
const connectToHost = () => {
|
||||
const hostConfig = {
|
||||
name: addHostForm.name || '',
|
||||
folder: addHostForm.folder || '',
|
||||
ip: addHostForm.ip,
|
||||
user: addHostForm.user,
|
||||
ip: addHostForm.ip.trim(),
|
||||
user: addHostForm.user.trim(),
|
||||
port: String(addHostForm.port),
|
||||
password: addHostForm.rememberHost && addHostForm.authMethod === 'password' ? addHostForm.password : undefined,
|
||||
rsaKey: addHostForm.rememberHost && addHostForm.authMethod === 'rsaKey' ? addHostForm.rsaKey : undefined,
|
||||
};
|
||||
|
||||
if (addHostForm.rememberHost && addHostForm.storePassword) {
|
||||
if (addHostForm.authMethod === 'password') {
|
||||
hostConfig.password = addHostForm.password;
|
||||
} else if (addHostForm.authMethod === 'key') {
|
||||
hostConfig.privateKey = addHostForm.privateKey;
|
||||
hostConfig.keyType = addHostForm.keyType;
|
||||
hostConfig.passphrase = addHostForm.passphrase;
|
||||
}
|
||||
}
|
||||
|
||||
const newTerminal = {
|
||||
id: nextId,
|
||||
title: hostConfig.name || hostConfig.ip,
|
||||
@@ -273,9 +281,21 @@ function App() {
|
||||
setTerminals([...terminals, newTerminal]);
|
||||
setActiveTab(nextId);
|
||||
setNextId(nextId + 1);
|
||||
setIsAddHostHidden(true);
|
||||
setAddHostForm({ name: "", folder: "", ip: "", user: "", password: "", rsaKey: "", port: 22, authMethod: "Select Auth", rememberHost: false, storePassword: true });
|
||||
}
|
||||
setAddHostForm({
|
||||
name: "",
|
||||
folder: "",
|
||||
ip: "",
|
||||
user: "",
|
||||
password: "",
|
||||
privateKey: "",
|
||||
keyType: "",
|
||||
passphrase: "",
|
||||
port: 22,
|
||||
authMethod: "Select Auth",
|
||||
rememberHost: true,
|
||||
storePassword: true
|
||||
});
|
||||
};
|
||||
|
||||
const handleAuthSubmit = (form) => {
|
||||
const updatedTerminals = terminals.map((terminal) => {
|
||||
@@ -327,21 +347,30 @@ function App() {
|
||||
}
|
||||
|
||||
const handleSaveHost = () => {
|
||||
let hostConfig = {
|
||||
const hostConfig = {
|
||||
name: addHostForm.name || addHostForm.ip,
|
||||
folder: addHostForm.folder,
|
||||
ip: addHostForm.ip,
|
||||
user: addHostForm.user,
|
||||
password: addHostForm.authMethod === 'password' ? addHostForm.password : undefined,
|
||||
rsaKey: addHostForm.authMethod === 'rsaKey' ? addHostForm.rsaKey : undefined,
|
||||
ip: addHostForm.ip.trim(),
|
||||
user: addHostForm.user.trim(),
|
||||
port: String(addHostForm.port),
|
||||
};
|
||||
|
||||
if (addHostForm.storePassword) {
|
||||
if (addHostForm.authMethod === 'password') {
|
||||
hostConfig.password = addHostForm.password;
|
||||
} else if (addHostForm.authMethod === 'key') {
|
||||
hostConfig.privateKey = addHostForm.privateKey;
|
||||
hostConfig.keyType = addHostForm.keyType;
|
||||
hostConfig.passphrase = addHostForm.passphrase;
|
||||
}
|
||||
}
|
||||
|
||||
if (userRef.current) {
|
||||
userRef.current.saveHost({
|
||||
hostConfig,
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleLoginUser = ({ username, password, sessionToken, onSuccess, onFailure }) => {
|
||||
if (userRef.current) {
|
||||
@@ -472,7 +501,6 @@ function App() {
|
||||
|
||||
updateEditHostForm(oldConfig);
|
||||
} catch (error) {
|
||||
console.error('Edit failed:', error);
|
||||
setErrorMessage(`Edit failed: ${error}`);
|
||||
setIsErrorHidden(false);
|
||||
setIsEditing(false);
|
||||
@@ -658,10 +686,10 @@ function App() {
|
||||
)}
|
||||
<NoAuthenticationModal
|
||||
isHidden={isNoAuthHidden}
|
||||
setIsHidden={setIsNoAuthHidden}
|
||||
form={authForm}
|
||||
setForm={setAuthForm}
|
||||
setIsNoAuthHidden={setIsNoAuthHidden}
|
||||
handleAuthSubmit={handleAuthSubmit}
|
||||
onAuthenticate={handleAuthSubmit}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -681,7 +709,7 @@ function App() {
|
||||
setForm={setEditHostForm}
|
||||
handleEditHost={handleEditHost}
|
||||
setIsEditHostHidden={setIsEditHostHidden}
|
||||
hostConfig={currentHostConfig}
|
||||
hostConfig={currentHostConfig || {}}
|
||||
/>
|
||||
<ProfileModal
|
||||
isHidden={isProfileHidden}
|
||||
|
||||
@@ -222,7 +222,6 @@ export const User = forwardRef(({ onLoginSuccess, onCreateSuccess, onDeleteSucce
|
||||
if (!currentUser.current) return onFailure("Not authenticated");
|
||||
|
||||
try {
|
||||
console.log('Editing host with configs:', { oldHostConfig, newHostConfig });
|
||||
const response = await new Promise((resolve) => {
|
||||
socketRef.current.emit("editHost", {
|
||||
userId: currentUser.current.id,
|
||||
|
||||
@@ -24,20 +24,7 @@ import { useState } from 'react';
|
||||
import Visibility from '@mui/icons-material/Visibility';
|
||||
import VisibilityOff from '@mui/icons-material/VisibilityOff';
|
||||
|
||||
const AddHostModal = ({ isHidden, setIsAddHostHidden, handleAddHost }) => {
|
||||
const [form, setForm] = useState({
|
||||
name: '',
|
||||
folder: '',
|
||||
ip: '',
|
||||
user: '',
|
||||
port: 22,
|
||||
password: '',
|
||||
privateKey: '',
|
||||
keyType: '',
|
||||
passphrase: '',
|
||||
authMethod: 'Select Auth',
|
||||
rememberHost: true
|
||||
});
|
||||
const AddHostModal = ({ isHidden, form, setForm, handleAddHost, setIsAddHostHidden }) => {
|
||||
const [showPassword, setShowPassword] = useState(false);
|
||||
const [showPassphrase, setShowPassphrase] = useState(false);
|
||||
const [activeTab, setActiveTab] = useState(0);
|
||||
@@ -100,14 +87,23 @@ const AddHostModal = ({ isHidden, setIsAddHostHidden, handleAddHost }) => {
|
||||
};
|
||||
|
||||
const isFormValid = () => {
|
||||
if (!form.ip || !form.user || !form.port) return false;
|
||||
const portNum = Number(form.port);
|
||||
const { ip, user, port, authMethod, password, privateKey } = form;
|
||||
|
||||
// Basic validation for required fields
|
||||
if (!ip?.trim() || !user?.trim() || !port) return false;
|
||||
|
||||
// Port validation
|
||||
const portNum = Number(port);
|
||||
if (isNaN(portNum) || portNum < 1 || portNum > 65535) return false;
|
||||
|
||||
// If not remembering host, only basic fields are required
|
||||
if (!form.rememberHost) return true;
|
||||
|
||||
// Auth method validation only if remembering host
|
||||
if (form.rememberHost) {
|
||||
if (form.authMethod === 'Select Auth') return false;
|
||||
if (form.authMethod === 'key' && !form.privateKey) return false;
|
||||
if (form.authMethod === 'password' && !form.password) return false;
|
||||
if (authMethod === 'Select Auth') return false;
|
||||
if (authMethod === 'password' && !password?.trim()) return false;
|
||||
if (authMethod === 'key' && !privateKey?.trim()) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -115,28 +111,11 @@ const AddHostModal = ({ isHidden, setIsAddHostHidden, handleAddHost }) => {
|
||||
|
||||
const handleSubmit = (event) => {
|
||||
event.preventDefault();
|
||||
if (isFormValid()) {
|
||||
if (!form.rememberHost) {
|
||||
handleAddHost();
|
||||
} else {
|
||||
handleAddHost();
|
||||
}
|
||||
|
||||
setForm({
|
||||
name: '',
|
||||
folder: '',
|
||||
ip: '',
|
||||
user: '',
|
||||
password: '',
|
||||
privateKey: '',
|
||||
keyType: '',
|
||||
port: 22,
|
||||
authMethod: 'Select Auth',
|
||||
rememberHost: false,
|
||||
storePassword: true,
|
||||
});
|
||||
setIsAddHostHidden(true);
|
||||
if (!form.ip?.trim() || !form.user?.trim() || !form.port) {
|
||||
alert("Please fill out all required fields (IP, User, Port).");
|
||||
return;
|
||||
}
|
||||
handleAddHost();
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -146,15 +125,18 @@ const AddHostModal = ({ isHidden, setIsAddHostHidden, handleAddHost }) => {
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
backdropFilter: 'blur(5px)',
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.2)',
|
||||
}}
|
||||
>
|
||||
<ModalDialog
|
||||
layout="center"
|
||||
variant="outlined"
|
||||
sx={{
|
||||
backgroundColor: theme.palette.general.tertiary,
|
||||
borderColor: theme.palette.general.secondary,
|
||||
color: theme.palette.text.primary,
|
||||
padding: 3,
|
||||
padding: 0,
|
||||
borderRadius: 10,
|
||||
maxWidth: '500px',
|
||||
width: '100%',
|
||||
@@ -164,137 +146,239 @@ const AddHostModal = ({ isHidden, setIsAddHostHidden, handleAddHost }) => {
|
||||
mx: 2,
|
||||
}}
|
||||
>
|
||||
<DialogTitle sx={{ mb: 2 }}>Add Host</DialogTitle>
|
||||
<DialogContent>
|
||||
<form onSubmit={handleSubmit}>
|
||||
<Tabs
|
||||
value={activeTab}
|
||||
onChange={(e, val) => setActiveTab(val)}
|
||||
sx={{
|
||||
backgroundColor: theme.palette.general.disabled,
|
||||
borderRadius: '8px',
|
||||
padding: '8px',
|
||||
marginBottom: '16px',
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
<TabList
|
||||
sx={{
|
||||
width: '100%',
|
||||
gap: 0,
|
||||
mb: 2,
|
||||
'& button': {
|
||||
flex: 1,
|
||||
bgcolor: 'transparent',
|
||||
color: theme.palette.text.secondary,
|
||||
'&:hover': {
|
||||
bgcolor: 'rgba(255, 255, 255, 0.1)',
|
||||
},
|
||||
'&.Mui-selected': {
|
||||
bgcolor: theme.palette.general.primary,
|
||||
color: theme.palette.text.primary,
|
||||
'&:hover': {
|
||||
bgcolor: theme.palette.general.primary,
|
||||
},
|
||||
},
|
||||
<Tabs
|
||||
value={activeTab}
|
||||
onChange={(e, val) => setActiveTab(val)}
|
||||
sx={{
|
||||
width: '100%',
|
||||
mb: 0,
|
||||
backgroundColor: theme.palette.general.tertiary,
|
||||
}}
|
||||
>
|
||||
<TabList
|
||||
sx={{
|
||||
width: '100%',
|
||||
gap: 0,
|
||||
borderTopLeftRadius: 10,
|
||||
borderTopRightRadius: 10,
|
||||
backgroundColor: theme.palette.general.primary,
|
||||
'& button': {
|
||||
flex: 1,
|
||||
bgcolor: 'transparent',
|
||||
color: theme.palette.text.secondary,
|
||||
'&:hover': {
|
||||
bgcolor: theme.palette.general.disabled,
|
||||
},
|
||||
'&.Mui-selected': {
|
||||
bgcolor: theme.palette.general.tertiary,
|
||||
color: theme.palette.text.primary,
|
||||
'&:hover': {
|
||||
bgcolor: theme.palette.general.tertiary,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Tab>Basic Info</Tab>
|
||||
<Tab>Connection</Tab>
|
||||
<Tab>Authentication</Tab>
|
||||
</TabList>
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Tab sx={{ flex: 1 }}>Basic Info</Tab>
|
||||
<Tab sx={{ flex: 1 }}>Connection</Tab>
|
||||
<Tab sx={{ flex: 1 }}>Authentication</Tab>
|
||||
</TabList>
|
||||
|
||||
<TabPanel value={0}>
|
||||
<Stack spacing={2}>
|
||||
<FormControl>
|
||||
<FormLabel>Host Name</FormLabel>
|
||||
<Input
|
||||
value={form.name}
|
||||
onChange={(e) => setForm({ ...form, name: e.target.value })}
|
||||
sx={{
|
||||
backgroundColor: theme.palette.general.primary,
|
||||
<div style={{ padding: '24px', backgroundColor: theme.palette.general.tertiary }}>
|
||||
<TabPanel value={0}>
|
||||
<Stack spacing={2}>
|
||||
<FormControl>
|
||||
<FormLabel>Host Name</FormLabel>
|
||||
<Input
|
||||
value={form.name}
|
||||
onChange={(e) => setForm({ ...form, name: e.target.value })}
|
||||
sx={{
|
||||
backgroundColor: theme.palette.general.primary,
|
||||
color: theme.palette.text.primary,
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormControl>
|
||||
<FormLabel>Folder</FormLabel>
|
||||
<Input
|
||||
value={form.folder || ''}
|
||||
onChange={(e) => setForm({ ...form, folder: e.target.value })}
|
||||
sx={{
|
||||
backgroundColor: theme.palette.general.primary,
|
||||
color: theme.palette.text.primary,
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormControl>
|
||||
<FormLabel>Remember Host</FormLabel>
|
||||
<Checkbox
|
||||
checked={Boolean(form.rememberHost)}
|
||||
onChange={(e) => setForm({
|
||||
...form,
|
||||
rememberHost: e.target.checked,
|
||||
})}
|
||||
sx={{
|
||||
color: theme.palette.text.primary,
|
||||
'&.Mui-checked': {
|
||||
color: theme.palette.text.primary,
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormControl>
|
||||
<FormLabel>Folder</FormLabel>
|
||||
<Input
|
||||
value={form.folder || ''}
|
||||
onChange={(e) => setForm({ ...form, folder: e.target.value })}
|
||||
sx={{
|
||||
backgroundColor: theme.palette.general.primary,
|
||||
color: theme.palette.text.primary,
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
</Stack>
|
||||
</TabPanel>
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
</Stack>
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel value={1}>
|
||||
<Stack spacing={2}>
|
||||
<FormControl error={!form.ip}>
|
||||
<FormLabel>Host IP</FormLabel>
|
||||
<Input
|
||||
value={form.ip}
|
||||
onChange={(e) => setForm({ ...form, ip: e.target.value })}
|
||||
required
|
||||
sx={{
|
||||
backgroundColor: theme.palette.general.primary,
|
||||
color: theme.palette.text.primary,
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormControl error={!form.user}>
|
||||
<FormLabel>Host User</FormLabel>
|
||||
<Input
|
||||
value={form.user}
|
||||
onChange={(e) => setForm({ ...form, user: e.target.value })}
|
||||
required
|
||||
sx={{
|
||||
backgroundColor: theme.palette.general.primary,
|
||||
color: theme.palette.text.primary,
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormControl error={form.port < 1 || form.port > 65535}>
|
||||
<FormLabel>Host Port</FormLabel>
|
||||
<Input
|
||||
type="number"
|
||||
value={form.port}
|
||||
onChange={(e) => setForm({ ...form, port: e.target.value })}
|
||||
min={1}
|
||||
max={65535}
|
||||
required
|
||||
sx={{
|
||||
backgroundColor: theme.palette.general.primary,
|
||||
color: theme.palette.text.primary,
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
</Stack>
|
||||
</TabPanel>
|
||||
<TabPanel value={1}>
|
||||
<Stack spacing={2}>
|
||||
<FormControl error={!form.ip}>
|
||||
<FormLabel>Host IP</FormLabel>
|
||||
<Input
|
||||
value={form.ip}
|
||||
onChange={(e) => setForm({ ...form, ip: e.target.value })}
|
||||
required
|
||||
sx={{
|
||||
backgroundColor: theme.palette.general.primary,
|
||||
color: theme.palette.text.primary,
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormControl error={!form.user}>
|
||||
<FormLabel>Host User</FormLabel>
|
||||
<Input
|
||||
value={form.user}
|
||||
onChange={(e) => setForm({ ...form, user: e.target.value })}
|
||||
required
|
||||
sx={{
|
||||
backgroundColor: theme.palette.general.primary,
|
||||
color: theme.palette.text.primary,
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormControl error={form.port < 1 || form.port > 65535}>
|
||||
<FormLabel>Host Port</FormLabel>
|
||||
<Input
|
||||
type="number"
|
||||
value={form.port}
|
||||
onChange={(e) => setForm({ ...form, port: e.target.value })}
|
||||
min={1}
|
||||
max={65535}
|
||||
required
|
||||
sx={{
|
||||
backgroundColor: theme.palette.general.primary,
|
||||
color: theme.palette.text.primary,
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
</Stack>
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel value={2}>
|
||||
<Stack spacing={2}>
|
||||
<TabPanel value={2}>
|
||||
<Stack spacing={2}>
|
||||
<FormControl error={!form.authMethod || form.authMethod === 'Select Auth'}>
|
||||
<FormLabel>Authentication Method</FormLabel>
|
||||
<Select
|
||||
value={form.authMethod}
|
||||
onChange={(e, val) => handleAuthChange(val)}
|
||||
sx={{
|
||||
backgroundColor: theme.palette.general.primary,
|
||||
color: theme.palette.text.primary,
|
||||
}}
|
||||
>
|
||||
<Option value="Select Auth" disabled>Select Auth</Option>
|
||||
<Option value="password">Password</Option>
|
||||
<Option value="key">SSH Key</Option>
|
||||
</Select>
|
||||
</FormControl>
|
||||
|
||||
{form.authMethod === 'password' && (
|
||||
<FormControl error={!form.password}>
|
||||
<FormLabel>Password</FormLabel>
|
||||
<div style={{ display: 'flex', alignItems: 'center' }}>
|
||||
<Input
|
||||
type={showPassword ? 'text' : 'password'}
|
||||
value={form.password}
|
||||
onChange={(e) => setForm({ ...form, password: e.target.value })}
|
||||
sx={{
|
||||
backgroundColor: theme.palette.general.primary,
|
||||
color: theme.palette.text.primary,
|
||||
flex: 1
|
||||
}}
|
||||
/>
|
||||
<IconButton
|
||||
onClick={() => setShowPassword(!showPassword)}
|
||||
sx={{
|
||||
color: theme.palette.text.primary,
|
||||
marginLeft: 1
|
||||
}}
|
||||
>
|
||||
{showPassword ? <VisibilityOff /> : <Visibility />}
|
||||
</IconButton>
|
||||
</div>
|
||||
</FormControl>
|
||||
)}
|
||||
|
||||
{form.authMethod === 'key' && (
|
||||
<Stack spacing={2}>
|
||||
<FormControl error={!form.privateKey}>
|
||||
<FormLabel>SSH Key</FormLabel>
|
||||
<Button
|
||||
component="label"
|
||||
sx={{
|
||||
backgroundColor: theme.palette.general.primary,
|
||||
color: theme.palette.text.primary,
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
height: '40px',
|
||||
'&:hover': {
|
||||
backgroundColor: theme.palette.general.disabled,
|
||||
},
|
||||
}}
|
||||
>
|
||||
{form.privateKey ? `Change ${form.keyType || 'SSH'} Key File` : 'Upload SSH Key File'}
|
||||
<Input
|
||||
type="file"
|
||||
onChange={handleFileChange}
|
||||
sx={{ display: 'none' }}
|
||||
/>
|
||||
</Button>
|
||||
</FormControl>
|
||||
{form.privateKey && (
|
||||
<FormControl>
|
||||
<FormLabel>Key Passphrase (optional)</FormLabel>
|
||||
<div style={{ display: 'flex', alignItems: 'center' }}>
|
||||
<Input
|
||||
type={showPassphrase ? "text" : "password"}
|
||||
value={form.passphrase || ''}
|
||||
onChange={(e) => setForm(prev => ({ ...prev, passphrase: e.target.value }))}
|
||||
sx={{
|
||||
backgroundColor: theme.palette.general.primary,
|
||||
color: theme.palette.text.primary,
|
||||
flex: 1
|
||||
}}
|
||||
/>
|
||||
<IconButton
|
||||
onClick={() => setShowPassphrase(!showPassphrase)}
|
||||
sx={{
|
||||
color: theme.palette.text.primary,
|
||||
marginLeft: 1
|
||||
}}
|
||||
>
|
||||
{showPassphrase ? <VisibilityOff /> : <Visibility />}
|
||||
</IconButton>
|
||||
</div>
|
||||
</FormControl>
|
||||
)}
|
||||
</Stack>
|
||||
)}
|
||||
|
||||
{form.rememberHost && (
|
||||
<FormControl>
|
||||
<FormLabel>Remember Host</FormLabel>
|
||||
<FormLabel>Store Password</FormLabel>
|
||||
<Checkbox
|
||||
checked={form.rememberHost}
|
||||
onChange={(e) => setForm({
|
||||
...form,
|
||||
rememberHost: e.target.checked,
|
||||
|
||||
...((!e.target.checked) && {
|
||||
authMethod: 'Select Auth',
|
||||
password: '',
|
||||
privateKey: '',
|
||||
keyType: '',
|
||||
passphrase: '',
|
||||
storePassword: true
|
||||
})
|
||||
})}
|
||||
checked={Boolean(form.storePassword)}
|
||||
onChange={(e) => setForm({ ...form, storePassword: e.target.checked })}
|
||||
sx={{
|
||||
color: theme.palette.text.primary,
|
||||
'&.Mui-checked': {
|
||||
@@ -303,147 +387,31 @@ const AddHostModal = ({ isHidden, setIsAddHostHidden, handleAddHost }) => {
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
{form.rememberHost && (
|
||||
<>
|
||||
<FormControl>
|
||||
<FormLabel>Store Password</FormLabel>
|
||||
<Checkbox
|
||||
checked={form.storePassword}
|
||||
onChange={(e) => setForm({ ...form, storePassword: e.target.checked })}
|
||||
sx={{
|
||||
color: theme.palette.text.primary,
|
||||
'&.Mui-checked': {
|
||||
color: theme.palette.text.primary,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormControl error={!form.authMethod || form.authMethod === 'Select Auth'}>
|
||||
<FormLabel>Authentication Method</FormLabel>
|
||||
<Select
|
||||
value={form.authMethod}
|
||||
onChange={(e, val) => handleAuthChange(val)}
|
||||
sx={{
|
||||
backgroundColor: theme.palette.general.primary,
|
||||
color: theme.palette.text.primary,
|
||||
}}
|
||||
>
|
||||
<Option value="Select Auth" disabled>Select Auth</Option>
|
||||
<Option value="password">Password</Option>
|
||||
<Option value="key">SSH Key</Option>
|
||||
</Select>
|
||||
</FormControl>
|
||||
)}
|
||||
</Stack>
|
||||
</TabPanel>
|
||||
</div>
|
||||
|
||||
{form.authMethod === 'password' && (
|
||||
<FormControl error={!form.password}>
|
||||
<FormLabel>Password</FormLabel>
|
||||
<div style={{ display: 'flex', alignItems: 'center' }}>
|
||||
<Input
|
||||
type={showPassword ? 'text' : 'password'}
|
||||
value={form.password}
|
||||
onChange={(e) => setForm({ ...form, password: e.target.value })}
|
||||
sx={{
|
||||
backgroundColor: theme.palette.general.primary,
|
||||
color: theme.palette.text.primary,
|
||||
flex: 1
|
||||
}}
|
||||
/>
|
||||
<IconButton
|
||||
onClick={() => setShowPassword(!showPassword)}
|
||||
sx={{
|
||||
color: theme.palette.text.primary,
|
||||
marginLeft: 1
|
||||
}}
|
||||
>
|
||||
{showPassword ? <VisibilityOff /> : <Visibility />}
|
||||
</IconButton>
|
||||
</div>
|
||||
</FormControl>
|
||||
)}
|
||||
|
||||
{form.authMethod === 'key' && form.rememberHost && (
|
||||
<Stack spacing={2}>
|
||||
<FormControl error={!form.privateKey}>
|
||||
<FormLabel>SSH Key</FormLabel>
|
||||
<Button
|
||||
component="label"
|
||||
sx={{
|
||||
backgroundColor: theme.palette.general.primary,
|
||||
color: theme.palette.text.primary,
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
height: '40px',
|
||||
'&:hover': {
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.3)',
|
||||
},
|
||||
}}
|
||||
>
|
||||
{form.privateKey ? `Change ${form.keyType || 'SSH'} Key File` : 'Upload SSH Key File'}
|
||||
<Input
|
||||
type="file"
|
||||
onChange={handleFileChange}
|
||||
sx={{ display: 'none' }}
|
||||
/>
|
||||
</Button>
|
||||
</FormControl>
|
||||
{form.privateKey && (
|
||||
<FormControl>
|
||||
<FormLabel>Key Passphrase (optional)</FormLabel>
|
||||
<div style={{ display: 'flex', alignItems: 'center' }}>
|
||||
<Input
|
||||
type={showPassphrase ? "text" : "password"}
|
||||
value={form.passphrase || ''}
|
||||
onChange={(e) => setForm(prev => ({ ...prev, passphrase: e.target.value }))}
|
||||
sx={{
|
||||
backgroundColor: theme.palette.general.primary,
|
||||
color: theme.palette.text.primary,
|
||||
flex: 1
|
||||
}}
|
||||
/>
|
||||
<IconButton
|
||||
onClick={() => setShowPassphrase(!showPassphrase)}
|
||||
sx={{
|
||||
color: theme.palette.text.primary,
|
||||
marginLeft: 1
|
||||
}}
|
||||
>
|
||||
{showPassphrase ? <VisibilityOff /> : <Visibility />}
|
||||
</IconButton>
|
||||
</div>
|
||||
</FormControl>
|
||||
)}
|
||||
</Stack>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Stack>
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={!isFormValid()}
|
||||
sx={{
|
||||
backgroundColor: theme.palette.general.primary,
|
||||
color: theme.palette.text.primary,
|
||||
'&:hover': {
|
||||
backgroundColor: theme.palette.general.disabled,
|
||||
},
|
||||
'&:disabled': {
|
||||
backgroundColor: 'rgba(255, 255, 255, 0.1)',
|
||||
color: 'rgba(255, 255, 255, 0.3)',
|
||||
},
|
||||
marginTop: 3,
|
||||
width: '100%',
|
||||
height: '40px',
|
||||
}}
|
||||
>
|
||||
Add Host
|
||||
</Button>
|
||||
</form>
|
||||
</DialogContent>
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
sx={{
|
||||
backgroundColor: theme.palette.general.primary,
|
||||
color: theme.palette.text.primary,
|
||||
'&:hover': {
|
||||
backgroundColor: theme.palette.general.disabled,
|
||||
},
|
||||
'&:disabled': {
|
||||
backgroundColor: 'rgba(255, 255, 255, 0.1)',
|
||||
color: 'rgba(255, 255, 255, 0.3)',
|
||||
},
|
||||
marginTop: 1,
|
||||
width: '100%',
|
||||
height: '40px',
|
||||
}}
|
||||
>
|
||||
Add Host
|
||||
</Button>
|
||||
</Tabs>
|
||||
</ModalDialog>
|
||||
</Modal>
|
||||
</CssVarsProvider>
|
||||
@@ -452,8 +420,10 @@ const AddHostModal = ({ isHidden, setIsAddHostHidden, handleAddHost }) => {
|
||||
|
||||
AddHostModal.propTypes = {
|
||||
isHidden: PropTypes.bool.isRequired,
|
||||
setIsAddHostHidden: PropTypes.func.isRequired,
|
||||
form: PropTypes.object.isRequired,
|
||||
setForm: PropTypes.func.isRequired,
|
||||
handleAddHost: PropTypes.func.isRequired,
|
||||
setIsAddHostHidden: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default AddHostModal;
|
||||
@@ -36,7 +36,8 @@ const EditHostModal = ({ isHidden, hostConfig, setIsEditHostHidden, handleEditHo
|
||||
keyType: hostConfig?.keyType || '',
|
||||
passphrase: '',
|
||||
authMethod: hostConfig?.authMethod || 'Select Auth',
|
||||
storePassword: true
|
||||
storePassword: true,
|
||||
rememberHost: true
|
||||
});
|
||||
const [showPassword, setShowPassword] = useState(false);
|
||||
const [showPassphrase, setShowPassphrase] = useState(false);
|
||||
@@ -51,11 +52,13 @@ const EditHostModal = ({ isHidden, hostConfig, setIsEditHostHidden, handleEditHo
|
||||
ip: hostConfig.ip || '',
|
||||
user: hostConfig.user || '',
|
||||
password: hostConfig.password || '',
|
||||
rsaKey: hostConfig.rsaKey || '',
|
||||
privateKey: hostConfig.privateKey || '',
|
||||
keyType: hostConfig.keyType || '',
|
||||
passphrase: hostConfig.passphrase || '',
|
||||
port: hostConfig.port || 22,
|
||||
authMethod: hostConfig.password ? 'password' : hostConfig.rsaKey ? 'rsaKey' : 'Select Auth',
|
||||
authMethod: hostConfig.password ? 'password' : hostConfig.privateKey ? 'key' : 'Select Auth',
|
||||
rememberHost: true,
|
||||
storePassword: !!(hostConfig.password || hostConfig.rsaKey),
|
||||
storePassword: !!(hostConfig.password || hostConfig.privateKey),
|
||||
});
|
||||
}
|
||||
}, [isHidden, hostConfig]);
|
||||
@@ -118,20 +121,28 @@ const EditHostModal = ({ isHidden, hostConfig, setIsEditHostHidden, handleEditHo
|
||||
...prev,
|
||||
storePassword: Boolean(checked),
|
||||
password: checked ? prev.password : "",
|
||||
rsaKey: checked ? prev.rsaKey : "",
|
||||
privateKey: checked ? prev.privateKey : "",
|
||||
passphrase: checked ? prev.passphrase : "",
|
||||
authMethod: checked ? prev.authMethod : "Select Auth"
|
||||
}));
|
||||
};
|
||||
|
||||
const isFormValid = () => {
|
||||
const { ip, user, port, authMethod, password, rsaKey, storePassword } = form;
|
||||
const { ip, user, port, authMethod, password, privateKey } = form;
|
||||
|
||||
// Basic validation for required fields
|
||||
if (!ip?.trim() || !user?.trim() || !port) return false;
|
||||
|
||||
// Port validation
|
||||
const portNum = Number(port);
|
||||
if (isNaN(portNum) || portNum < 1 || portNum > 65535) return false;
|
||||
|
||||
if (Boolean(storePassword) && authMethod === 'password' && !password?.trim()) return false;
|
||||
if (Boolean(storePassword) && authMethod === 'rsaKey' && !rsaKey && !hostConfig?.rsaKey) return false;
|
||||
if (Boolean(storePassword) && authMethod === 'Select Auth') return false;
|
||||
// Auth method validation only if storing password
|
||||
if (form.storePassword) {
|
||||
if (authMethod === 'Select Auth') return false;
|
||||
if (authMethod === 'password' && !password?.trim()) return false;
|
||||
if (authMethod === 'key' && !privateKey?.trim()) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
@@ -142,15 +153,25 @@ const EditHostModal = ({ isHidden, hostConfig, setIsEditHostHidden, handleEditHo
|
||||
|
||||
setIsLoading(true);
|
||||
try {
|
||||
await handleEditHost(hostConfig, {
|
||||
const newConfig = {
|
||||
name: form.name || form.ip,
|
||||
folder: form.folder,
|
||||
ip: form.ip,
|
||||
user: form.user,
|
||||
password: form.authMethod === 'password' ? form.password : undefined,
|
||||
rsaKey: form.authMethod === 'rsaKey' ? form.rsaKey : undefined,
|
||||
port: String(form.port),
|
||||
});
|
||||
};
|
||||
|
||||
if (form.storePassword) {
|
||||
if (form.authMethod === 'password') {
|
||||
newConfig.password = form.password;
|
||||
} else if (form.authMethod === 'key') {
|
||||
newConfig.privateKey = form.privateKey;
|
||||
newConfig.keyType = form.keyType;
|
||||
newConfig.passphrase = form.passphrase;
|
||||
}
|
||||
}
|
||||
|
||||
await handleEditHost(hostConfig, newConfig);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
@@ -162,11 +183,9 @@ const EditHostModal = ({ isHidden, hostConfig, setIsEditHostHidden, handleEditHo
|
||||
open={!isHidden}
|
||||
onClose={() => !isLoading && setIsEditHostHidden(true)}
|
||||
sx={{
|
||||
position: 'fixed',
|
||||
inset: 0,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
backdropFilter: 'blur(5px)',
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.2)',
|
||||
}}
|
||||
@@ -178,7 +197,7 @@ const EditHostModal = ({ isHidden, hostConfig, setIsEditHostHidden, handleEditHo
|
||||
backgroundColor: theme.palette.general.tertiary,
|
||||
borderColor: theme.palette.general.secondary,
|
||||
color: theme.palette.text.primary,
|
||||
padding: 3,
|
||||
padding: 0,
|
||||
borderRadius: 10,
|
||||
maxWidth: '500px',
|
||||
width: '100%',
|
||||
@@ -188,134 +207,133 @@ const EditHostModal = ({ isHidden, hostConfig, setIsEditHostHidden, handleEditHo
|
||||
mx: 2,
|
||||
}}
|
||||
>
|
||||
<DialogTitle sx={{ mb: 2 }}>Edit Host</DialogTitle>
|
||||
<DialogContent>
|
||||
<form onSubmit={handleSubmit}>
|
||||
<Tabs
|
||||
value={activeTab}
|
||||
onChange={(e, val) => setActiveTab(val)}
|
||||
sx={{
|
||||
backgroundColor: theme.palette.general.disabled,
|
||||
borderRadius: '8px',
|
||||
padding: '8px',
|
||||
marginBottom: '16px',
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
<TabList
|
||||
sx={{
|
||||
width: '100%',
|
||||
gap: 0,
|
||||
mb: 2,
|
||||
'& button': {
|
||||
flex: 1,
|
||||
bgcolor: 'transparent',
|
||||
color: theme.palette.text.secondary,
|
||||
'&:hover': {
|
||||
bgcolor: 'rgba(255, 255, 255, 0.1)',
|
||||
},
|
||||
'&.Mui-selected': {
|
||||
bgcolor: theme.palette.general.primary,
|
||||
color: theme.palette.text.primary,
|
||||
'&:hover': {
|
||||
bgcolor: theme.palette.general.primary,
|
||||
},
|
||||
},
|
||||
<Tabs
|
||||
value={activeTab}
|
||||
onChange={(e, val) => setActiveTab(val)}
|
||||
sx={{
|
||||
width: '100%',
|
||||
mb: 0,
|
||||
backgroundColor: theme.palette.general.tertiary,
|
||||
}}
|
||||
>
|
||||
<TabList
|
||||
sx={{
|
||||
width: '100%',
|
||||
gap: 0,
|
||||
borderTopLeftRadius: 10,
|
||||
borderTopRightRadius: 10,
|
||||
backgroundColor: theme.palette.general.primary,
|
||||
'& button': {
|
||||
flex: 1,
|
||||
bgcolor: 'transparent',
|
||||
color: theme.palette.text.secondary,
|
||||
'&:hover': {
|
||||
bgcolor: theme.palette.general.disabled,
|
||||
},
|
||||
'&.Mui-selected': {
|
||||
bgcolor: theme.palette.general.tertiary,
|
||||
color: theme.palette.text.primary,
|
||||
'&:hover': {
|
||||
bgcolor: theme.palette.general.tertiary,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Tab>Basic Info</Tab>
|
||||
<Tab>Connection</Tab>
|
||||
<Tab>Authentication</Tab>
|
||||
</TabList>
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Tab sx={{ flex: 1 }}>Basic Info</Tab>
|
||||
<Tab sx={{ flex: 1 }}>Connection</Tab>
|
||||
<Tab sx={{ flex: 1 }}>Authentication</Tab>
|
||||
</TabList>
|
||||
|
||||
<TabPanel value={0}>
|
||||
<Stack spacing={2}>
|
||||
<FormControl>
|
||||
<FormLabel>Host Name</FormLabel>
|
||||
<Input
|
||||
value={form.name}
|
||||
onChange={(e) => setForm((prev) => ({ ...prev, name: e.target.value }))}
|
||||
sx={{
|
||||
backgroundColor: theme.palette.general.primary,
|
||||
color: theme.palette.text.primary
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
<div style={{ padding: '24px', backgroundColor: theme.palette.general.tertiary }}>
|
||||
<TabPanel value={0}>
|
||||
<Stack spacing={2}>
|
||||
<FormControl>
|
||||
<FormLabel>Host Name</FormLabel>
|
||||
<Input
|
||||
value={form.name}
|
||||
onChange={(e) => setForm((prev) => ({ ...prev, name: e.target.value }))}
|
||||
sx={{
|
||||
backgroundColor: theme.palette.general.primary,
|
||||
color: theme.palette.text.primary
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
<FormControl>
|
||||
<FormLabel>Folder</FormLabel>
|
||||
<Input
|
||||
value={form.folder}
|
||||
onChange={(e) => setForm((prev) => ({ ...prev, folder: e.target.value }))}
|
||||
sx={{
|
||||
backgroundColor: theme.palette.general.primary,
|
||||
color: theme.palette.text.primary
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
</Stack>
|
||||
</TabPanel>
|
||||
<FormControl>
|
||||
<FormLabel>Folder</FormLabel>
|
||||
<Input
|
||||
value={form.folder}
|
||||
onChange={(e) => setForm((prev) => ({ ...prev, folder: e.target.value }))}
|
||||
sx={{
|
||||
backgroundColor: theme.palette.general.primary,
|
||||
color: theme.palette.text.primary
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
</Stack>
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel value={1}>
|
||||
<Stack spacing={2}>
|
||||
<FormControl error={!form.ip}>
|
||||
<FormLabel>Host IP</FormLabel>
|
||||
<Input
|
||||
value={form.ip}
|
||||
onChange={(e) => setForm((prev) => ({ ...prev, ip: e.target.value }))}
|
||||
sx={{
|
||||
backgroundColor: theme.palette.general.primary,
|
||||
color: theme.palette.text.primary
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
<TabPanel value={1}>
|
||||
<Stack spacing={2}>
|
||||
<FormControl error={!form.ip}>
|
||||
<FormLabel>Host IP</FormLabel>
|
||||
<Input
|
||||
value={form.ip}
|
||||
onChange={(e) => setForm((prev) => ({ ...prev, ip: e.target.value }))}
|
||||
sx={{
|
||||
backgroundColor: theme.palette.general.primary,
|
||||
color: theme.palette.text.primary
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
<FormControl error={form.port < 1 || form.port > 65535}>
|
||||
<FormLabel>Host Port</FormLabel>
|
||||
<Input
|
||||
type="number"
|
||||
value={form.port}
|
||||
onChange={(e) => setForm((prev) => ({ ...prev, port: e.target.value }))}
|
||||
sx={{
|
||||
backgroundColor: theme.palette.general.primary,
|
||||
color: theme.palette.text.primary
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormControl error={form.port < 1 || form.port > 65535}>
|
||||
<FormLabel>Host Port</FormLabel>
|
||||
<Input
|
||||
type="number"
|
||||
value={form.port}
|
||||
onChange={(e) => setForm((prev) => ({ ...prev, port: e.target.value }))}
|
||||
sx={{
|
||||
backgroundColor: theme.palette.general.primary,
|
||||
color: theme.palette.text.primary
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
<FormControl error={!form.user}>
|
||||
<FormLabel>Host User</FormLabel>
|
||||
<Input
|
||||
value={form.user}
|
||||
onChange={(e) => setForm((prev) => ({ ...prev, user: e.target.value }))}
|
||||
sx={{
|
||||
backgroundColor: theme.palette.general.primary,
|
||||
color: theme.palette.text.primary
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
</Stack>
|
||||
</TabPanel>
|
||||
<FormControl error={!form.user}>
|
||||
<FormLabel>Host User</FormLabel>
|
||||
<Input
|
||||
value={form.user}
|
||||
onChange={(e) => setForm((prev) => ({ ...prev, user: e.target.value }))}
|
||||
sx={{
|
||||
backgroundColor: theme.palette.general.primary,
|
||||
color: theme.palette.text.primary
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
</Stack>
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel value={2}>
|
||||
<Stack spacing={2}>
|
||||
<FormControl>
|
||||
<FormLabel>Store Password</FormLabel>
|
||||
<Checkbox
|
||||
checked={form.storePassword}
|
||||
onChange={(e) => handleStorePasswordChange(e.target.checked)}
|
||||
sx={{
|
||||
<TabPanel value={2}>
|
||||
<Stack spacing={2}>
|
||||
<FormControl>
|
||||
<FormLabel>Store Password</FormLabel>
|
||||
<Checkbox
|
||||
checked={Boolean(form.storePassword)}
|
||||
onChange={(e) => handleStorePasswordChange(e.target.checked)}
|
||||
sx={{
|
||||
color: theme.palette.text.primary,
|
||||
'&.Mui-checked': {
|
||||
color: theme.palette.text.primary,
|
||||
'&.Mui-checked': {
|
||||
color: theme.palette.text.primary
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
{form.storePassword && (
|
||||
<FormControl error={form.authMethod === 'Select Auth'}>
|
||||
{form.storePassword && (
|
||||
<>
|
||||
<FormControl error={form.storePassword && (!form.authMethod || form.authMethod === 'Select Auth')}>
|
||||
<FormLabel>Authentication Method</FormLabel>
|
||||
<Select
|
||||
value={form.authMethod}
|
||||
@@ -330,170 +348,129 @@ const EditHostModal = ({ isHidden, hostConfig, setIsEditHostHidden, handleEditHo
|
||||
<Option value="key">SSH Key</Option>
|
||||
</Select>
|
||||
</FormControl>
|
||||
)}
|
||||
|
||||
{form.authMethod === 'password' && form.storePassword && (
|
||||
<FormControl error={!form.password}>
|
||||
<FormLabel>Password</FormLabel>
|
||||
<div style={{ display: 'flex', alignItems: 'center' }}>
|
||||
<Input
|
||||
type={showPassword ? 'text' : 'password'}
|
||||
value={form.password}
|
||||
onChange={(e) => setForm((prev) => ({ ...prev, password: e.target.value }))}
|
||||
sx={{
|
||||
backgroundColor: theme.palette.general.primary,
|
||||
color: theme.palette.text.primary,
|
||||
flex: 1
|
||||
}}
|
||||
/>
|
||||
<IconButton
|
||||
onClick={() => setShowPassword(!showPassword)}
|
||||
sx={{
|
||||
color: theme.palette.text.primary,
|
||||
marginLeft: 1
|
||||
}}
|
||||
>
|
||||
{showPassword ? <VisibilityOff /> : <Visibility />}
|
||||
</IconButton>
|
||||
</div>
|
||||
</FormControl>
|
||||
)}
|
||||
|
||||
{form.authMethod === 'rsaKey' && form.storePassword && (
|
||||
<FormControl error={!form.rsaKey && !hostConfig?.rsaKey}>
|
||||
<FormLabel>Public Key</FormLabel>
|
||||
<Button
|
||||
component="label"
|
||||
sx={{
|
||||
backgroundColor: theme.palette.general.primary,
|
||||
color: theme.palette.text.primary,
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
height: '40px',
|
||||
'&:hover': {
|
||||
backgroundColor: theme.palette.general.disabled,
|
||||
},
|
||||
}}
|
||||
>
|
||||
{form.rsaKey ? 'Change Public Key File' : 'Upload Public Key File'}
|
||||
<Input
|
||||
type="file"
|
||||
onChange={handleFileChange}
|
||||
sx={{ display: 'none' }}
|
||||
/>
|
||||
</Button>
|
||||
{hostConfig?.rsaKey && !form.rsaKey && (
|
||||
<FormLabel
|
||||
sx={{
|
||||
color: theme.palette.text.secondary,
|
||||
fontSize: '0.875rem',
|
||||
mt: 1,
|
||||
display: 'block',
|
||||
textAlign: 'center'
|
||||
}}
|
||||
>
|
||||
Existing key detected. Upload to replace.
|
||||
</FormLabel>
|
||||
)}
|
||||
</FormControl>
|
||||
)}
|
||||
|
||||
{form.authMethod === 'key' && form.storePassword && (
|
||||
<Stack spacing={2}>
|
||||
<FormControl error={!form.privateKey && !hostConfig?.privateKey}>
|
||||
<FormLabel>SSH Key</FormLabel>
|
||||
<Button
|
||||
component="label"
|
||||
sx={{
|
||||
backgroundColor: theme.palette.general.primary,
|
||||
color: theme.palette.text.primary,
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
height: '40px',
|
||||
'&:hover': {
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.3)',
|
||||
},
|
||||
}}
|
||||
>
|
||||
{form.privateKey ? `Change ${form.keyType || 'SSH'} Key File` : 'Upload SSH Key File'}
|
||||
{form.authMethod === 'password' && (
|
||||
<FormControl error={form.storePassword && !form.password}>
|
||||
<FormLabel>Password</FormLabel>
|
||||
<div style={{ display: 'flex', alignItems: 'center' }}>
|
||||
<Input
|
||||
type="file"
|
||||
onChange={handleFileChange}
|
||||
sx={{ display: 'none' }}
|
||||
type={showPassword ? 'text' : 'password'}
|
||||
value={form.password}
|
||||
onChange={(e) => setForm(prev => ({ ...prev, password: e.target.value }))}
|
||||
sx={{
|
||||
backgroundColor: theme.palette.general.primary,
|
||||
color: theme.palette.text.primary,
|
||||
flex: 1
|
||||
}}
|
||||
/>
|
||||
</Button>
|
||||
{hostConfig?.privateKey && !form.privateKey && (
|
||||
<FormLabel
|
||||
sx={{
|
||||
color: theme.palette.text.secondary,
|
||||
fontSize: '0.875rem',
|
||||
mt: 1,
|
||||
display: 'block',
|
||||
textAlign: 'center'
|
||||
<IconButton
|
||||
onClick={() => setShowPassword(!showPassword)}
|
||||
sx={{
|
||||
color: theme.palette.text.primary,
|
||||
marginLeft: 1
|
||||
}}
|
||||
>
|
||||
Existing {hostConfig.keyType || 'SSH'} key detected. Upload to replace.
|
||||
</FormLabel>
|
||||
)}
|
||||
{showPassword ? <VisibilityOff /> : <Visibility />}
|
||||
</IconButton>
|
||||
</div>
|
||||
</FormControl>
|
||||
{form.privateKey && (
|
||||
<FormControl>
|
||||
<FormLabel>Key Passphrase (optional)</FormLabel>
|
||||
<div style={{ display: 'flex', alignItems: 'center' }}>
|
||||
)}
|
||||
|
||||
{form.authMethod === 'key' && (
|
||||
<Stack spacing={2}>
|
||||
<FormControl error={form.storePassword && !form.privateKey}>
|
||||
<FormLabel>SSH Key</FormLabel>
|
||||
<Button
|
||||
component="label"
|
||||
sx={{
|
||||
backgroundColor: theme.palette.general.primary,
|
||||
color: theme.palette.text.primary,
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
height: '40px',
|
||||
'&:hover': {
|
||||
backgroundColor: theme.palette.general.disabled,
|
||||
},
|
||||
}}
|
||||
>
|
||||
{form.privateKey ? `Change ${form.keyType || 'SSH'} Key File` : 'Upload SSH Key File'}
|
||||
<Input
|
||||
type={showPassphrase ? "text" : "password"}
|
||||
value={form.passphrase || ''}
|
||||
onChange={(e) => setForm(prev => ({ ...prev, passphrase: e.target.value }))}
|
||||
sx={{
|
||||
backgroundColor: theme.palette.general.primary,
|
||||
color: theme.palette.text.primary,
|
||||
flex: 1
|
||||
}}
|
||||
type="file"
|
||||
onChange={handleFileChange}
|
||||
sx={{ display: 'none' }}
|
||||
/>
|
||||
<IconButton
|
||||
onClick={() => setShowPassphrase(!showPassphrase)}
|
||||
sx={{
|
||||
color: theme.palette.text.primary,
|
||||
marginLeft: 1
|
||||
</Button>
|
||||
{hostConfig?.privateKey && !form.privateKey && (
|
||||
<FormLabel
|
||||
sx={{
|
||||
color: theme.palette.text.secondary,
|
||||
fontSize: '0.875rem',
|
||||
mt: 1,
|
||||
display: 'block',
|
||||
textAlign: 'center'
|
||||
}}
|
||||
>
|
||||
{showPassphrase ? <VisibilityOff /> : <Visibility />}
|
||||
</IconButton>
|
||||
</div>
|
||||
Existing {hostConfig.keyType || 'SSH'} key detected. Upload to replace.
|
||||
</FormLabel>
|
||||
)}
|
||||
</FormControl>
|
||||
)}
|
||||
</Stack>
|
||||
)}
|
||||
</Stack>
|
||||
</TabPanel>
|
||||
</Tabs>
|
||||
{form.privateKey && (
|
||||
<FormControl>
|
||||
<FormLabel>Key Passphrase (optional)</FormLabel>
|
||||
<div style={{ display: 'flex', alignItems: 'center' }}>
|
||||
<Input
|
||||
type={showPassphrase ? "text" : "password"}
|
||||
value={form.passphrase || ''}
|
||||
onChange={(e) => setForm(prev => ({ ...prev, passphrase: e.target.value }))}
|
||||
sx={{
|
||||
backgroundColor: theme.palette.general.primary,
|
||||
color: theme.palette.text.primary,
|
||||
flex: 1
|
||||
}}
|
||||
/>
|
||||
<IconButton
|
||||
onClick={() => setShowPassphrase(!showPassphrase)}
|
||||
sx={{
|
||||
color: theme.palette.text.primary,
|
||||
marginLeft: 1
|
||||
}}
|
||||
>
|
||||
{showPassphrase ? <VisibilityOff /> : <Visibility />}
|
||||
</IconButton>
|
||||
</div>
|
||||
</FormControl>
|
||||
)}
|
||||
</Stack>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Stack>
|
||||
</TabPanel>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={!isFormValid() || isLoading}
|
||||
sx={{
|
||||
backgroundColor: theme.palette.general.primary,
|
||||
color: theme.palette.text.primary,
|
||||
'&:hover': {
|
||||
backgroundColor: theme.palette.general.disabled
|
||||
},
|
||||
'&:disabled': {
|
||||
backgroundColor: 'rgba(255, 255, 255, 0.1)',
|
||||
color: 'rgba(255, 255, 255, 0.3)',
|
||||
},
|
||||
marginTop: 3,
|
||||
width: '100%',
|
||||
height: '40px',
|
||||
}}
|
||||
>
|
||||
{isLoading ? "Saving..." : "Save Changes"}
|
||||
</Button>
|
||||
</form>
|
||||
</DialogContent>
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
disabled={isLoading}
|
||||
sx={{
|
||||
backgroundColor: theme.palette.general.primary,
|
||||
color: theme.palette.text.primary,
|
||||
'&:hover': {
|
||||
backgroundColor: theme.palette.general.disabled,
|
||||
},
|
||||
'&:disabled': {
|
||||
backgroundColor: 'rgba(255, 255, 255, 0.1)',
|
||||
color: 'rgba(255, 255, 255, 0.3)',
|
||||
},
|
||||
marginTop: 1,
|
||||
width: '100%',
|
||||
height: '40px',
|
||||
}}
|
||||
>
|
||||
{isLoading ? "Saving changes..." : "Save changes"}
|
||||
</Button>
|
||||
</Tabs>
|
||||
</ModalDialog>
|
||||
</Modal>
|
||||
</CssVarsProvider>
|
||||
|
||||
Reference in New Issue
Block a user