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