Revamped login system, fixed no auth modal errors, changed many UI's.
This commit is contained in:
7
package-lock.json
generated
7
package-lock.json
generated
@@ -29,6 +29,7 @@
|
||||
"express": "^4.21.2",
|
||||
"is-stream": "^4.0.1",
|
||||
"make-dir": "^5.0.0",
|
||||
"mitt": "^3.0.1",
|
||||
"mongoose": "^8.12.1",
|
||||
"node-ssh": "^13.2.0",
|
||||
"prop-types": "^15.8.1",
|
||||
@@ -6341,6 +6342,12 @@
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/mitt": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz",
|
||||
"integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/mkdirp": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
"express": "^4.21.2",
|
||||
"is-stream": "^4.0.1",
|
||||
"make-dir": "^5.0.0",
|
||||
"mitt": "^3.0.1",
|
||||
"mongoose": "^8.12.1",
|
||||
"node-ssh": "^13.2.0",
|
||||
"prop-types": "^15.8.1",
|
||||
|
||||
195
src/App.jsx
195
src/App.jsx
@@ -16,6 +16,7 @@ import ProfileModal from "./modals/ProfileModal.jsx";
|
||||
import ErrorModal from "./modals/ErrorModal.jsx";
|
||||
import EditHostModal from "./modals/EditHostModal.jsx";
|
||||
import NoAuthenticationModal from "./modals/NoAuthenticationModal.jsx";
|
||||
import eventBus from "./other/eventBus.jsx";
|
||||
|
||||
function App() {
|
||||
const [isAddHostHidden, setIsAddHostHidden] = useState(true);
|
||||
@@ -33,6 +34,7 @@ function App() {
|
||||
ip: "",
|
||||
user: "",
|
||||
password: "",
|
||||
sshKey: "",
|
||||
port: 22,
|
||||
authMethod: "Select Auth",
|
||||
rememberHost: true,
|
||||
@@ -44,6 +46,7 @@ function App() {
|
||||
ip: "",
|
||||
user: "",
|
||||
password: "",
|
||||
sshKey: "",
|
||||
port: 22,
|
||||
authMethod: "Select Auth",
|
||||
rememberHost: true,
|
||||
@@ -51,9 +54,16 @@ function App() {
|
||||
});
|
||||
const [isNoAuthHidden, setIsNoAuthHidden] = useState(true);
|
||||
const [authForm, setAuthForm] = useState({
|
||||
username: "",
|
||||
password: "",
|
||||
username: '',
|
||||
password: '',
|
||||
confirmPassword: ''
|
||||
});
|
||||
const [noAuthenticationForm, setNoAuthenticationForm] = useState({
|
||||
authMethod: 'Select Auth',
|
||||
password: '',
|
||||
sshKey: '',
|
||||
keyType: '',
|
||||
})
|
||||
const [isLaunchpadOpen, setIsLaunchpadOpen] = useState(false);
|
||||
const [splitTabIds, setSplitTabIds] = useState([]);
|
||||
const [isEditHostHidden, setIsEditHostHidden] = useState(true);
|
||||
@@ -137,7 +147,7 @@ function App() {
|
||||
let loginAttempts = 0;
|
||||
const maxAttempts = 50;
|
||||
let attemptLoginInterval;
|
||||
|
||||
|
||||
const loginTimeout = setTimeout(() => {
|
||||
if (isComponentMounted) {
|
||||
clearInterval(attemptLoginInterval);
|
||||
@@ -153,11 +163,11 @@ function App() {
|
||||
|
||||
const attemptLogin = () => {
|
||||
if (!isComponentMounted || isLoginInProgress) return;
|
||||
|
||||
|
||||
if (loginAttempts >= maxAttempts || userRef.current?.getUser()) {
|
||||
clearTimeout(loginTimeout);
|
||||
clearInterval(attemptLoginInterval);
|
||||
|
||||
|
||||
if (!userRef.current?.getUser()) {
|
||||
localStorage.removeItem('sessionToken');
|
||||
setIsAuthModalHidden(false);
|
||||
@@ -212,12 +222,13 @@ function App() {
|
||||
}, []);
|
||||
|
||||
const handleAddHost = () => {
|
||||
if (!addHostForm.ip?.trim() || !addHostForm.user?.trim() || !addHostForm.port) {
|
||||
alert("Please fill out all required fields (IP, User, Port).");
|
||||
return;
|
||||
}
|
||||
if (addHostForm.ip && addHostForm.user && addHostForm.port) {
|
||||
if (!addHostForm.rememberHost) {
|
||||
connectToHost();
|
||||
setIsAddHostHidden(true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (addHostForm.rememberHost) {
|
||||
if (addHostForm.authMethod === 'Select Auth') {
|
||||
alert("Please select an authentication method.");
|
||||
return;
|
||||
@@ -226,42 +237,33 @@ function App() {
|
||||
setIsNoAuthHidden(false);
|
||||
return;
|
||||
}
|
||||
if (addHostForm.authMethod === 'key' && !addHostForm.privateKey) {
|
||||
if (addHostForm.authMethod === 'sshKey' && !addHostForm.sshKey) {
|
||||
setIsNoAuthHidden(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
connectToHost();
|
||||
if (addHostForm.rememberHost) {
|
||||
connectToHost();
|
||||
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.trim(),
|
||||
user: addHostForm.user.trim(),
|
||||
ip: addHostForm.ip,
|
||||
user: addHostForm.user,
|
||||
port: String(addHostForm.port),
|
||||
password: addHostForm.rememberHost && addHostForm.authMethod === 'password' ? addHostForm.password : undefined,
|
||||
sshKey: addHostForm.rememberHost && addHostForm.authMethod === 'sshKey' ? addHostForm.sshKey : 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,
|
||||
@@ -271,21 +273,9 @@ function App() {
|
||||
setTerminals([...terminals, newTerminal]);
|
||||
setActiveTab(nextId);
|
||||
setNextId(nextId + 1);
|
||||
setAddHostForm({
|
||||
name: "",
|
||||
folder: "",
|
||||
ip: "",
|
||||
user: "",
|
||||
password: "",
|
||||
privateKey: "",
|
||||
keyType: "",
|
||||
passphrase: "",
|
||||
port: 22,
|
||||
authMethod: "Select Auth",
|
||||
rememberHost: true,
|
||||
storePassword: true
|
||||
});
|
||||
};
|
||||
setIsAddHostHidden(true);
|
||||
setAddHostForm({ name: "", folder: "", ip: "", user: "", password: "", sshKey: "", port: 22, authMethod: "Select Auth", rememberHost: true, storePassword: true });
|
||||
}
|
||||
|
||||
const handleAuthSubmit = (form) => {
|
||||
const updatedTerminals = terminals.map((terminal) => {
|
||||
@@ -295,7 +285,7 @@ function App() {
|
||||
hostConfig: {
|
||||
...terminal.hostConfig,
|
||||
password: form.password,
|
||||
rsaKey: form.rsaKey
|
||||
sshKey: form.sshKey
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -321,7 +311,7 @@ function App() {
|
||||
user: hostConfig.user.trim(),
|
||||
port: hostConfig.port || '22',
|
||||
password: hostConfig.password?.trim(),
|
||||
rsaKey: hostConfig.rsaKey?.trim(),
|
||||
sshKey: hostConfig.sshKey?.trim(),
|
||||
};
|
||||
|
||||
const newTerminal = {
|
||||
@@ -337,74 +327,71 @@ function App() {
|
||||
}
|
||||
|
||||
const handleSaveHost = () => {
|
||||
const hostConfig = {
|
||||
let hostConfig = {
|
||||
name: addHostForm.name || addHostForm.ip,
|
||||
folder: addHostForm.folder,
|
||||
ip: addHostForm.ip.trim(),
|
||||
user: addHostForm.user.trim(),
|
||||
ip: addHostForm.ip,
|
||||
user: addHostForm.user,
|
||||
password: addHostForm.authMethod === 'password' ? addHostForm.password : undefined,
|
||||
sshKey: addHostForm.authMethod === 'sshKey' ? addHostForm.sshKey : undefined,
|
||||
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, onSuccess, onFailure }) => {
|
||||
const handleLoginUser = ({ username, password, sessionToken, onSuccess, onFailure }) => {
|
||||
if (userRef.current) {
|
||||
userRef.current.loginUser({
|
||||
username,
|
||||
password,
|
||||
onSuccess: () => {
|
||||
setIsAuthModalHidden(true);
|
||||
if (onSuccess) onSuccess();
|
||||
},
|
||||
onFailure: (error) => {
|
||||
setIsAuthModalHidden(false);
|
||||
if (onFailure) onFailure(error);
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleGuestLogin = async ({ onSuccess, onFailure }) => {
|
||||
if (userRef.current) {
|
||||
try {
|
||||
await userRef.current.loginAsGuest();
|
||||
setIsAuthModalHidden(true);
|
||||
if (onSuccess) onSuccess();
|
||||
} catch (error) {
|
||||
setIsAuthModalHidden(false);
|
||||
if (onFailure) onFailure(error);
|
||||
if (sessionToken) {
|
||||
userRef.current.loginUser({
|
||||
sessionToken,
|
||||
onSuccess: () => {
|
||||
setIsAuthModalHidden(true);
|
||||
setIsLoggingIn(false);
|
||||
if (onSuccess) onSuccess();
|
||||
},
|
||||
onFailure: (error) => {
|
||||
localStorage.removeItem('sessionToken');
|
||||
setIsAuthModalHidden(false);
|
||||
setIsLoggingIn(false);
|
||||
if (onFailure) onFailure(error);
|
||||
},
|
||||
});
|
||||
} else {
|
||||
userRef.current.loginUser({
|
||||
username,
|
||||
password,
|
||||
onSuccess: () => {
|
||||
setIsAuthModalHidden(true);
|
||||
setIsLoggingIn(false);
|
||||
if (onSuccess) onSuccess();
|
||||
},
|
||||
onFailure: (error) => {
|
||||
setIsAuthModalHidden(false);
|
||||
setIsLoggingIn(false);
|
||||
if (onFailure) onFailure(error);
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleGuestLogin = () => {
|
||||
if (userRef.current) {
|
||||
userRef.current.loginAsGuest();
|
||||
}
|
||||
}
|
||||
|
||||
const handleCreateUser = ({ username, password, onSuccess, onFailure }) => {
|
||||
if (userRef.current) {
|
||||
userRef.current.createUser({
|
||||
username,
|
||||
password,
|
||||
onSuccess: () => {
|
||||
setIsAuthModalHidden(true);
|
||||
if (onSuccess) onSuccess();
|
||||
},
|
||||
onFailure: (error) => {
|
||||
setIsAuthModalHidden(false);
|
||||
if (onFailure) onFailure(error);
|
||||
},
|
||||
onSuccess,
|
||||
onFailure,
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -459,7 +446,7 @@ function App() {
|
||||
if (newConfig) {
|
||||
if (isEditing) return;
|
||||
setIsEditing(true);
|
||||
|
||||
|
||||
try {
|
||||
await userRef.current.editHost({
|
||||
oldHostConfig: oldConfig,
|
||||
@@ -476,6 +463,7 @@ function App() {
|
||||
|
||||
updateEditHostForm(oldConfig);
|
||||
} catch (error) {
|
||||
console.error('Edit failed:', error);
|
||||
setErrorMessage(`Edit failed: ${error}`);
|
||||
setIsErrorHidden(false);
|
||||
setIsEditing(false);
|
||||
@@ -661,10 +649,10 @@ function App() {
|
||||
)}
|
||||
<NoAuthenticationModal
|
||||
isHidden={isNoAuthHidden}
|
||||
setIsHidden={setIsNoAuthHidden}
|
||||
form={authForm}
|
||||
setForm={setAuthForm}
|
||||
onAuthenticate={handleAuthSubmit}
|
||||
form={noAuthenticationForm}
|
||||
setForm={setNoAuthenticationForm}
|
||||
setIsNoAuthHidden={setIsNoAuthHidden}
|
||||
handleAuthSubmit={handleAuthSubmit}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -684,7 +672,7 @@ function App() {
|
||||
setForm={setEditHostForm}
|
||||
handleEditHost={handleEditHost}
|
||||
setIsEditHostHidden={setIsEditHostHidden}
|
||||
hostConfig={currentHostConfig || {}}
|
||||
hostConfig={currentHostConfig}
|
||||
/>
|
||||
<ProfileModal
|
||||
isHidden={isProfileHidden}
|
||||
@@ -722,9 +710,9 @@ function App() {
|
||||
form={authForm}
|
||||
setForm={setAuthForm}
|
||||
handleLoginUser={handleLoginUser}
|
||||
handleGuestLogin={handleGuestLogin}
|
||||
handleCreateUser={handleCreateUser}
|
||||
setIsLoginUserHidden={setIsAuthModalHidden}
|
||||
handleGuestLogin={handleGuestLogin}
|
||||
setIsAuthModalHidden={setIsAuthModalHidden}
|
||||
/>
|
||||
|
||||
{/* User component */}
|
||||
@@ -737,8 +725,8 @@ function App() {
|
||||
}}
|
||||
onCreateSuccess={() => {
|
||||
setIsAuthModalHidden(true);
|
||||
handleLoginUser({
|
||||
username: authForm.username,
|
||||
handleLoginUser({
|
||||
username: authForm.username,
|
||||
password: authForm.password,
|
||||
onSuccess: () => {
|
||||
setIsAuthModalHidden(true);
|
||||
@@ -759,6 +747,7 @@ function App() {
|
||||
setErrorMessage(`Action failed: ${error}`);
|
||||
setIsErrorHidden(false);
|
||||
setIsLoggingIn(false);
|
||||
eventBus.emit('failedLoginUser');
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -76,7 +76,7 @@ export const NewTerminal = forwardRef(({ hostConfig, isVisible, setIsNoAuthHidde
|
||||
|
||||
socket.on("error", (err) => {
|
||||
const isAuthError = err.toLowerCase().includes("authentication") || err.toLowerCase().includes("auth");
|
||||
if (isAuthError && !hostConfig.password?.trim() && !hostConfig.rsaKey?.trim() && !authModalShown) {
|
||||
if (isAuthError && !hostConfig.password?.trim() && !hostConfig.sshKey?.trim() && !authModalShown) {
|
||||
authModalShown = true;
|
||||
setIsNoAuthHidden(false);
|
||||
}
|
||||
@@ -88,7 +88,7 @@ export const NewTerminal = forwardRef(({ hostConfig, isVisible, setIsNoAuthHidde
|
||||
resizeTerminal();
|
||||
const { cols, rows } = terminalInstance.current;
|
||||
|
||||
if (!hostConfig.password?.trim() && !hostConfig.rsaKey?.trim()) {
|
||||
if (!hostConfig.password?.trim() && !hostConfig.sshKey?.trim()) {
|
||||
setIsNoAuthHidden(false);
|
||||
return;
|
||||
}
|
||||
@@ -98,7 +98,7 @@ export const NewTerminal = forwardRef(({ hostConfig, isVisible, setIsNoAuthHidde
|
||||
user: hostConfig.user,
|
||||
port: Number(hostConfig.port) || 22,
|
||||
password: hostConfig.password?.trim(),
|
||||
rsaKey: hostConfig.rsaKey?.trim()
|
||||
sshKey: hostConfig.sshKey?.trim()
|
||||
};
|
||||
|
||||
socket.emit("connectToHost", cols, rows, sshConfig);
|
||||
@@ -172,7 +172,7 @@ export const NewTerminal = forwardRef(({ hostConfig, isVisible, setIsNoAuthHidde
|
||||
let authModalShown = false;
|
||||
|
||||
socket.on("noAuthRequired", () => {
|
||||
if (!hostConfig.password?.trim() && !hostConfig.rsaKey?.trim() && !authModalShown) {
|
||||
if (!hostConfig.password?.trim() && !hostConfig.sshKey?.trim() && !authModalShown) {
|
||||
authModalShown = true;
|
||||
setIsNoAuthHidden(false);
|
||||
}
|
||||
@@ -235,7 +235,7 @@ NewTerminal.propTypes = {
|
||||
ip: PropTypes.string.isRequired,
|
||||
user: PropTypes.string.isRequired,
|
||||
password: PropTypes.string,
|
||||
rsaKey: PropTypes.string,
|
||||
sshKey: PropTypes.string,
|
||||
port: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
|
||||
}).isRequired,
|
||||
isVisible: PropTypes.bool.isRequired,
|
||||
|
||||
@@ -186,7 +186,7 @@ export const User = forwardRef(({ onLoginSuccess, onCreateSuccess, onDeleteSucce
|
||||
user: host.config.user || '',
|
||||
port: host.config.port || '22',
|
||||
password: host.config.password || '',
|
||||
rsaKey: host.config.rsaKey || '',
|
||||
sshKey: host.config.sshKey || '',
|
||||
} : {}
|
||||
})).filter(host => host.config && host.config.ip && host.config.user);
|
||||
} else {
|
||||
|
||||
@@ -185,7 +185,7 @@ io.of('/database.io').on('connection', (socket) => {
|
||||
user: hostConfig.user.trim(),
|
||||
port: hostConfig.port || 22,
|
||||
password: hostConfig.password?.trim() || undefined,
|
||||
rsaKey: hostConfig.rsaKey?.trim() || undefined
|
||||
sshKey: hostConfig.sshKey?.trim() || undefined,
|
||||
};
|
||||
|
||||
const finalName = cleanConfig.name || cleanConfig.ip;
|
||||
@@ -415,7 +415,7 @@ io.of('/database.io').on('connection', (socket) => {
|
||||
user: newHostConfig.user.trim(),
|
||||
port: newHostConfig.port || 22,
|
||||
password: newHostConfig.password?.trim() || undefined,
|
||||
rsaKey: newHostConfig.rsaKey?.trim() || undefined
|
||||
sshKey: newHostConfig.sshKey?.trim() || undefined,
|
||||
};
|
||||
|
||||
const encryptedConfig = encryptData(cleanConfig, userId, sessionToken);
|
||||
|
||||
@@ -47,7 +47,7 @@ io.on("connection", (socket) => {
|
||||
};
|
||||
|
||||
logger.info("Connecting with config:", safeHostConfig);
|
||||
const { ip, port, user, password, privateKey, passphrase } = hostConfig;
|
||||
const { ip, port, user, password, sshKey, } = hostConfig;
|
||||
|
||||
const conn = new SSHClient();
|
||||
conn
|
||||
@@ -99,8 +99,7 @@ io.on("connection", (socket) => {
|
||||
port: port,
|
||||
username: user,
|
||||
password: password,
|
||||
privateKey: privateKey ? Buffer.from(privateKey) : undefined,
|
||||
passphrase: passphrase,
|
||||
sshKey: sshKey ? Buffer.from(sshKey) : undefined,
|
||||
tryKeyboard: true,
|
||||
algorithms: {
|
||||
kex: [
|
||||
|
||||
@@ -24,7 +24,6 @@ import VisibilityOff from '@mui/icons-material/VisibilityOff';
|
||||
|
||||
const AddHostModal = ({ isHidden, form, setForm, handleAddHost, setIsAddHostHidden }) => {
|
||||
const [showPassword, setShowPassword] = useState(false);
|
||||
const [showPassphrase, setShowPassphrase] = useState(false);
|
||||
const [activeTab, setActiveTab] = useState(0);
|
||||
|
||||
const handleFileChange = (e) => {
|
||||
@@ -39,7 +38,7 @@ const AddHostModal = ({ isHidden, form, setForm, handleAddHost, setIsAddHostHidd
|
||||
'.ppk': 'PPK'
|
||||
};
|
||||
|
||||
const isValidKeyFile = Object.keys(supportedKeyTypes).some(ext =>
|
||||
const isValidKeyFile = Object.keys(supportedKeyTypes).some(ext =>
|
||||
file.name.toLowerCase().includes(ext) || file.name.endsWith('.pub')
|
||||
);
|
||||
|
||||
@@ -48,8 +47,7 @@ const AddHostModal = ({ isHidden, form, setForm, handleAddHost, setIsAddHostHidd
|
||||
reader.onload = (event) => {
|
||||
const keyContent = event.target.result;
|
||||
let keyType = 'UNKNOWN';
|
||||
|
||||
// Detect key type from content
|
||||
|
||||
if (keyContent.includes('BEGIN RSA PRIVATE KEY') || keyContent.includes('BEGIN RSA PUBLIC KEY')) {
|
||||
keyType = 'RSA';
|
||||
} else if (keyContent.includes('BEGIN OPENSSH PRIVATE KEY') && keyContent.includes('ssh-ed25519')) {
|
||||
@@ -60,11 +58,11 @@ const AddHostModal = ({ isHidden, form, setForm, handleAddHost, setIsAddHostHidd
|
||||
keyType = 'DSA';
|
||||
}
|
||||
|
||||
setForm(prev => ({
|
||||
...prev,
|
||||
privateKey: keyContent,
|
||||
setForm(prev => ({
|
||||
...prev,
|
||||
sshKey: keyContent,
|
||||
keyType: keyType,
|
||||
authMethod: 'key'
|
||||
authMethod: 'sshKey'
|
||||
}));
|
||||
};
|
||||
reader.readAsText(file);
|
||||
@@ -78,30 +76,25 @@ const AddHostModal = ({ isHidden, form, setForm, handleAddHost, setIsAddHostHidd
|
||||
...prev,
|
||||
authMethod: newMethod,
|
||||
password: "",
|
||||
privateKey: "",
|
||||
sshKey: "",
|
||||
keyType: "",
|
||||
passphrase: ""
|
||||
}));
|
||||
};
|
||||
|
||||
const isFormValid = () => {
|
||||
const { ip, user, port, authMethod, password, privateKey } = form;
|
||||
|
||||
// Basic validation for required fields
|
||||
const { ip, user, port, authMethod, password, sshKey } = form;
|
||||
|
||||
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 (authMethod === 'Select Auth') return false;
|
||||
if (authMethod === 'password' && !password?.trim()) return false;
|
||||
if (authMethod === 'key' && !privateKey?.trim()) return false;
|
||||
if (authMethod === 'sshKey' && !sshKey?.trim()) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -114,6 +107,7 @@ const AddHostModal = ({ isHidden, form, setForm, handleAddHost, setIsAddHostHidd
|
||||
return;
|
||||
}
|
||||
handleAddHost();
|
||||
setActiveTab(0);
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -144,10 +138,10 @@ const AddHostModal = ({ isHidden, form, setForm, handleAddHost, setIsAddHostHidd
|
||||
mx: 2,
|
||||
}}
|
||||
>
|
||||
<Tabs
|
||||
value={activeTab}
|
||||
<Tabs
|
||||
value={activeTab}
|
||||
onChange={(e, val) => setActiveTab(val)}
|
||||
sx={{
|
||||
sx={{
|
||||
width: '100%',
|
||||
mb: 0,
|
||||
backgroundColor: theme.palette.general.tertiary,
|
||||
@@ -211,8 +205,8 @@ const AddHostModal = ({ isHidden, form, setForm, handleAddHost, setIsAddHostHidd
|
||||
<FormLabel>Remember Host</FormLabel>
|
||||
<Checkbox
|
||||
checked={Boolean(form.rememberHost)}
|
||||
onChange={(e) => setForm({
|
||||
...form,
|
||||
onChange={(e) => setForm({
|
||||
...form,
|
||||
rememberHost: e.target.checked,
|
||||
})}
|
||||
sx={{
|
||||
@@ -284,7 +278,7 @@ const AddHostModal = ({ isHidden, form, setForm, handleAddHost, setIsAddHostHidd
|
||||
>
|
||||
<Option value="Select Auth" disabled>Select Auth</Option>
|
||||
<Option value="password">Password</Option>
|
||||
<Option value="key">SSH Key</Option>
|
||||
<Option value="sshKey">SSH Key</Option>
|
||||
</Select>
|
||||
</FormControl>
|
||||
|
||||
@@ -315,9 +309,9 @@ const AddHostModal = ({ isHidden, form, setForm, handleAddHost, setIsAddHostHidd
|
||||
</FormControl>
|
||||
)}
|
||||
|
||||
{form.authMethod === 'key' && (
|
||||
{form.authMethod === 'sshKey' && (
|
||||
<Stack spacing={2}>
|
||||
<FormControl error={!form.privateKey}>
|
||||
<FormControl error={!form.sshKey}>
|
||||
<FormLabel>SSH Key</FormLabel>
|
||||
<Button
|
||||
component="label"
|
||||
@@ -334,7 +328,7 @@ const AddHostModal = ({ isHidden, form, setForm, handleAddHost, setIsAddHostHidd
|
||||
},
|
||||
}}
|
||||
>
|
||||
{form.privateKey ? `Change ${form.keyType || 'SSH'} Key File` : 'Upload SSH Key File'}
|
||||
{form.sshKey ? `Change ${form.keyType || 'SSH'} Key File` : 'Upload SSH Key File'}
|
||||
<Input
|
||||
type="file"
|
||||
onChange={handleFileChange}
|
||||
@@ -342,32 +336,6 @@ const AddHostModal = ({ isHidden, form, setForm, handleAddHost, setIsAddHostHidd
|
||||
/>
|
||||
</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>
|
||||
)}
|
||||
|
||||
|
||||
@@ -1,159 +1,267 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import { CssVarsProvider } from '@mui/joy/styles';
|
||||
import { Modal, Button, FormControl, FormLabel, Input, Stack, ModalDialog, IconButton, Tabs, TabList, Tab, TabPanel } from '@mui/joy';
|
||||
import {
|
||||
Modal,
|
||||
Button,
|
||||
FormControl,
|
||||
FormLabel,
|
||||
Input,
|
||||
Stack,
|
||||
DialogContent,
|
||||
ModalDialog,
|
||||
IconButton,
|
||||
Tabs,
|
||||
TabList,
|
||||
Tab,
|
||||
TabPanel
|
||||
} from '@mui/joy';
|
||||
import theme from '/src/theme';
|
||||
import { useEffect, useState } from 'react';
|
||||
import Visibility from '@mui/icons-material/Visibility';
|
||||
import VisibilityOff from '@mui/icons-material/VisibilityOff';
|
||||
import eventBus from '/src/other/eventBus';
|
||||
|
||||
const AuthModal = ({ isHidden, form, setForm, handleLoginUser, handleGuestLogin, handleCreateUser, setIsLoginUserHidden }) => {
|
||||
const [showPassword, setShowPassword] = useState(false);
|
||||
const [confirmPassword, setConfirmPassword] = useState('');
|
||||
const AuthModal = ({
|
||||
isHidden,
|
||||
form,
|
||||
setForm,
|
||||
handleLoginUser,
|
||||
handleCreateUser,
|
||||
handleGuestLogin,
|
||||
setIsAuthModalHidden
|
||||
}) => {
|
||||
const [activeTab, setActiveTab] = useState(0);
|
||||
const [showPassword, setShowPassword] = useState(false);
|
||||
const [showConfirmPassword, setShowConfirmPassword] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
const isLoginFormValid = () => {
|
||||
return form.username?.trim() && form.password?.trim();
|
||||
useEffect(() => {
|
||||
const loginErrorHandler = () => setIsLoading(false);
|
||||
eventBus.on('failedLoginUser', loginErrorHandler);
|
||||
return () => eventBus.off('failedLoginUser', loginErrorHandler);
|
||||
}, []);
|
||||
|
||||
const resetForm = () => {
|
||||
setForm({ username: '', password: '' });
|
||||
setShowPassword(false);
|
||||
setShowConfirmPassword(false);
|
||||
setIsLoading(false);
|
||||
};
|
||||
|
||||
const isCreateFormValid = () => {
|
||||
return form.username?.trim() && form.password?.trim() && confirmPassword?.trim() && form.password === confirmPassword;
|
||||
};
|
||||
|
||||
const handleLogin = () => {
|
||||
if (!isLoginFormValid()) {
|
||||
alert("Please fill out all fields");
|
||||
return;
|
||||
}
|
||||
|
||||
const handleLogin = async () => {
|
||||
setIsLoading(true);
|
||||
handleLoginUser({
|
||||
username: form.username.trim(),
|
||||
password: form.password.trim(),
|
||||
onSuccess: () => {
|
||||
setIsLoading(false);
|
||||
setIsLoginUserHidden(true);
|
||||
},
|
||||
onFailure: (error) => {
|
||||
setIsLoading(false);
|
||||
alert(error);
|
||||
}
|
||||
});
|
||||
try {
|
||||
await handleLoginUser({
|
||||
...form,
|
||||
onSuccess: () => {
|
||||
setIsLoading(false);
|
||||
setIsAuthModalHidden(true);
|
||||
},
|
||||
onFailure: () => setIsLoading(false),
|
||||
});
|
||||
} catch (error) {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleCreate = () => {
|
||||
if (!isCreateFormValid()) {
|
||||
alert("Please fill out all fields and ensure passwords match");
|
||||
return;
|
||||
}
|
||||
|
||||
const handleCreate = async () => {
|
||||
setIsLoading(true);
|
||||
handleCreateUser({
|
||||
username: form.username.trim(),
|
||||
password: form.password.trim(),
|
||||
onSuccess: () => {
|
||||
setIsLoading(false);
|
||||
setIsLoginUserHidden(true);
|
||||
},
|
||||
onFailure: (error) => {
|
||||
setIsLoading(false);
|
||||
alert(error);
|
||||
}
|
||||
});
|
||||
try {
|
||||
await handleCreateUser({
|
||||
...form,
|
||||
onSuccess: () => {
|
||||
setIsLoading(false);
|
||||
setActiveTab(0);
|
||||
setIsAuthModalHidden(true);
|
||||
},
|
||||
onFailure: () => setIsLoading(false),
|
||||
});
|
||||
} catch (error) {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleGuest = async () => {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
await handleGuestLogin({
|
||||
onSuccess: () => {
|
||||
setIsLoading(false);
|
||||
setIsAuthModalHidden(true);
|
||||
},
|
||||
onFailure: () => setIsLoading(false)
|
||||
});
|
||||
} catch (error) {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (isHidden) {
|
||||
setForm({ username: '', password: '' });
|
||||
setConfirmPassword('');
|
||||
setIsLoading(false);
|
||||
setActiveTab(0);
|
||||
}
|
||||
if (isHidden) resetForm();
|
||||
}, [isHidden]);
|
||||
|
||||
const isLoginValid = !!form.username && !!form.password;
|
||||
const isCreateValid = isLoginValid && form.password === form.confirmPassword;
|
||||
|
||||
return (
|
||||
<CssVarsProvider theme={theme}>
|
||||
<Modal open={!isHidden} onClose={() => setIsLoginUserHidden(true)}>
|
||||
<ModalDialog layout="center" sx={{ backgroundColor: theme.palette.general.tertiary }}>
|
||||
<Tabs value={activeTab} onChange={(e, val) => setActiveTab(val)}>
|
||||
<TabList>
|
||||
<Tab>Login</Tab>
|
||||
<Tab>Create Account</Tab>
|
||||
<Modal open={!isHidden} onClose={() => setIsAuthModalHidden(true)}>
|
||||
<ModalDialog
|
||||
layout="center"
|
||||
variant="outlined"
|
||||
sx={{
|
||||
backgroundColor: theme.palette.general.tertiary,
|
||||
borderColor: theme.palette.general.secondary,
|
||||
color: theme.palette.text.primary,
|
||||
padding: 0,
|
||||
borderRadius: 10,
|
||||
maxWidth: '400px',
|
||||
width: '100%',
|
||||
overflow: 'hidden',
|
||||
}}
|
||||
>
|
||||
<Tabs
|
||||
value={activeTab}
|
||||
onChange={(e, val) => setActiveTab(val)}
|
||||
sx={{
|
||||
width: '100%',
|
||||
backgroundColor: theme.palette.general.tertiary,
|
||||
}}
|
||||
>
|
||||
<TabList
|
||||
sx={{
|
||||
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 sx={{ flex: 1 }}>Login</Tab>
|
||||
<Tab sx={{ flex: 1 }}>Create</Tab>
|
||||
</TabList>
|
||||
|
||||
<div style={{ padding: '24px', backgroundColor: theme.palette.general.tertiary }}>
|
||||
<TabPanel value={0}>
|
||||
<Stack spacing={2}>
|
||||
<DialogContent sx={{ padding: 3, backgroundColor: theme.palette.general.tertiary }}>
|
||||
<TabPanel value={0} sx={{ p: 0 }}>
|
||||
<Stack spacing={2} component="form" onSubmit={(e) => { e.preventDefault(); handleLogin(); }}>
|
||||
<FormControl>
|
||||
<FormLabel>Username</FormLabel>
|
||||
<Input
|
||||
value={form.username}
|
||||
onChange={(event) => setForm({ ...form, username: event.target.value })}
|
||||
disabled={isLoading}
|
||||
value={form.username}
|
||||
onChange={(e) => setForm({ ...form, username: e.target.value })}
|
||||
sx={inputStyle}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormControl>
|
||||
<FormLabel>Password</FormLabel>
|
||||
<div style={{ display: 'flex', alignItems: 'center' }}>
|
||||
<Input
|
||||
disabled={isLoading}
|
||||
type={showPassword ? 'text' : 'password'}
|
||||
value={form.password}
|
||||
onChange={(event) => setForm({ ...form, password: event.target.value })}
|
||||
disabled={isLoading}
|
||||
onChange={(e) => setForm({ ...form, password: e.target.value })}
|
||||
sx={{ ...inputStyle, flex: 1 }}
|
||||
/>
|
||||
<IconButton onClick={() => setShowPassword(!showPassword)}>
|
||||
<IconButton
|
||||
disabled={isLoading}
|
||||
onClick={() => setShowPassword(!showPassword)}
|
||||
sx={iconButtonStyle}
|
||||
>
|
||||
{showPassword ? <VisibilityOff /> : <Visibility />}
|
||||
</IconButton>
|
||||
</div>
|
||||
</FormControl>
|
||||
<Button onClick={handleLogin} disabled={!isLoginFormValid() || isLoading}>
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={!isLoginValid || isLoading}
|
||||
sx={buttonStyle}
|
||||
>
|
||||
{isLoading ? "Logging in..." : "Login"}
|
||||
</Button>
|
||||
<Button onClick={handleGuestLogin} disabled={isLoading}>
|
||||
{isLoading ? "Logging in as guest..." : "Login as Guest"}
|
||||
<Button
|
||||
disabled={isLoading}
|
||||
onClick={handleGuest}
|
||||
sx={buttonStyle}
|
||||
>
|
||||
{isLoading ? "Logging in..." : "Continue as Guest"}
|
||||
</Button>
|
||||
</Stack>
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel value={1}>
|
||||
<Stack spacing={2}>
|
||||
<TabPanel value={1} sx={{ p: 0 }}>
|
||||
<Stack spacing={2} component="form" onSubmit={(e) => { e.preventDefault(); handleCreate(); }}>
|
||||
<FormControl>
|
||||
<FormLabel>Username</FormLabel>
|
||||
<Input
|
||||
value={form.username}
|
||||
onChange={(event) => setForm({ ...form, username: event.target.value })}
|
||||
disabled={isLoading}
|
||||
value={form.username}
|
||||
onChange={(e) => setForm({ ...form, username: e.target.value })}
|
||||
sx={inputStyle}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormControl>
|
||||
<FormLabel>Password</FormLabel>
|
||||
<div style={{ display: 'flex', alignItems: 'center' }}>
|
||||
<Input
|
||||
disabled={isLoading}
|
||||
type={showPassword ? 'text' : 'password'}
|
||||
value={form.password}
|
||||
onChange={(event) => setForm({ ...form, password: event.target.value })}
|
||||
disabled={isLoading}
|
||||
onChange={(e) => setForm({ ...form, password: e.target.value })}
|
||||
sx={{ ...inputStyle, flex: 1 }}
|
||||
/>
|
||||
<IconButton onClick={() => setShowPassword(!showPassword)}>
|
||||
<IconButton
|
||||
disabled={isLoading}
|
||||
onClick={() => setShowPassword(!showPassword)}
|
||||
sx={iconButtonStyle}
|
||||
>
|
||||
{showPassword ? <VisibilityOff /> : <Visibility />}
|
||||
</IconButton>
|
||||
</div>
|
||||
</FormControl>
|
||||
<FormControl>
|
||||
<FormLabel>Confirm Password</FormLabel>
|
||||
<Input
|
||||
type={showPassword ? 'text' : 'password'}
|
||||
value={confirmPassword}
|
||||
onChange={(event) => setConfirmPassword(event.target.value)}
|
||||
disabled={isLoading}
|
||||
/>
|
||||
<div style={{ display: 'flex', alignItems: 'center' }}>
|
||||
<Input
|
||||
disabled={isLoading}
|
||||
type={showConfirmPassword ? 'text' : 'password'}
|
||||
value={form.confirmPassword || ''}
|
||||
onChange={(e) => setForm({ ...form, confirmPassword: e.target.value })}
|
||||
sx={{ ...inputStyle, flex: 1 }}
|
||||
/>
|
||||
<IconButton
|
||||
disabled={isLoading}
|
||||
onClick={() => setShowConfirmPassword(!showConfirmPassword)}
|
||||
sx={iconButtonStyle}
|
||||
>
|
||||
{showConfirmPassword ? <VisibilityOff /> : <Visibility />}
|
||||
</IconButton>
|
||||
</div>
|
||||
</FormControl>
|
||||
<Button onClick={handleCreate} disabled={!isCreateFormValid() || isLoading}>
|
||||
{isLoading ? "Creating account..." : "Create Account"}
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={!isCreateValid || isLoading}
|
||||
sx={buttonStyle}
|
||||
>
|
||||
{isLoading ? "Creating..." : "Create Account"}
|
||||
</Button>
|
||||
</Stack>
|
||||
</TabPanel>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Tabs>
|
||||
</ModalDialog>
|
||||
</Modal>
|
||||
@@ -161,14 +269,38 @@ const AuthModal = ({ isHidden, form, setForm, handleLoginUser, handleGuestLogin,
|
||||
);
|
||||
};
|
||||
|
||||
const inputStyle = {
|
||||
backgroundColor: theme.palette.general.primary,
|
||||
color: theme.palette.text.primary,
|
||||
'&:disabled': {
|
||||
opacity: 0.5,
|
||||
backgroundColor: theme.palette.general.primary,
|
||||
},
|
||||
};
|
||||
|
||||
const iconButtonStyle = {
|
||||
color: theme.palette.text.primary,
|
||||
marginLeft: 1,
|
||||
'&:disabled': { opacity: 0.5 },
|
||||
};
|
||||
|
||||
const buttonStyle = {
|
||||
backgroundColor: theme.palette.general.primary,
|
||||
'&:hover': { backgroundColor: theme.palette.general.disabled },
|
||||
'&:disabled': {
|
||||
opacity: 0.5,
|
||||
backgroundColor: theme.palette.general.primary,
|
||||
},
|
||||
};
|
||||
|
||||
AuthModal.propTypes = {
|
||||
isHidden: PropTypes.bool.isRequired,
|
||||
form: PropTypes.object.isRequired,
|
||||
setForm: PropTypes.func.isRequired,
|
||||
handleLoginUser: PropTypes.func.isRequired,
|
||||
handleGuestLogin: PropTypes.func.isRequired,
|
||||
handleCreateUser: PropTypes.func.isRequired,
|
||||
setIsLoginUserHidden: PropTypes.func.isRequired,
|
||||
handleGuestLogin: PropTypes.func.isRequired,
|
||||
setIsAuthModalHidden: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default AuthModal;
|
||||
@@ -1,207 +0,0 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import { CssVarsProvider } from '@mui/joy/styles';
|
||||
import { Modal, Button, FormControl, FormLabel, Input, Stack, DialogTitle, DialogContent, ModalDialog, IconButton } from '@mui/joy';
|
||||
import theme from '/src/theme';
|
||||
import { useEffect, useState } from 'react';
|
||||
import Visibility from '@mui/icons-material/Visibility';
|
||||
import VisibilityOff from '@mui/icons-material/VisibilityOff';
|
||||
|
||||
const CreateUserModal = ({ isHidden, form, setForm, handleCreateUser, setIsCreateUserHidden, setIsLoginUserHidden }) => {
|
||||
const [confirmPassword, setConfirmPassword] = useState('');
|
||||
const [showPassword, setShowPassword] = useState(false);
|
||||
const [showConfirmPassword, setShowConfirmPassword] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
const isFormValid = () => {
|
||||
if (!form.username || !form.password || form.password !== confirmPassword) return false;
|
||||
return true;
|
||||
};
|
||||
|
||||
const handleCreate = async () => {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
await handleCreateUser({
|
||||
...form,
|
||||
onSuccess: () => setIsLoading(false),
|
||||
onFailure: () => setIsLoading(false)
|
||||
});
|
||||
} catch (error) {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (isHidden) {
|
||||
setForm({ username: '', password: '' });
|
||||
setConfirmPassword('');
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [isHidden]);
|
||||
|
||||
return (
|
||||
<CssVarsProvider theme={theme}>
|
||||
<Modal open={!isHidden} onClose={() => {}}>
|
||||
<ModalDialog
|
||||
layout="center"
|
||||
sx={{
|
||||
backgroundColor: theme.palette.general.tertiary,
|
||||
borderColor: theme.palette.general.secondary,
|
||||
color: theme.palette.text.primary,
|
||||
padding: 3,
|
||||
borderRadius: 10,
|
||||
width: "auto",
|
||||
maxWidth: "90vw",
|
||||
minWidth: "fit-content",
|
||||
overflow: "hidden",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<DialogTitle>Create</DialogTitle>
|
||||
<DialogContent>
|
||||
<form
|
||||
onSubmit={(event) => {
|
||||
event.preventDefault();
|
||||
if (isFormValid() && !isLoading) handleCreate();
|
||||
}}
|
||||
>
|
||||
<Stack spacing={2} sx={{ width: "100%", maxWidth: "100%", overflow: "hidden" }}>
|
||||
<FormControl>
|
||||
<FormLabel>Username</FormLabel>
|
||||
<Input
|
||||
disabled={isLoading}
|
||||
value={form.username}
|
||||
onChange={(event) => setForm({ ...form, username: event.target.value })}
|
||||
sx={{
|
||||
backgroundColor: theme.palette.general.primary,
|
||||
color: theme.palette.text.primary,
|
||||
'&:disabled': {
|
||||
opacity: 0.5,
|
||||
backgroundColor: theme.palette.general.primary,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormControl>
|
||||
<FormLabel>Password</FormLabel>
|
||||
<div style={{ display: 'flex', alignItems: 'center' }}>
|
||||
<Input
|
||||
disabled={isLoading}
|
||||
type={showPassword ? 'text' : 'password'}
|
||||
value={form.password}
|
||||
onChange={(event) => setForm({ ...form, password: event.target.value })}
|
||||
sx={{
|
||||
backgroundColor: theme.palette.general.primary,
|
||||
color: theme.palette.text.primary,
|
||||
flex: 1,
|
||||
'&:disabled': {
|
||||
opacity: 0.5,
|
||||
backgroundColor: theme.palette.general.primary,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<IconButton
|
||||
disabled={isLoading}
|
||||
onClick={() => setShowPassword(!showPassword)}
|
||||
sx={{
|
||||
color: theme.palette.text.primary,
|
||||
marginLeft: 1,
|
||||
'&:disabled': {
|
||||
opacity: 0.5,
|
||||
},
|
||||
}}
|
||||
>
|
||||
{showPassword ? <VisibilityOff /> : <Visibility />}
|
||||
</IconButton>
|
||||
</div>
|
||||
</FormControl>
|
||||
<FormControl>
|
||||
<FormLabel>Confirm Password</FormLabel>
|
||||
<div style={{ display: 'flex', alignItems: 'center' }}>
|
||||
<Input
|
||||
disabled={isLoading}
|
||||
type={showConfirmPassword ? 'text' : 'password'}
|
||||
value={confirmPassword}
|
||||
onChange={(event) => setConfirmPassword(event.target.value)}
|
||||
sx={{
|
||||
backgroundColor: theme.palette.general.primary,
|
||||
color: theme.palette.text.primary,
|
||||
flex: 1,
|
||||
'&:disabled': {
|
||||
opacity: 0.5,
|
||||
backgroundColor: theme.palette.general.primary,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<IconButton
|
||||
disabled={isLoading}
|
||||
onClick={() => setShowConfirmPassword(!showConfirmPassword)}
|
||||
sx={{
|
||||
color: theme.palette.text.primary,
|
||||
marginLeft: 1,
|
||||
'&:disabled': {
|
||||
opacity: 0.5,
|
||||
},
|
||||
}}
|
||||
>
|
||||
{showConfirmPassword ? <VisibilityOff /> : <Visibility />}
|
||||
</IconButton>
|
||||
</div>
|
||||
</FormControl>
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={!isFormValid() || isLoading}
|
||||
sx={{
|
||||
backgroundColor: theme.palette.general.primary,
|
||||
'&:hover': {
|
||||
backgroundColor: theme.palette.general.disabled,
|
||||
},
|
||||
'&:disabled': {
|
||||
opacity: 0.5,
|
||||
backgroundColor: theme.palette.general.primary,
|
||||
},
|
||||
}}
|
||||
>
|
||||
{isLoading ? "Creating user..." : "Create"}
|
||||
</Button>
|
||||
<Button
|
||||
disabled={isLoading}
|
||||
onClick={() => {
|
||||
setForm({ username: '', password: '' });
|
||||
setConfirmPassword('');
|
||||
setIsCreateUserHidden(true);
|
||||
setIsLoginUserHidden(false);
|
||||
}}
|
||||
sx={{
|
||||
backgroundColor: theme.palette.general.primary,
|
||||
'&:hover': {
|
||||
backgroundColor: theme.palette.general.disabled,
|
||||
},
|
||||
'&:disabled': {
|
||||
opacity: 0.5,
|
||||
backgroundColor: theme.palette.general.primary,
|
||||
},
|
||||
}}
|
||||
>
|
||||
Back
|
||||
</Button>
|
||||
</Stack>
|
||||
</form>
|
||||
</DialogContent>
|
||||
</ModalDialog>
|
||||
</Modal>
|
||||
</CssVarsProvider>
|
||||
);
|
||||
};
|
||||
|
||||
CreateUserModal.propTypes = {
|
||||
isHidden: PropTypes.bool.isRequired,
|
||||
form: PropTypes.object.isRequired,
|
||||
setForm: PropTypes.func.isRequired,
|
||||
handleCreateUser: PropTypes.func.isRequired,
|
||||
setIsCreateUserHidden: PropTypes.func.isRequired,
|
||||
setIsLoginUserHidden: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default CreateUserModal;
|
||||
@@ -8,8 +8,6 @@ import {
|
||||
FormLabel,
|
||||
Input,
|
||||
Stack,
|
||||
DialogTitle,
|
||||
DialogContent,
|
||||
ModalDialog,
|
||||
Select,
|
||||
Option,
|
||||
@@ -32,15 +30,13 @@ const EditHostModal = ({ isHidden, hostConfig, setIsEditHostHidden, handleEditHo
|
||||
user: hostConfig?.user || '',
|
||||
port: hostConfig?.port || '',
|
||||
password: '',
|
||||
privateKey: hostConfig?.privateKey || '',
|
||||
sshKey: hostConfig?.sshKey || '',
|
||||
keyType: hostConfig?.keyType || '',
|
||||
passphrase: '',
|
||||
authMethod: hostConfig?.authMethod || 'Select Auth',
|
||||
storePassword: true,
|
||||
rememberHost: true
|
||||
});
|
||||
const [showPassword, setShowPassword] = useState(false);
|
||||
const [showPassphrase, setShowPassphrase] = useState(false);
|
||||
const [activeTab, setActiveTab] = useState(0);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
@@ -52,13 +48,12 @@ const EditHostModal = ({ isHidden, hostConfig, setIsEditHostHidden, handleEditHo
|
||||
ip: hostConfig.ip || '',
|
||||
user: hostConfig.user || '',
|
||||
password: hostConfig.password || '',
|
||||
privateKey: hostConfig.privateKey || '',
|
||||
sshKey: hostConfig.sshKey || '',
|
||||
keyType: hostConfig.keyType || '',
|
||||
passphrase: hostConfig.passphrase || '',
|
||||
port: hostConfig.port || 22,
|
||||
authMethod: hostConfig.password ? 'password' : hostConfig.privateKey ? 'key' : 'Select Auth',
|
||||
authMethod: hostConfig.password ? 'password' : hostConfig.sshKey ? 'key' : 'Select Auth',
|
||||
rememberHost: true,
|
||||
storePassword: !!(hostConfig.password || hostConfig.privateKey),
|
||||
storePassword: !!(hostConfig.password || hostConfig.sshKey),
|
||||
});
|
||||
}
|
||||
}, [isHidden, hostConfig]);
|
||||
@@ -84,8 +79,7 @@ const EditHostModal = ({ isHidden, hostConfig, setIsEditHostHidden, handleEditHo
|
||||
reader.onload = (evt) => {
|
||||
const keyContent = evt.target.result;
|
||||
let keyType = 'UNKNOWN';
|
||||
|
||||
// Detect key type from content
|
||||
|
||||
if (keyContent.includes('BEGIN RSA PRIVATE KEY') || keyContent.includes('BEGIN RSA PUBLIC KEY')) {
|
||||
keyType = 'RSA';
|
||||
} else if (keyContent.includes('BEGIN OPENSSH PRIVATE KEY') && keyContent.includes('ssh-ed25519')) {
|
||||
@@ -98,7 +92,7 @@ const EditHostModal = ({ isHidden, hostConfig, setIsEditHostHidden, handleEditHo
|
||||
|
||||
setForm((prev) => ({
|
||||
...prev,
|
||||
privateKey: keyContent,
|
||||
sshKey: keyContent,
|
||||
keyType: keyType,
|
||||
authMethod: 'key'
|
||||
}));
|
||||
@@ -121,27 +115,23 @@ const EditHostModal = ({ isHidden, hostConfig, setIsEditHostHidden, handleEditHo
|
||||
...prev,
|
||||
storePassword: Boolean(checked),
|
||||
password: checked ? prev.password : "",
|
||||
privateKey: checked ? prev.privateKey : "",
|
||||
passphrase: checked ? prev.passphrase : "",
|
||||
sshKey: checked ? prev.sshKey : "",
|
||||
authMethod: checked ? prev.authMethod : "Select Auth"
|
||||
}));
|
||||
};
|
||||
|
||||
const isFormValid = () => {
|
||||
const { ip, user, port, authMethod, password, privateKey } = form;
|
||||
|
||||
// Basic validation for required fields
|
||||
const { ip, user, port, authMethod, password, sshKey } = form;
|
||||
|
||||
if (!ip?.trim() || !user?.trim() || !port) return false;
|
||||
|
||||
// Port validation
|
||||
|
||||
const portNum = Number(port);
|
||||
if (isNaN(portNum) || portNum < 1 || portNum > 65535) 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;
|
||||
if (authMethod === 'sshKey' && !sshKey?.trim()) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -150,7 +140,7 @@ const EditHostModal = ({ isHidden, hostConfig, setIsEditHostHidden, handleEditHo
|
||||
const handleSubmit = async (event) => {
|
||||
event.preventDefault();
|
||||
if (isLoading) return;
|
||||
|
||||
|
||||
setIsLoading(true);
|
||||
try {
|
||||
const newConfig = {
|
||||
@@ -165,9 +155,8 @@ const EditHostModal = ({ isHidden, hostConfig, setIsEditHostHidden, handleEditHo
|
||||
if (form.authMethod === 'password') {
|
||||
newConfig.password = form.password;
|
||||
} else if (form.authMethod === 'key') {
|
||||
newConfig.privateKey = form.privateKey;
|
||||
newConfig.sshKey = form.sshKey;
|
||||
newConfig.keyType = form.keyType;
|
||||
newConfig.passphrase = form.passphrase;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -378,7 +367,7 @@ const EditHostModal = ({ isHidden, hostConfig, setIsEditHostHidden, handleEditHo
|
||||
|
||||
{form.authMethod === 'key' && (
|
||||
<Stack spacing={2}>
|
||||
<FormControl error={form.storePassword && !form.privateKey}>
|
||||
<FormControl error={form.storePassword && !form.sshKey}>
|
||||
<FormLabel>SSH Key</FormLabel>
|
||||
<Button
|
||||
component="label"
|
||||
@@ -395,14 +384,14 @@ const EditHostModal = ({ isHidden, hostConfig, setIsEditHostHidden, handleEditHo
|
||||
},
|
||||
}}
|
||||
>
|
||||
{form.privateKey ? `Change ${form.keyType || 'SSH'} Key File` : 'Upload SSH Key File'}
|
||||
{form.sshKey ? `Change ${form.keyType || 'SSH'} Key File` : 'Upload SSH Key File'}
|
||||
<Input
|
||||
type="file"
|
||||
onChange={handleFileChange}
|
||||
sx={{ display: 'none' }}
|
||||
/>
|
||||
</Button>
|
||||
{hostConfig?.privateKey && !form.privateKey && (
|
||||
{hostConfig?.sshKey && !form.sshKey && (
|
||||
<FormLabel
|
||||
sx={{
|
||||
color: theme.palette.text.secondary,
|
||||
@@ -416,32 +405,6 @@ const EditHostModal = ({ isHidden, hostConfig, setIsEditHostHidden, handleEditHo
|
||||
</FormLabel>
|
||||
)}
|
||||
</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>
|
||||
)}
|
||||
</>
|
||||
@@ -452,7 +415,7 @@ const EditHostModal = ({ isHidden, hostConfig, setIsEditHostHidden, handleEditHo
|
||||
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
disabled={isLoading}
|
||||
disabled={isLoading || !isFormValid()}
|
||||
sx={{
|
||||
backgroundColor: theme.palette.general.primary,
|
||||
color: theme.palette.text.primary,
|
||||
|
||||
@@ -1,199 +0,0 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import { CssVarsProvider } from '@mui/joy/styles';
|
||||
import { Modal, Button, FormControl, FormLabel, Input, Stack, DialogTitle, DialogContent, ModalDialog, IconButton } from '@mui/joy';
|
||||
import theme from '/src/theme';
|
||||
import { useEffect, useState } from 'react';
|
||||
import Visibility from '@mui/icons-material/Visibility';
|
||||
import VisibilityOff from '@mui/icons-material/VisibilityOff';
|
||||
|
||||
const LoginUserModal = ({ isHidden, form, setForm, handleLoginUser, handleGuestLogin, setIsLoginUserHidden, setIsCreateUserHidden }) => {
|
||||
const [showPassword, setShowPassword] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
const isFormValid = () => {
|
||||
if (!form.username || !form.password) return false;
|
||||
return true;
|
||||
};
|
||||
|
||||
const handleLogin = async () => {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
await handleLoginUser({
|
||||
...form,
|
||||
onSuccess: () => setIsLoading(false),
|
||||
onFailure: () => setIsLoading(false)
|
||||
});
|
||||
} catch (error) {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleGuest = async () => {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
await handleGuestLogin({
|
||||
onSuccess: () => setIsLoading(false),
|
||||
onFailure: () => setIsLoading(false)
|
||||
});
|
||||
} catch (error) {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (isHidden) {
|
||||
setForm({ username: '', password: '' });
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, [isHidden]);
|
||||
|
||||
return (
|
||||
<CssVarsProvider theme={theme}>
|
||||
<Modal open={!isHidden} onClose={() => {}}>
|
||||
<ModalDialog
|
||||
layout="center"
|
||||
sx={{
|
||||
backgroundColor: theme.palette.general.tertiary,
|
||||
borderColor: theme.palette.general.secondary,
|
||||
color: theme.palette.text.primary,
|
||||
padding: 3,
|
||||
borderRadius: 10,
|
||||
width: "auto",
|
||||
maxWidth: "90vw",
|
||||
minWidth: "fit-content",
|
||||
overflow: "hidden",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<DialogTitle>Login</DialogTitle>
|
||||
<DialogContent>
|
||||
<form
|
||||
onSubmit={(event) => {
|
||||
event.preventDefault();
|
||||
if (isFormValid() && !isLoading) handleLogin();
|
||||
}}
|
||||
>
|
||||
<Stack spacing={2} sx={{ width: "100%", maxWidth: "100%", overflow: "hidden" }}>
|
||||
<FormControl>
|
||||
<FormLabel>Username</FormLabel>
|
||||
<Input
|
||||
disabled={isLoading}
|
||||
value={form.username}
|
||||
onChange={(event) => setForm({ ...form, username: event.target.value })}
|
||||
sx={{
|
||||
backgroundColor: theme.palette.general.primary,
|
||||
color: theme.palette.text.primary,
|
||||
'&:disabled': {
|
||||
opacity: 0.5,
|
||||
backgroundColor: theme.palette.general.primary,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormControl>
|
||||
<FormLabel>Password</FormLabel>
|
||||
<div style={{ display: 'flex', alignItems: 'center' }}>
|
||||
<Input
|
||||
disabled={isLoading}
|
||||
type={showPassword ? 'text' : 'password'}
|
||||
value={form.password}
|
||||
onChange={(event) => setForm({ ...form, password: event.target.value })}
|
||||
sx={{
|
||||
backgroundColor: theme.palette.general.primary,
|
||||
color: theme.palette.text.primary,
|
||||
flex: 1,
|
||||
'&:disabled': {
|
||||
opacity: 0.5,
|
||||
backgroundColor: theme.palette.general.primary,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<IconButton
|
||||
disabled={isLoading}
|
||||
onClick={() => setShowPassword(!showPassword)}
|
||||
sx={{
|
||||
color: theme.palette.text.primary,
|
||||
marginLeft: 1,
|
||||
'&:disabled': {
|
||||
opacity: 0.5,
|
||||
},
|
||||
}}
|
||||
>
|
||||
{showPassword ? <VisibilityOff /> : <Visibility />}
|
||||
</IconButton>
|
||||
</div>
|
||||
</FormControl>
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={!isFormValid() || isLoading}
|
||||
sx={{
|
||||
backgroundColor: theme.palette.general.primary,
|
||||
'&:hover': {
|
||||
backgroundColor: theme.palette.general.disabled,
|
||||
},
|
||||
'&:disabled': {
|
||||
opacity: 0.5,
|
||||
backgroundColor: theme.palette.general.primary,
|
||||
},
|
||||
}}
|
||||
>
|
||||
{isLoading ? "Logging in..." : "Login"}
|
||||
</Button>
|
||||
<Button
|
||||
disabled={isLoading}
|
||||
onClick={() => {
|
||||
setForm({ username: '', password: '' });
|
||||
setIsCreateUserHidden(false);
|
||||
setIsLoginUserHidden(true);
|
||||
}}
|
||||
sx={{
|
||||
backgroundColor: theme.palette.general.primary,
|
||||
'&:hover': {
|
||||
backgroundColor: theme.palette.general.disabled,
|
||||
},
|
||||
'&:disabled': {
|
||||
opacity: 0.5,
|
||||
backgroundColor: theme.palette.general.primary,
|
||||
},
|
||||
}}
|
||||
>
|
||||
Create User
|
||||
</Button>
|
||||
<Button
|
||||
disabled={isLoading}
|
||||
onClick={handleGuest}
|
||||
sx={{
|
||||
backgroundColor: theme.palette.general.primary,
|
||||
'&:hover': {
|
||||
backgroundColor: theme.palette.general.disabled,
|
||||
},
|
||||
'&:disabled': {
|
||||
opacity: 0.5,
|
||||
backgroundColor: theme.palette.general.primary,
|
||||
},
|
||||
}}
|
||||
>
|
||||
{isLoading ? "Logging in as guest..." : "Login as Guest"}
|
||||
</Button>
|
||||
</Stack>
|
||||
</form>
|
||||
</DialogContent>
|
||||
</ModalDialog>
|
||||
</Modal>
|
||||
</CssVarsProvider>
|
||||
);
|
||||
};
|
||||
|
||||
LoginUserModal.propTypes = {
|
||||
isHidden: PropTypes.bool.isRequired,
|
||||
form: PropTypes.object.isRequired,
|
||||
setForm: PropTypes.func.isRequired,
|
||||
handleLoginUser: PropTypes.func.isRequired,
|
||||
handleGuestLogin: PropTypes.func.isRequired,
|
||||
setIsLoginUserHidden: PropTypes.func.isRequired,
|
||||
setIsCreateUserHidden: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default LoginUserModal;
|
||||
@@ -15,31 +15,44 @@ import {
|
||||
Option,
|
||||
} from '@mui/joy';
|
||||
import theme from '/src/theme';
|
||||
import { useState, useEffect } from 'react';
|
||||
import {useEffect, useState} from 'react';
|
||||
import Visibility from '@mui/icons-material/Visibility';
|
||||
import VisibilityOff from '@mui/icons-material/VisibilityOff';
|
||||
|
||||
const NoAuthenticationModal = ({ isHidden, setIsHidden, onAuthenticate }) => {
|
||||
const [form, setForm] = useState({
|
||||
authMethod: 'Select Auth',
|
||||
password: '',
|
||||
privateKey: '',
|
||||
keyType: '',
|
||||
passphrase: ''
|
||||
});
|
||||
const NoAuthenticationModal = ({ isHidden, form, setForm, setIsNoAuthHidden, handleAuthSubmit }) => {
|
||||
const [showPassword, setShowPassword] = useState(false);
|
||||
const [showPassphrase, setShowPassphrase] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (!form.authMethod) {
|
||||
setForm(prev => ({
|
||||
...prev,
|
||||
authMethod: 'Select Auth',
|
||||
password: '',
|
||||
sshKey: '',
|
||||
keyType: '',
|
||||
}));
|
||||
}
|
||||
}, []);
|
||||
|
||||
const isFormValid = () => {
|
||||
if (!form.authMethod || form.authMethod === 'Select Auth') return false;
|
||||
if (form.authMethod === 'sshKey' && !form.sshKey) return false;
|
||||
if (form.authMethod === 'password' && !form.password) return false;
|
||||
return true;
|
||||
};
|
||||
|
||||
const handleSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
onAuthenticate({
|
||||
authMethod: form.authMethod,
|
||||
password: form.password,
|
||||
privateKey: form.privateKey,
|
||||
keyType: form.keyType,
|
||||
passphrase: form.passphrase
|
||||
});
|
||||
setIsHidden(true);
|
||||
if(isFormValid()) {
|
||||
handleAuthSubmit(form);
|
||||
setForm (prev => ({
|
||||
...prev,
|
||||
authMethod: 'Select Auth',
|
||||
password: '',
|
||||
sshKey: '',
|
||||
keyType: '',
|
||||
}))
|
||||
}
|
||||
};
|
||||
|
||||
const handleFileChange = (e) => {
|
||||
@@ -77,9 +90,9 @@ const NoAuthenticationModal = ({ isHidden, setIsHidden, onAuthenticate }) => {
|
||||
|
||||
setForm({
|
||||
...form,
|
||||
privateKey: keyContent,
|
||||
sshKey: keyContent,
|
||||
keyType: keyType,
|
||||
authMethod: 'key'
|
||||
authMethod: 'sshKey'
|
||||
});
|
||||
};
|
||||
reader.readAsText(file);
|
||||
@@ -92,12 +105,31 @@ const NoAuthenticationModal = ({ isHidden, setIsHidden, onAuthenticate }) => {
|
||||
<CssVarsProvider theme={theme}>
|
||||
<Modal
|
||||
open={!isHidden}
|
||||
onClose={() => setIsHidden(true)}
|
||||
onClose={(e, reason) => {
|
||||
if (reason !== 'backdropClick') {
|
||||
setIsNoAuthHidden(true);
|
||||
}
|
||||
}}
|
||||
sx={{
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<ModalDialog
|
||||
layout="center"
|
||||
sx={{
|
||||
backgroundColor: theme.palette.general.secondary,
|
||||
backgroundColor: theme.palette.general.tertiary,
|
||||
borderColor: theme.palette.general.secondary,
|
||||
color: theme.palette.text.primary,
|
||||
padding: 3,
|
||||
borderRadius: 10,
|
||||
maxWidth: '500px',
|
||||
width: '100%',
|
||||
maxHeight: '80vh',
|
||||
overflow: 'auto',
|
||||
boxSizing: 'border-box',
|
||||
mx: 2,
|
||||
}}
|
||||
>
|
||||
<DialogTitle sx={{ mb: 2 }}>Authentication Required</DialogTitle>
|
||||
@@ -107,14 +139,13 @@ const NoAuthenticationModal = ({ isHidden, setIsHidden, onAuthenticate }) => {
|
||||
<FormControl error={!form.authMethod || form.authMethod === 'Select Auth'}>
|
||||
<FormLabel>Authentication Method</FormLabel>
|
||||
<Select
|
||||
value={form.authMethod}
|
||||
value={form.authMethod || 'Select Auth'}
|
||||
onChange={(e, val) => setForm(prev => ({
|
||||
...prev,
|
||||
authMethod: val,
|
||||
password: '',
|
||||
privateKey: '',
|
||||
sshKey: '',
|
||||
keyType: '',
|
||||
passphrase: ''
|
||||
}))}
|
||||
sx={{
|
||||
backgroundColor: theme.palette.general.primary,
|
||||
@@ -123,29 +154,43 @@ const NoAuthenticationModal = ({ isHidden, setIsHidden, onAuthenticate }) => {
|
||||
>
|
||||
<Option value="Select Auth" disabled>Select Auth</Option>
|
||||
<Option value="password">Password</Option>
|
||||
<Option value="key">SSH Key</Option>
|
||||
<Option value="sshKey">SSH Key</Option >
|
||||
</Select>
|
||||
</FormControl>
|
||||
|
||||
{form.authMethod === 'password' && (
|
||||
<FormControl error={!form.password}>
|
||||
<FormLabel>Password</FormLabel>
|
||||
<Input
|
||||
type={showPassword ? "text" : "password"}
|
||||
value={form.password}
|
||||
onChange={(e) => setForm({ ...form, password: e.target.value })}
|
||||
endDecorator={
|
||||
<IconButton onClick={() => setShowPassword(!showPassword)}>
|
||||
{showPassword ? <VisibilityOff /> : <Visibility />}
|
||||
</IconButton>
|
||||
}
|
||||
/>
|
||||
<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,
|
||||
'&:disabled': {
|
||||
opacity: 0.5,
|
||||
},
|
||||
}}
|
||||
>
|
||||
{showPassword ? <VisibilityOff /> : <Visibility />}
|
||||
</IconButton>
|
||||
</div>
|
||||
</FormControl>
|
||||
)}
|
||||
|
||||
{form.authMethod === 'key' && (
|
||||
{form.authMethod === 'sshKey' && (
|
||||
<Stack spacing={2}>
|
||||
<FormControl error={!form.privateKey}>
|
||||
<FormControl error={!form.sshKey}>
|
||||
<FormLabel>SSH Key</FormLabel>
|
||||
<Button
|
||||
component="label"
|
||||
@@ -162,7 +207,7 @@ const NoAuthenticationModal = ({ isHidden, setIsHidden, onAuthenticate }) => {
|
||||
},
|
||||
}}
|
||||
>
|
||||
{form.privateKey ? `Change ${form.keyType || 'SSH'} Key File` : 'Upload SSH Key File'}
|
||||
{form.sshKey ? `Change ${form.keyType || 'SSH'} Key File` : 'Upload SSH Key File'}
|
||||
<Input
|
||||
type="file"
|
||||
onChange={handleFileChange}
|
||||
@@ -170,29 +215,12 @@ const NoAuthenticationModal = ({ isHidden, setIsHidden, onAuthenticate }) => {
|
||||
/>
|
||||
</Button>
|
||||
</FormControl>
|
||||
{form.privateKey && (
|
||||
<FormControl>
|
||||
<FormLabel>Key Passphrase (optional)</FormLabel>
|
||||
<Input
|
||||
type={showPassphrase ? "text" : "password"}
|
||||
value={form.passphrase || ''}
|
||||
onChange={(e) => setForm(prev => ({ ...prev, passphrase: e.target.value }))}
|
||||
endDecorator={
|
||||
<IconButton onClick={() => setShowPassphrase(!showPassphrase)}>
|
||||
{showPassphrase ? <VisibilityOff /> : <Visibility />}
|
||||
</IconButton>
|
||||
}
|
||||
/>
|
||||
</FormControl>
|
||||
)}
|
||||
</Stack>
|
||||
)}
|
||||
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={!form.authMethod || form.authMethod === 'Select Auth' ||
|
||||
(form.authMethod === 'password' && !form.password) ||
|
||||
(form.authMethod === 'key' && !form.privateKey)}
|
||||
disabled={!isFormValid()}
|
||||
sx={{
|
||||
backgroundColor: theme.palette.general.primary,
|
||||
color: theme.palette.text.primary,
|
||||
@@ -203,6 +231,8 @@ const NoAuthenticationModal = ({ isHidden, setIsHidden, onAuthenticate }) => {
|
||||
backgroundColor: 'rgba(255, 255, 255, 0.1)',
|
||||
color: 'rgba(255, 255, 255, 0.3)',
|
||||
},
|
||||
marginTop: 2,
|
||||
height: '40px',
|
||||
}}
|
||||
>
|
||||
Connect
|
||||
@@ -218,8 +248,10 @@ const NoAuthenticationModal = ({ isHidden, setIsHidden, onAuthenticate }) => {
|
||||
|
||||
NoAuthenticationModal.propTypes = {
|
||||
isHidden: PropTypes.bool.isRequired,
|
||||
setIsHidden: PropTypes.func.isRequired,
|
||||
onAuthenticate: PropTypes.func.isRequired,
|
||||
form: PropTypes.object.isRequired,
|
||||
setForm: PropTypes.func.isRequired,
|
||||
setIsNoAuthHidden: PropTypes.func.isRequired,
|
||||
handleAuthSubmit: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default NoAuthenticationModal;
|
||||
Reference in New Issue
Block a user