diff --git a/src/App.jsx b/src/App.jsx index b15ae738..6bb46c5d 100644 --- a/src/App.jsx +++ b/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() { )} @@ -681,7 +709,7 @@ function App() { setForm={setEditHostForm} handleEditHost={handleEditHost} setIsEditHostHidden={setIsEditHostHidden} - hostConfig={currentHostConfig} + hostConfig={currentHostConfig || {}} /> { socketRef.current.emit("editHost", { userId: currentUser.current.id, diff --git a/src/modals/AddHostModal.jsx b/src/modals/AddHostModal.jsx index ed21a4b7..03043b11 100644 --- a/src/modals/AddHostModal.jsx +++ b/src/modals/AddHostModal.jsx @@ -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)', }} > { mx: 2, }} > - Add Host - -
- setActiveTab(val)} - sx={{ - backgroundColor: theme.palette.general.disabled, - borderRadius: '8px', - padding: '8px', - marginBottom: '16px', - width: '100%', - }} - > - setActiveTab(val)} + sx={{ + width: '100%', + mb: 0, + backgroundColor: theme.palette.general.tertiary, + }} + > + - Basic Info - Connection - Authentication - + }, + }, + }} + > + Basic Info + Connection + Authentication + - - - - Host Name - setForm({ ...form, name: e.target.value })} - sx={{ - backgroundColor: theme.palette.general.primary, +
+ + + + Host Name + setForm({ ...form, name: e.target.value })} + sx={{ + backgroundColor: theme.palette.general.primary, + color: theme.palette.text.primary, + }} + /> + + + Folder + setForm({ ...form, folder: e.target.value })} + sx={{ + backgroundColor: theme.palette.general.primary, + color: theme.palette.text.primary, + }} + /> + + + Remember Host + setForm({ + ...form, + rememberHost: e.target.checked, + })} + sx={{ + color: theme.palette.text.primary, + '&.Mui-checked': { color: theme.palette.text.primary, - }} - /> - - - Folder - setForm({ ...form, folder: e.target.value })} - sx={{ - backgroundColor: theme.palette.general.primary, - color: theme.palette.text.primary, - }} - /> - - - + }, + }} + /> + + + - - - - Host IP - setForm({ ...form, ip: e.target.value })} - required - sx={{ - backgroundColor: theme.palette.general.primary, - color: theme.palette.text.primary, - }} - /> - - - Host User - setForm({ ...form, user: e.target.value })} - required - sx={{ - backgroundColor: theme.palette.general.primary, - color: theme.palette.text.primary, - }} - /> - - 65535}> - Host Port - setForm({ ...form, port: e.target.value })} - min={1} - max={65535} - required - sx={{ - backgroundColor: theme.palette.general.primary, - color: theme.palette.text.primary, - }} - /> - - - + + + + Host IP + setForm({ ...form, ip: e.target.value })} + required + sx={{ + backgroundColor: theme.palette.general.primary, + color: theme.palette.text.primary, + }} + /> + + + Host User + setForm({ ...form, user: e.target.value })} + required + sx={{ + backgroundColor: theme.palette.general.primary, + color: theme.palette.text.primary, + }} + /> + + 65535}> + Host Port + setForm({ ...form, port: e.target.value })} + min={1} + max={65535} + required + sx={{ + backgroundColor: theme.palette.general.primary, + color: theme.palette.text.primary, + }} + /> + + + - - + + + + Authentication Method + + + + {form.authMethod === 'password' && ( + + Password +
+ setForm({ ...form, password: e.target.value })} + sx={{ + backgroundColor: theme.palette.general.primary, + color: theme.palette.text.primary, + flex: 1 + }} + /> + setShowPassword(!showPassword)} + sx={{ + color: theme.palette.text.primary, + marginLeft: 1 + }} + > + {showPassword ? : } + +
+
+ )} + + {form.authMethod === 'key' && ( + + + SSH Key + + + {form.privateKey && ( + + Key Passphrase (optional) +
+ setForm(prev => ({ ...prev, passphrase: e.target.value }))} + sx={{ + backgroundColor: theme.palette.general.primary, + color: theme.palette.text.primary, + flex: 1 + }} + /> + setShowPassphrase(!showPassphrase)} + sx={{ + color: theme.palette.text.primary, + marginLeft: 1 + }} + > + {showPassphrase ? : } + +
+
+ )} +
+ )} + + {form.rememberHost && ( - Remember Host + Store Password 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 }) => { }} /> - {form.rememberHost && ( - <> - - Store Password - setForm({ ...form, storePassword: e.target.checked })} - sx={{ - color: theme.palette.text.primary, - '&.Mui-checked': { - color: theme.palette.text.primary, - }, - }} - /> - - - Authentication Method - - + )} +
+
+
- {form.authMethod === 'password' && ( - - Password -
- setForm({ ...form, password: e.target.value })} - sx={{ - backgroundColor: theme.palette.general.primary, - color: theme.palette.text.primary, - flex: 1 - }} - /> - setShowPassword(!showPassword)} - sx={{ - color: theme.palette.text.primary, - marginLeft: 1 - }} - > - {showPassword ? : } - -
-
- )} - - {form.authMethod === 'key' && form.rememberHost && ( - - - SSH Key - - - {form.privateKey && ( - - Key Passphrase (optional) -
- setForm(prev => ({ ...prev, passphrase: e.target.value }))} - sx={{ - backgroundColor: theme.palette.general.primary, - color: theme.palette.text.primary, - flex: 1 - }} - /> - setShowPassphrase(!showPassphrase)} - sx={{ - color: theme.palette.text.primary, - marginLeft: 1 - }} - > - {showPassphrase ? : } - -
-
- )} -
- )} - - )} -
-
-
- - -
-
+ +
@@ -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; \ No newline at end of file diff --git a/src/modals/EditHostModal.jsx b/src/modals/EditHostModal.jsx index 80da2c2b..9608fbe4 100644 --- a/src/modals/EditHostModal.jsx +++ b/src/modals/EditHostModal.jsx @@ -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, }} > - Edit Host - -
- setActiveTab(val)} - sx={{ - backgroundColor: theme.palette.general.disabled, - borderRadius: '8px', - padding: '8px', - marginBottom: '16px', - width: '100%', - }} - > - setActiveTab(val)} + sx={{ + width: '100%', + mb: 0, + backgroundColor: theme.palette.general.tertiary, + }} + > + - Basic Info - Connection - Authentication - + }, + }, + }} + > + Basic Info + Connection + Authentication + - - - - Host Name - setForm((prev) => ({ ...prev, name: e.target.value }))} - sx={{ - backgroundColor: theme.palette.general.primary, - color: theme.palette.text.primary - }} - /> - +
+ + + + Host Name + setForm((prev) => ({ ...prev, name: e.target.value }))} + sx={{ + backgroundColor: theme.palette.general.primary, + color: theme.palette.text.primary + }} + /> + - - Folder - setForm((prev) => ({ ...prev, folder: e.target.value }))} - sx={{ - backgroundColor: theme.palette.general.primary, - color: theme.palette.text.primary - }} - /> - - - + + Folder + setForm((prev) => ({ ...prev, folder: e.target.value }))} + sx={{ + backgroundColor: theme.palette.general.primary, + color: theme.palette.text.primary + }} + /> + + + - - - - Host IP - setForm((prev) => ({ ...prev, ip: e.target.value }))} - sx={{ - backgroundColor: theme.palette.general.primary, - color: theme.palette.text.primary - }} - /> - + + + + Host IP + setForm((prev) => ({ ...prev, ip: e.target.value }))} + sx={{ + backgroundColor: theme.palette.general.primary, + color: theme.palette.text.primary + }} + /> + - 65535}> - Host Port - setForm((prev) => ({ ...prev, port: e.target.value }))} - sx={{ - backgroundColor: theme.palette.general.primary, - color: theme.palette.text.primary - }} - /> - + 65535}> + Host Port + setForm((prev) => ({ ...prev, port: e.target.value }))} + sx={{ + backgroundColor: theme.palette.general.primary, + color: theme.palette.text.primary + }} + /> + - - Host User - setForm((prev) => ({ ...prev, user: e.target.value }))} - sx={{ - backgroundColor: theme.palette.general.primary, - color: theme.palette.text.primary - }} - /> - - - + + Host User + setForm((prev) => ({ ...prev, user: e.target.value }))} + sx={{ + backgroundColor: theme.palette.general.primary, + color: theme.palette.text.primary + }} + /> + + + - - - - Store Password - handleStorePasswordChange(e.target.checked)} - sx={{ + + + + Store Password + handleStorePasswordChange(e.target.checked)} + sx={{ + color: theme.palette.text.primary, + '&.Mui-checked': { color: theme.palette.text.primary, - '&.Mui-checked': { - color: theme.palette.text.primary - } - }} - /> - + }, + }} + /> + - {form.storePassword && ( - + {form.storePassword && ( + <> + Authentication Method - )} - {form.authMethod === 'password' && form.storePassword && ( - - Password -
- setForm((prev) => ({ ...prev, password: e.target.value }))} - sx={{ - backgroundColor: theme.palette.general.primary, - color: theme.palette.text.primary, - flex: 1 - }} - /> - setShowPassword(!showPassword)} - sx={{ - color: theme.palette.text.primary, - marginLeft: 1 - }} - > - {showPassword ? : } - -
-
- )} - - {form.authMethod === 'rsaKey' && form.storePassword && ( - - Public Key - - {hostConfig?.rsaKey && !form.rsaKey && ( - - Existing key detected. Upload to replace. - - )} - - )} - - {form.authMethod === 'key' && form.storePassword && ( - - - SSH Key - - {hostConfig?.privateKey && !form.privateKey && ( - setShowPassword(!showPassword)} + sx={{ + color: theme.palette.text.primary, + marginLeft: 1 }} > - Existing {hostConfig.keyType || 'SSH'} key detected. Upload to replace. - - )} + {showPassword ? : } + +
- {form.privateKey && ( - - Key Passphrase (optional) -
+ )} + + {form.authMethod === 'key' && ( + + + SSH Key + + {hostConfig?.privateKey && !form.privateKey && ( + - {showPassphrase ? : } - -
+ Existing {hostConfig.keyType || 'SSH'} key detected. Upload to replace. + + )}
- )} -
- )} - -
-
+ {form.privateKey && ( + + Key Passphrase (optional) +
+ setForm(prev => ({ ...prev, passphrase: e.target.value }))} + sx={{ + backgroundColor: theme.palette.general.primary, + color: theme.palette.text.primary, + flex: 1 + }} + /> + setShowPassphrase(!showPassphrase)} + sx={{ + color: theme.palette.text.primary, + marginLeft: 1 + }} + > + {showPassphrase ? : } + +
+
+ )} + + )} + + )} + + + - -
-
+ +