diff --git a/src/AddHostModal.jsx b/src/AddHostModal.jsx index d73b78a3..da3537a7 100644 --- a/src/AddHostModal.jsx +++ b/src/AddHostModal.jsx @@ -1,6 +1,19 @@ import PropTypes from 'prop-types'; import { CssVarsProvider } from '@mui/joy/styles'; -import { Modal, Button, FormControl, FormLabel, Input, Stack, DialogTitle, DialogContent, ModalDialog, Select, Option } from '@mui/joy'; +import { + Modal, + Button, + FormControl, + FormLabel, + Input, + Stack, + DialogTitle, + DialogContent, + ModalDialog, + Select, + Option, + Checkbox +} from '@mui/joy'; import theme from './theme'; const AddHostModal = ({ isHidden, form, setForm, handleAddHost, setIsAddHostHidden }) => { @@ -160,6 +173,16 @@ const AddHostModal = ({ isHidden, form, setForm, handleAddHost, setIsAddHostHidd }} /> + + Remember Host + setForm({ ...form, rememberHost: e.target.checked })} + sx={{ + color: theme.palette.text.primary, + }} + /> + + + ); + })} + + ) : ( +

Hosts are loading...

+ )} + + + ); +} + +HostViewer.propTypes = { + getHosts: PropTypes.func.isRequired, + connectToHost: PropTypes.func.isRequired, +}; + +export default HostViewer; \ No newline at end of file diff --git a/src/Launchpad.jsx b/src/Launchpad.jsx index a59d5595..7aa6e391 100644 --- a/src/Launchpad.jsx +++ b/src/Launchpad.jsx @@ -1,10 +1,12 @@ import PropTypes from 'prop-types'; import { useEffect, useRef } from 'react'; import { CssVarsProvider } from '@mui/joy/styles'; -import { Button } from '@mui/joy'; import theme from './theme'; -function Launchpad({ onClose }) { +// Apps +import HostViewer from './Apps/HostViewer'; + +function Launchpad({ onClose, getHosts, connectToHost }) { const launchpadRef = useRef(null); useEffect(() => { @@ -54,25 +56,7 @@ function Launchpad({ onClose }) { padding: 3, }} > -
-

Launchpad

-

A one-stop shop for adding hosts, apps (AI, notes, etc.), and all new features to come! Coming to you in a future update. Stay tuned!

-

- Can also be opened using Ctrl + L -

- -
+ @@ -81,6 +65,8 @@ function Launchpad({ onClose }) { Launchpad.propTypes = { onClose: PropTypes.func.isRequired, + connectToHost: PropTypes.func.isRequired, + getHosts: PropTypes.func.isRequired, }; export default Launchpad; \ No newline at end of file diff --git a/src/User.jsx b/src/User.jsx index 8c763dd4..0c217213 100644 --- a/src/User.jsx +++ b/src/User.jsx @@ -106,16 +106,57 @@ export const User = forwardRef(({ onLoginSuccess, onCreateSuccess, onDeleteSucce } }; + const saveHost = (hostConfig) => { + if (currentUser.current?.id && socketRef.current) { + socketRef.current.emit("saveHostConfig", { + userId: currentUser.current.id, + hostConfig: hostConfig, + }); + + socketRef.current.once("error", (error) => { + onFailure(error); + }); + } else { + onFailure("No user is currently logged in."); + } + } + const getUser = () => { return currentUser.current; } + const getAllHosts = () => { + return new Promise((resolve, reject) => { + if (currentUser.current?.id && socketRef.current) { + socketRef.current.emit("getHosts", { + userId: currentUser.current.id, + }); + + socketRef.current.once("hostsFound", (data) => { + resolve(data); + }); + + socketRef.current.once("error", (error) => { + console.error(error); + const errorMsg = (error && typeof error === 'object' && error !== null) + ? error.error || error.message || 'An error occurred' + : String(error); + reject(errorMsg); + }); + } else { + reject("No user is currently logged in."); + } + }); + }; + useImperativeHandle(ref, () => ({ createUser, loginUser, logoutUser, deleteUser, + saveHost, getUser, + getAllHosts, })); return
; diff --git a/src/backend/database.cjs b/src/backend/database.cjs index 5d272319..7c13f0f2 100644 --- a/src/backend/database.cjs +++ b/src/backend/database.cjs @@ -113,6 +113,34 @@ async function deleteUser(userId) { } } +async function saveHostConfig(userId, hostConfig) { + try { + const user = await User.findById(userId); + if (user) { + user.sshConnections.push(hostConfig); + await user.save(); + return { success: true }; + } else { + return { error: 'User not found' }; + } + } catch (err) { + return { error: 'Error saving host config: ' + err.message }; + } +} + +async function getHosts(userId) { + try { + const user = await User.findById(userId); + if (user) { + return user.sshConnections; + } else { + return { error: 'User not found' }; + } + } catch (err) { + return { error: 'Error getting hosts: ' + err.message }; + } +} + dbNamespace.on("connection", (socket) => { console.log("New socket connection established on"); @@ -152,6 +180,28 @@ dbNamespace.on("connection", (socket) => { socket.emit(result.error ? "error" : "userDeleted", result); console.log(result.error || `User deleted`); }); + + socket.on("saveHostConfig", async (data) => { + const { userId, hostConfig } = data; + if (!userId || !hostConfig) { + socket.emit("error", "User ID and host config are required"); + return; + } + const result = await saveHostConfig(userId, hostConfig); + socket.emit(result.error ? "error" : "hostConfigSaved", result); + console.log(result.error || `Host config saved`); + }); + + socket.on("getHosts", async (data) => { + const { userId } = data; + if (!userId) { + socket.emit("error", "User ID is required"); + return; + } + const result = await getHosts(userId); + socket.emit(result.error ? "error" : "hostsFound", result); + console.log(result.error || `Hosts found`); + }); }); server.listen(8082, '0.0.0.0', async () => {