diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 00000000..13566b81 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/SSH-Project-JB.iml b/.idea/SSH-Project-JB.iml new file mode 100644 index 00000000..24643cc3 --- /dev/null +++ b/.idea/SSH-Project-JB.iml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 00000000..f7db5cae --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 00000000..35eb1ddf --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/frontend/package.json b/frontend/package.json index cbe8f407..45136381 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -24,7 +24,7 @@ }, "scripts": { "start-vite": "cross-env BROWSER=none WDS_SOCKET_PORT=0 vite --port 8080", - "start-server": "node 'D:/Programming Projects/SSH-Project/ssh-project/backend/server.js'", + "start-server": "node ./start.js", "start": "npm run start-vite && npm run start-server", "build": "vite build", "preview": "vite preview", diff --git a/frontend/src/App.css b/frontend/src/App.css index fe359acc..99b631b2 100644 --- a/frontend/src/App.css +++ b/frontend/src/App.css @@ -63,7 +63,7 @@ .hide-sidebar-button { position: fixed; bottom: 10px; - right: 10px; + right: 25px; background-color: rgb(108, 108, 108); color: white; border: none; diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 3b7bf407..69dada08 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -16,85 +16,92 @@ const App = () => { const [isSideBarHidden, setIsSideBarHidden] = useState(false); useEffect(() => { - // Initialize the terminal and the fit addon + console.log('Initializing terminal...'); terminal.current = new Terminal({ cursorBlink: true, - theme: { - background: '#1e1e1e', - foreground: '#ffffff', - }, + theme: { background: '#1e1e1e', foreground: '#ffffff' }, macOptionIsMeta: true, allowProposedApi: true, - scrollback: 1000, // Allow scrollback so the terminal doesn't lose state + fontSize: 14, }); - // Initialize and attach the fit addon to the terminal fitAddon.current = new FitAddon(); terminal.current.loadAddon(fitAddon.current); - terminal.current.open(terminalRef.current); + if (terminalRef.current) { + terminal.current.open(terminalRef.current); + console.log('Terminal opened successfully.'); + } else { + console.error('Terminal reference is not valid!'); + } - // Resize terminal to fit the container initially - fitAddon.current.fit(); - - // Adjust terminal size on window resize - const resizeListener = () => { - fitAddon.current.fit(); - }; - window.addEventListener('resize', resizeListener); - - // Monitor terminal data (activity) terminal.current.onData((data) => { if (socket.current && socket.current.readyState === WebSocket.OPEN) { socket.current.send(data); } }); - // Add specific resize call for certain programs like nano or vim - const resizeTerminalOnStart = () => { - // Resize immediately after starting vim/nano or other programs - fitAddon.current.fit(); - terminal.current.clear(); + const resizeTerminal = () => { + if (terminalRef.current) { + fitAddon.current.fit(); + notifyServerOfResize(); + } }; - terminal.current.onData((data) => { - if (data.includes('nano') || data.includes('vim')) { - // Trigger resize immediately when these programs start - resizeTerminalOnStart(); + const notifyServerOfResize = () => { + if (socket.current && socket.current.readyState === WebSocket.OPEN) { + const { rows, cols } = terminal.current; + socket.current.send( + JSON.stringify({ + type: 'resize', + rows, + cols, + height: terminalRef.current.offsetHeight, + width: terminalRef.current.offsetWidth, + }) + ); } - }); + }; + + resizeTerminal(); + window.addEventListener('resize', resizeTerminal); - // Cleanup on component unmount return () => { terminal.current.dispose(); if (socket.current) { socket.current.close(); } - window.removeEventListener('resize', resizeListener); + window.removeEventListener('resize', resizeTerminal); }; }, []); const handleConnect = () => { + console.log('Connecting...'); const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; - const wsUrl = `${protocol}//${window.location.host}/ws/`; // Use current host and "/ws/" endpoint - + const wsUrl = `${protocol}//${window.location.host}/ws/`; + console.log(`WebSocket URL: ${wsUrl}`); + socket.current = new WebSocket(wsUrl); - + socket.current.onopen = () => { + console.log('WebSocket connection opened'); terminal.current.writeln(`Connected to WebSocket server at ${wsUrl}`); socket.current.send(JSON.stringify({ host, username, password })); setIsConnected(true); }; - + socket.current.onmessage = (event) => { + console.log('Received message:', event.data); terminal.current.write(event.data); }; - + socket.current.onerror = (error) => { + console.error('WebSocket error:', error); terminal.current.writeln(`WebSocket error: ${error.message}`); }; - + socket.current.onclose = () => { + console.log('WebSocket connection closed'); terminal.current.writeln('Disconnected from WebSocket server.'); setIsConnected(false); }; @@ -106,47 +113,49 @@ const App = () => { const handleSideBarHiding = () => { setIsSideBarHidden((prevState) => !prevState); + if (!isSideBarHidden) { + setTimeout(() => { + fitAddon.current.fit(); + notifyServerOfResize(); + }, 100); + } }; return ( -
-
-
-

Connection Details

- handleInputChange(e, setHost)} - /> - handleInputChange(e, setUsername)} - /> - handleInputChange(e, setPassword)} - /> - +
+
+
+

Connection Details

+ handleInputChange(e, setHost)} + /> + handleInputChange(e, setUsername)} + /> + handleInputChange(e, setPassword)} + /> + +
+ +
-
+
- - {/* Hide button always positioned in the bottom-right corner */} - -
); }; diff --git a/frontend/start.js b/frontend/start.js new file mode 100644 index 00000000..c491af14 --- /dev/null +++ b/frontend/start.js @@ -0,0 +1,12 @@ +// start.js +const { spawn } = require('child_process'); + +const child = spawn('node', ["\"D:/Programming Projects/SSH-Project-JB/backend/server.js\""], { + stdio: 'inherit', // this is key for interactivity + shell: true, // use system shell +}); + +child.on('exit', function (code, signal) { + console.log('child process exited with ' + + `code ${code} and signal ${signal}`); +}); \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 047d800b..79fc05ec 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,13 +1,90 @@ { - "name": "ssh-project", + "name": "SSH-Project-JB", "lockfileVersion": 2, "requires": true, "packages": { "": { "dependencies": { + "cross-env": "^7.0.3", "xterm-addon-fit": "^0.8.0" } }, + "node_modules/cross-env": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", + "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", + "dependencies": { + "cross-spawn": "^7.0.1" + }, + "bin": { + "cross-env": "src/bin/cross-env.js", + "cross-env-shell": "src/bin/cross-env-shell.js" + }, + "engines": { + "node": ">=10.14", + "npm": ">=6", + "yarn": ">=1" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "engines": { + "node": ">=8" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/xterm": { "version": "5.3.0", "license": "MIT", @@ -24,6 +101,55 @@ } }, "dependencies": { + "cross-env": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", + "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", + "requires": { + "cross-spawn": "^7.0.1" + } + }, + "cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "requires": { + "isexe": "^2.0.0" + } + }, "xterm": { "version": "5.3.0", "peer": true diff --git a/package.json b/package.json index d951c9b1..6546f2d6 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,6 @@ { "dependencies": { + "cross-env": "^7.0.3", "xterm-addon-fit": "^0.8.0" } }