From 15e1fd215ebe0dc8313c672775bd57681b61080f Mon Sep 17 00:00:00 2001
From: Karmaa <88517757+LukeGus@users.noreply.github.com>
Date: Thu, 12 Dec 2024 18:46:43 -0600
Subject: [PATCH] Release 1.0 (#3)
* Nano and timeout error fix #1
* Nano and timeout error fix #2
* Nano and timeout error fix #3
* Nano and timeout error fix #4
* Nano and timeout error fix #5
* Nano and timeout error fix #6
* Nano and timeout error fix #7 (plus css changes for hide buttons)
* Nano and timeout error fix #8 (zoom method)
* Nano zoom fix #9
* Nano zoom fix #10
* Nano zoom fix #11
* Nano zoom fix #12
* Nano zoom fix #13
* Nano zoom fix #14
* Nano zoom fix #15
* Nano zoom fix #16 (ssh-2-promise)
* Nano zoom fix #17 (ssh-2-promise port fix)
* Full return back to old code with minor fixes
* Terminal size fix #18 (rows & cols fix)
* Test ntfy build notification system
* Silent resize cmds & Auto resize terminal #1
* Silent resize cmds & Auto resize terminal #2
* Docker build update
* Docker build update #2
* Docker build update #3
* Docker build update #4 (cache and cleanup)
* Docker build update #5 (cache and cleanup)
* Docker build update #6 (cache and cleanup)
* Docker build update #7 (final)
* Docker build update #8 (nevermind not finanl)
* Docker build update #9 (nevermind not finanl)
* Docker build update #10
* Docker build update #10 (I hope final)
* Docker build update #10 (actual final)
* Release 1.0!!!!
* Repo clean-up
* Remove files ignored by .gitignore
* Change project name, create logo, change README.md to be soon updated.
* Update README.md #1 (also added MIT License)
* Update README.md #2
* Update README.md #3
* Update README.md #4
* Update README.md #5
* Update README.md #6
* Update README.md #7
* Update README.md
* Add images
* Update image name
* Update README.md
* Final changes to release-1.0
* Update README.md
* Update name (need to fix logo)
* Final updates (I hope for real this time)
* Fix web-socket timeout.
* Fix timeout on close.
---
.bash_logout | 7 -
.docker/buildx/.lock | 0
.docker/buildx/current | 1 -
.github/workflows/docker-image.yml | 48 ++-
.gitignore | 2 +
.idea/.gitignore | 8 +
.idea/.name | 1 +
.idea/Termix.iml | 12 +
.idea/icon.svg | 1 +
.idea/modules.xml | 8 +
.idea/vcs.xml | 6 +
.idea/workspace.xml | 455 +++++++++++++++++++++++++++++
LICENSE | 21 ++
README.md | 50 +++-
backend/package-lock.json | 73 +++--
backend/package.json | 4 +-
backend/server.js | 159 +++++-----
frontend/favicon.ico | Bin 0 -> 4286 bytes
frontend/index.html | 6 +-
frontend/package-lock.json | 73 +++--
frontend/package.json | 4 +-
frontend/src/App.css | 3 +-
frontend/src/App.jsx | 188 +++++++-----
frontend/src/index.css | 2 +-
frontend/src/logo.svg | 2 +-
frontend/start.js | 12 +
git_push.sh | 0
info.txt | 11 +-
package-lock.json | 38 ---
package.json | 5 -
public/Octocat.png | Bin 2131769 -> 0 bytes
public/favicon.ico | Bin 3870 -> 4286 bytes
public/logo192.png | Bin 5347 -> 10570 bytes
public/logo512.png | Bin 9664 -> 40273 bytes
public/manifest.json | 4 +-
repo-images/DemoImage1.png | Bin 0 -> 21876 bytes
repo-images/TermixLogo.png | Bin 0 -> 19709 bytes
37 files changed, 926 insertions(+), 278 deletions(-)
delete mode 100644 .bash_logout
delete mode 100644 .docker/buildx/.lock
delete mode 100644 .docker/buildx/current
create mode 100644 .idea/.gitignore
create mode 100644 .idea/.name
create mode 100644 .idea/Termix.iml
create mode 100644 .idea/icon.svg
create mode 100644 .idea/modules.xml
create mode 100644 .idea/vcs.xml
create mode 100644 .idea/workspace.xml
create mode 100644 LICENSE
create mode 100644 frontend/favicon.ico
create mode 100644 frontend/start.js
mode change 100755 => 100644 git_push.sh
delete mode 100644 package-lock.json
delete mode 100644 package.json
delete mode 100644 public/Octocat.png
create mode 100644 repo-images/DemoImage1.png
create mode 100644 repo-images/TermixLogo.png
diff --git a/.bash_logout b/.bash_logout
deleted file mode 100644
index de4f5f75..00000000
--- a/.bash_logout
+++ /dev/null
@@ -1,7 +0,0 @@
-# ~/.bash_logout: executed by bash(1) when login shell exits.
-
-# when leaving the console clear the screen to increase privacy
-
-if [ "$SHLVL" = 1 ]; then
- [ -x /usr/bin/clear_console ] && /usr/bin/clear_console -q
-fi
diff --git a/.docker/buildx/.lock b/.docker/buildx/.lock
deleted file mode 100644
index e69de29b..00000000
diff --git a/.docker/buildx/current b/.docker/buildx/current
deleted file mode 100644
index f34f695d..00000000
--- a/.docker/buildx/current
+++ /dev/null
@@ -1 +0,0 @@
-{"Key":"unix:///var/run/docker.sock","Name":"","Global":false}
\ No newline at end of file
diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml
index 7a44e9c1..05961696 100644
--- a/.github/workflows/docker-image.yml
+++ b/.github/workflows/docker-image.yml
@@ -1,4 +1,5 @@
name: Build and Push Docker Image
+
on:
push:
branches:
@@ -8,7 +9,7 @@ on:
tag_name:
description: "Custom tag name for the Docker image"
required: false
- default: "development-latest"
+ default: ""
jobs:
build:
@@ -16,49 +17,64 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v2
-
+
- name: Setup Node.js
uses: actions/setup-node@v2
with:
node-version: '18'
-
+
- name: Install Dependencies and Build Frontend
run: |
cd frontend
- npm install
+ npm ci
npm run build
-
+
- name: Setup Docker Buildx
uses: docker/setup-buildx-action@v1
-
+
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
-
+
- name: Login to Docker Registry
uses: docker/login-action@v1
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
-
+
- name: Determine Docker image tag
run: |
echo "REPO_OWNER=$(echo ${{ github.repository_owner }} | tr '[:upper:]' '[:lower:]')" >> $GITHUB_ENV
- if [ -n "${{ github.event.inputs.tag_name }}" ]; then
- IMAGE_TAG="${{ github.event.inputs.tag_name }}"
+ if [ "${{ github.event.inputs.tag_name }}" == "" ]; then
+ IMAGE_TAG="${{ github.ref_name }}-development-latest"
else
- IMAGE_TAG="development-latest"
+ IMAGE_TAG="${{ github.event.inputs.tag_name }}"
fi
echo "IMAGE_TAG=$IMAGE_TAG" >> $GITHUB_ENV
-
+
- name: Build and Push Docker Image
uses: docker/build-push-action@v2
with:
context: .
file: ./docker/Dockerfile
push: true
- tags: ghcr.io/${{ env.REPO_OWNER }}/ssh-project:${{ env.IMAGE_TAG }}
+ tags: ghcr.io/${{ env.REPO_OWNER }}/termix:${{ env.IMAGE_TAG }}
labels: org.opencontainers.image.source=https://github.com/${{ github.repository }}
-
- - name: Cleanup Docker Images
- run: docker image prune -af
\ No newline at end of file
+
+ - name: Notify via ntfy
+ run: |
+ curl -d "Docker image build and push completed successfully for tag: ${{ env.IMAGE_TAG }}" \
+ https://ntfy.karmaashomepage.online/termix-build
+
+ - name: Delete all untagged image versions
+ uses: quartx-analytics/ghcr-cleaner@v1
+ with:
+ owner-type: user
+ token: ${{ secrets.GHCR_TOKEN }}
+ repository-owner: ${{ github.repository_owner }}
+ delete-untagged: true
+
+ - name: Cleanup Docker Images Locally
+ run: |
+ docker image prune -af
+ docker system prune -af --volumes
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 7b702ae0..563fa9a0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -133,6 +133,8 @@ yarn-error.log*
.sudo_as_admin_successful
.wget-hsts
.git-credentials
+.docker/
+.bash_logout
# VSCode Files
.vscode-server/
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/.name b/.idea/.name
new file mode 100644
index 00000000..f85d9ef2
--- /dev/null
+++ b/.idea/.name
@@ -0,0 +1 @@
+Termix
\ No newline at end of file
diff --git a/.idea/Termix.iml b/.idea/Termix.iml
new file mode 100644
index 00000000..24643cc3
--- /dev/null
+++ b/.idea/Termix.iml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/icon.svg b/.idea/icon.svg
new file mode 100644
index 00000000..43d2b1c8
--- /dev/null
+++ b/.idea/icon.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 00000000..a12919a6
--- /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..94a25f7f
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/workspace.xml b/.idea/workspace.xml
new file mode 100644
index 00000000..78395fd8
--- /dev/null
+++ b/.idea/workspace.xml
@@ -0,0 +1,455 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {
+ "lastFilter": {}
+}
+
+ {
+ "customColor": "636378de",
+ "associatedIndex": 8
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1733439468142
+
+
+ 1733439468142
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1733447944692
+
+
+
+ 1733447944692
+
+
+
+ 1733448864234
+
+
+
+ 1733448864234
+
+
+
+ 1733449234908
+
+
+
+ 1733449234908
+
+
+
+ 1733449331913
+
+
+
+ 1733449331913
+
+
+
+ 1733449760767
+
+
+
+ 1733449760767
+
+
+
+ 1733450304640
+
+
+
+ 1733450304640
+
+
+
+ 1733450681262
+
+
+
+ 1733450681262
+
+
+
+ 1733451925922
+
+
+
+ 1733451925922
+
+
+
+ 1733532896448
+
+
+
+ 1733532896448
+
+
+
+ 1733538097802
+
+
+
+ 1733538097802
+
+
+
+ 1733539381433
+
+
+
+ 1733539381433
+
+
+
+ 1733553128900
+
+
+
+ 1733553128900
+
+
+
+ 1733553902375
+
+
+
+ 1733553902375
+
+
+
+ 1733555933987
+
+
+
+ 1733555933987
+
+
+
+ 1733556269270
+
+
+
+ 1733556269270
+
+
+
+ 1733556428424
+
+
+
+ 1733556428424
+
+
+
+ 1733556699034
+
+
+
+ 1733556699034
+
+
+
+ 1733556917800
+
+
+
+ 1733556917800
+
+
+
+ 1733557286584
+
+
+
+ 1733557286584
+
+
+
+ 1733557337662
+
+
+
+ 1733557337662
+
+
+
+ 1733557558085
+
+
+
+ 1733557558085
+
+
+
+ 1733557773727
+
+
+
+ 1733557773727
+
+
+
+ 1733558138278
+
+
+
+ 1733558138278
+
+
+
+ 1733558347319
+
+
+
+ 1733558347319
+
+
+
+ 1733558781194
+
+
+
+ 1733558781194
+
+
+
+ 1733558952696
+
+
+
+ 1733558952696
+
+
+
+ 1733603759658
+
+
+
+ 1733603759658
+
+
+
+ 1733688870582
+
+
+
+ 1733688870582
+
+
+
+ 1733691729582
+
+
+
+ 1733691729582
+
+
+
+ 1733691982293
+
+
+
+ 1733691982293
+
+
+
+ 1733692181011
+
+
+
+ 1733692181011
+
+
+
+ 1733692962039
+
+
+
+ 1733692962039
+
+
+
+ 1733886236040
+
+
+
+ 1733886236040
+
+
+
+ 1733887722496
+
+
+
+ 1733887722496
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 00000000..21146d9c
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2024 Luke Gustafson
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
\ No newline at end of file
diff --git a/README.md b/README.md
index 895bf2a4..678c711b 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,49 @@
-SSH is silly
\ No newline at end of file
+# Repo Stats
+
+
+
+#### Top Technologies
+[](#)
+[](#)
+[](#)
+[](#)
+[](#)
+[](#)
+
+
+
+
+
+
+
+# Description
+Termix is an open-source forever free self-hosted SSH (other protocols planned, see [Planned Features](#planned-features)) management panel inspired by [Nexterm](https://github.com/gnmyt/Nexterm). Its purpose is to provide an all-in-one docker-hosted web solution to manage your servers in one easy place. I'm using this project to help me learn [React](https://github.com/facebook/react), [Vite](https://github.com/vitejs/vite-plugin-react), and [Docker](https://www.docker.com) but also because I could never settle on a server management software that I enjoyed to use.
+
+> [!WARNING]
+> This app is in the VERY early stages of development. Expect bugs, data loss, and possibly even security issues!
+
+# Planned Features
+- [x] SSH
+- [ ] VNC
+- [ ] RDP
+- [ ] SMTP (build in file transfer)
+- [ ] Split Screen & Tabs
+- [ ] ChatGPT/Ollama Integration (for commands)
+- [ ] Login Screen
+
+# How Termix is Different
+Before developing Termix, I faced the issue of a server management panel that did not have every feature I liked. [Guacamole](https://guacamole.apache.org/) had poor copy/paste abilities and a poor UI. [Shellngn](https://shellngn.com/) was too expensive and all other alternatives had one major problem with them. I plan to develop the management panel of my dreams with even an AI integration for those pesky commands I always forget the syntax of.
+
+# Installation
+View the Termix [Wiki](https://github.com/LukeGus/Termix/wiki) for information on how to install Termix. You can also use these links to go directly to guide. [Docker](https://github.com/LukeGus/Termix/wiki/Docker) or [Manual](https://github.com/LukeGus/Termix/wiki/Manual).
+
+# Known Bugs
+### Please create an [Issue](https://github.com/LukeGus/Termix/issues) if you find any problems!
+Start session button stays connected even if SSH fails to connect.
+
+# Show-off
+
+
+
+# License
+Distributed under the MIT license. See LICENSE for more information.
diff --git a/backend/package-lock.json b/backend/package-lock.json
index d77add2f..16cf5968 100644
--- a/backend/package-lock.json
+++ b/backend/package-lock.json
@@ -11,6 +11,8 @@
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^14.4.3",
+ "@xterm/addon-fit": "^0.10.0",
+ "@xterm/xterm": "^5.5.0",
"express": "^4.21.1",
"guacamole-common-js": "^1.5.0",
"http-proxy-middleware": "^3.0.3",
@@ -1501,6 +1503,19 @@
"integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==",
"dev": true
},
+ "node_modules/@xterm/addon-fit": {
+ "version": "0.10.0",
+ "resolved": "https://registry.npmjs.org/@xterm/addon-fit/-/addon-fit-0.10.0.tgz",
+ "integrity": "sha512-UFYkDm4HUahf2lnEyHvio51TNGiLK66mqP2JoATy7hRZeXaGMRDr00JiSF7m63vR5WKATF605yEggJKsw0JpMQ==",
+ "peerDependencies": {
+ "@xterm/xterm": "^5.0.0"
+ }
+ },
+ "node_modules/@xterm/xterm": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-5.5.0.tgz",
+ "integrity": "sha512-hqJHYaQb5OptNunnyAnkHyM8aCjZ1MEIDTQu1iIbbTD/xops91NB5yq1ZK/dC2JDbVWtF23zUtl9JE2NqwT87A=="
+ },
"node_modules/abab": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz",
@@ -2288,10 +2303,9 @@
}
},
"node_modules/express": {
- "version": "4.21.1",
- "resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz",
- "integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==",
- "license": "MIT",
+ "version": "4.21.2",
+ "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz",
+ "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==",
"dependencies": {
"accepts": "~1.3.8",
"array-flatten": "1.1.1",
@@ -2312,7 +2326,7 @@
"methods": "~1.1.2",
"on-finished": "2.4.1",
"parseurl": "~1.3.3",
- "path-to-regexp": "0.1.10",
+ "path-to-regexp": "0.1.12",
"proxy-addr": "~2.0.7",
"qs": "6.13.0",
"range-parser": "~1.2.1",
@@ -2327,6 +2341,10 @@
},
"engines": {
"node": ">= 0.10.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
}
},
"node_modules/express/node_modules/debug": {
@@ -3394,9 +3412,9 @@
"optional": true
},
"node_modules/nanoid": {
- "version": "3.3.6",
- "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz",
- "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==",
+ "version": "3.3.8",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz",
+ "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==",
"dev": true,
"funding": [
{
@@ -3404,6 +3422,7 @@
"url": "https://github.com/sponsors/ai"
}
],
+ "license": "MIT",
"bin": {
"nanoid": "bin/nanoid.cjs"
},
@@ -3539,10 +3558,9 @@
}
},
"node_modules/path-to-regexp": {
- "version": "0.1.10",
- "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz",
- "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==",
- "license": "MIT"
+ "version": "0.1.12",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz",
+ "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ=="
},
"node_modules/pathe": {
"version": "1.1.1",
@@ -5774,6 +5792,17 @@
}
}
},
+ "@xterm/addon-fit": {
+ "version": "0.10.0",
+ "resolved": "https://registry.npmjs.org/@xterm/addon-fit/-/addon-fit-0.10.0.tgz",
+ "integrity": "sha512-UFYkDm4HUahf2lnEyHvio51TNGiLK66mqP2JoATy7hRZeXaGMRDr00JiSF7m63vR5WKATF605yEggJKsw0JpMQ==",
+ "requires": {}
+ },
+ "@xterm/xterm": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-5.5.0.tgz",
+ "integrity": "sha512-hqJHYaQb5OptNunnyAnkHyM8aCjZ1MEIDTQu1iIbbTD/xops91NB5yq1ZK/dC2JDbVWtF23zUtl9JE2NqwT87A=="
+ },
"abab": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz",
@@ -6320,9 +6349,9 @@
}
},
"express": {
- "version": "4.21.1",
- "resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz",
- "integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==",
+ "version": "4.21.2",
+ "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz",
+ "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==",
"requires": {
"accepts": "~1.3.8",
"array-flatten": "1.1.1",
@@ -6343,7 +6372,7 @@
"methods": "~1.1.2",
"on-finished": "2.4.1",
"parseurl": "~1.3.3",
- "path-to-regexp": "0.1.10",
+ "path-to-regexp": "0.1.12",
"proxy-addr": "~2.0.7",
"qs": "6.13.0",
"range-parser": "~1.2.1",
@@ -7096,9 +7125,9 @@
"optional": true
},
"nanoid": {
- "version": "3.3.6",
- "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz",
- "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==",
+ "version": "3.3.8",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz",
+ "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==",
"dev": true
},
"negotiator": {
@@ -7185,9 +7214,9 @@
"dev": true
},
"path-to-regexp": {
- "version": "0.1.10",
- "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz",
- "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w=="
+ "version": "0.1.12",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz",
+ "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ=="
},
"pathe": {
"version": "1.1.1",
diff --git a/backend/package.json b/backend/package.json
index 576bf6c3..19a0692a 100644
--- a/backend/package.json
+++ b/backend/package.json
@@ -6,6 +6,8 @@
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^14.4.3",
+ "@xterm/addon-fit": "^0.10.0",
+ "@xterm/xterm": "^5.5.0",
"express": "^4.21.1",
"guacamole-common-js": "^1.5.0",
"http-proxy-middleware": "^3.0.3",
@@ -55,4 +57,4 @@
"vite": "^4.5.5",
"vitest": "^0.34.6"
}
-}
\ No newline at end of file
+}
diff --git a/backend/server.js b/backend/server.js
index 56bf24c8..e041c2b2 100644
--- a/backend/server.js
+++ b/backend/server.js
@@ -2,97 +2,104 @@ const WebSocket = require('ws');
const ssh2 = require('ssh2');
const http = require('http');
-// Create an HTTP server to serve WebSocket connections
const server = http.createServer((req, res) => {
- res.writeHead(200, { 'Content-Type': 'text/plain' });
- res.end('WebSocket server is running\n');
+ res.writeHead(200, { 'Content-Type': 'text/plain' });
+ res.end('WebSocket server is running\n');
});
-// Create a WebSocket server attached to the HTTP server
const wss = new WebSocket.Server({ server });
-// WebSocket connection handling
wss.on('connection', (ws) => {
- console.log('WebSocket connection established');
+ console.log('WebSocket connection established');
- let conn = null; // Declare SSH client outside to manage lifecycle
+ let conn = new ssh2.Client();
+ let stream = null;
+ let currentCols = 80;
+ let currentRows = 24;
+ let interval = null;
- ws.on('message', (message) => {
- try {
- const data = JSON.parse(message); // Try parsing the incoming message as JSON
-
- // Check if message contains SSH connection details
- if (data.host && data.username && data.password) {
- if (conn) {
- conn.end(); // Close any previous connection before starting a new one
+ const resizeTerminal = (cols, rows) => {
+ if (stream && stream.setWindow) {
+ stream.setWindow(rows, cols, rows * 100, cols * 100); // Adjust terminal size
+ console.log(`Terminal resized successfully: cols=${cols}, rows=${rows}`);
}
+ };
- conn = new ssh2.Client(); // Create a new SSH connection instance
+ ws.on('message', (message) => {
+ const messageStr = message.toString();
- // When the SSH connection is ready
- conn.on('ready', () => {
- console.log('SSH Connection established');
-
- // Start an interactive shell session
- conn.shell((err, stream) => {
- if (err) {
- console.log(`SSH Error: ${err}`);
- ws.send(`Error: ${err}`);
- return;
+ let data;
+ try {
+ if (messageStr.trim().startsWith('{')) {
+ data = JSON.parse(messageStr);
+ } else if (stream && stream.writable) {
+ stream.write(messageStr);
+ return;
}
-
- // Handle data from SSH session
- stream.on('data', (data) => {
- console.log(`SSH Output: ${data}`);
- ws.send(data.toString()); // Send the SSH output back to WebSocket client
- });
-
- // Handle stream close event
- stream.on('close', () => {
- console.log('SSH stream closed');
- conn.end();
- });
-
- // When the WebSocket client sends a message (from terminal input), forward it to the SSH stream
- ws.on('message', (message) => {
- console.log(`Received message from WebSocket: ${message}`);
- stream.write(message); // Write the message (input) to the SSH shell
- });
- });
- }).on('error', (err) => {
- console.log('SSH Connection Error: ', err);
- ws.send(`SSH Error: ${err}`);
- }).connect({
- host: data.host, // Host provided from the client
- port: 22, // Default SSH port
- username: data.username, // Username provided from the client
- password: data.password, // Password provided from the client
- });
- }
- } catch (error) {
- // If message is not valid JSON (i.e., terminal input), treat it as raw text and send it to SSH
- console.log('Received non-JSON message, sending to SSH session:', message);
- if (conn) {
- const stream = conn._stream; // Access the SSH stream directly
- if (stream && stream.writable) {
- stream.write(message); // Write raw input message to SSH stream
+ } catch (error) {
+ console.error('Failed to process message:', error);
+ return;
}
- } else {
- console.error('SSH connection is not established yet.');
- }
- }
- });
- // Handle WebSocket close event
- ws.on('close', () => {
- console.log('WebSocket closed');
- if (conn) {
- conn.end(); // Close SSH connection when WebSocket client disconnects
- }
- });
+ if (data?.host && data.port && data.username && data.password) {
+ conn.on('ready', () => {
+ console.log('SSH Connection established');
+
+ interval = setInterval(() => {
+ if (ws.readyState === WebSocket.OPEN) {
+ ws.ping();
+ } else {
+ clearInterval(interval);
+ }
+ }, 15000);
+
+ conn.shell({ term: 'xterm', cols: currentCols, rows: currentRows }, (error, newStream) => {
+ if (error) {
+ console.error(`SSH Shell Error: ${error}`);
+ ws.send(`Error: Could not establish a shell: ${error.message}`);
+ return;
+ }
+
+ stream = newStream;
+
+ stream.on('data', (chunk) => {
+ ws.send(chunk.toString());
+ });
+
+ stream.on('close', () => {
+ console.log('SSH stream closed');
+ conn.end();
+ });
+ });
+ }).on('error', (err) => {
+ console.log('SSH Connection Error:', err);
+ ws.send(`SSH Connection Error: ${err.message}`);
+ }).connect({
+ host: data.host,
+ port: data.port,
+ username: data.username,
+ password: data.password,
+ keepaliveInterval: 10000,
+ keepaliveCountMax: 5,
+ });
+ } else if (data?.cols && data.rows) {
+ currentCols = data.cols;
+ currentRows = data.rows;
+ resizeTerminal(currentCols, currentRows);
+ }
+ });
+
+ ws.on('close', () => {
+ console.log('WebSocket closed');
+ if (interval) {
+ clearInterval(interval);
+ }
+ if (conn) {
+ conn.end();
+ }
+ });
});
-// Start the WebSocket server on port 8081
server.listen(8081, () => {
- console.log('WebSocket server is listening on ws://localhost:8081');
+ console.log('WebSocket server is listening on ws://localhost:8081');
});
\ No newline at end of file
diff --git a/frontend/favicon.ico b/frontend/favicon.ico
new file mode 100644
index 0000000000000000000000000000000000000000..9dfe8173ff4bc41901559aa383551647a5148801
GIT binary patch
literal 4286
zcmc(jO>7%g5XWB$YHxfUxY7fuxX~j$6p9q6TJfm_2}+`te3is@i0ydSu7VsFq|yRH
zO~nE6AyE?G*1(NhL}-8_(l(T+`B0Tpi_}Wtf`q8VV}7&y96MQW92}_|jrZ-mnfcF~
z_jYDB#%z)<%J|Xye$=uXE0R@cue*JvG__EpMCC2cW
zLk3yVc~tIA*x2mld7c?A7LDjO%lLG5AoT|r{VzUij~-~Ty-)A6y-z(+O~PXi8D!Bx
zS8O~yJW@2+tmi^b2*2BMne-gCUVmo)Dkj)*gkLg`4!UCFcd^xq&5e9eY~SB5aZ6`Y
z$(2Og^YK`5euQ(_z!o-zw%77OvGLLOGnqu+UwzLV2;v)o(Iac@Zh5?l;6=to_}Idx
z`VbL^aNWks=U>XE5-V}oV7!w~+0WiTWj}rIq!MFzk@)x@`haaPyu4p^9>AA9+BGD5
z)!!;HueBDjb#(ue_S>(g%UmXNp1cva?=BV%k3(SYQhN
zW3msOgWa9~xb8Sj}IzBq_w&wVsroOku-cH+hu%%}H
zXAT);sSW62qcWbHy)gewgG~;sm4DIw_|yrlar0NN+Mj=zvd0b|vJ>NH*VUhi@h@ya
z=8!>_+KBEt{MGzhQf#fg(e(4&R)7`hhJ;A=f*x?k3V)+=a50R7JgWNu9xdi
zQ2VKcQhz(DV&(9uuh_iu+pPWIm}kG6IA@na9(=~|n7cANtFq{P^6vWegW-98x&G3V
z1~oX}Om9Gs;OY?gAHG$zmoLuPOEbSJU6?*^XMVX*es+9#%(-WD&@IPb_PwJ2psy~s
z!;G~jlAd>M|26Q!LEUrqUFpcH)z6O4ea#%YdiDgHo=*Ev!I1M-)qlAdh#&Ny>8k!y
zbbBNY|A#b=A$DB<=>nl~aQ$~Ov7)`;2KBh=-HMQ{xBss4LE<2Jxos$&o}*4_Unc8t
z=OPvpIq?IX@cUy9TN>MnJ$HCfcjEA7spP)DM){V)5AsI2PjUXZyI?YhOvU%ts9-l^
z3!9t0L>C`czdtjX>1?w97WD#usGAUDcrtfW`l@L;LnHcr>$mIXzdzUdDS47U5xQSU
zdnV6c{r+aGy$o}KjLNUI6ZLE-o2)g){%cI}Z(|<%)0lggjoATB8q+i)|2}YJ>b#sh
U_ers_9NNP+7+`^Emv`9z0KJ2if&c&j
literal 0
HcmV?d00001
diff --git a/frontend/index.html b/frontend/index.html
index 6eb6d291..945c1192 100644
--- a/frontend/index.html
+++ b/frontend/index.html
@@ -2,12 +2,12 @@
-
+
- React App
+ Termix
diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index 795ad30d..5ca56465 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -11,6 +11,8 @@
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^14.4.3",
+ "@xterm/addon-fit": "^0.10.0",
+ "@xterm/xterm": "^5.5.0",
"express": "^4.21.1",
"guacamole-common-js": "^1.5.0",
"http-proxy-middleware": "^3.0.3",
@@ -1501,6 +1503,19 @@
"integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==",
"dev": true
},
+ "node_modules/@xterm/addon-fit": {
+ "version": "0.10.0",
+ "resolved": "https://registry.npmjs.org/@xterm/addon-fit/-/addon-fit-0.10.0.tgz",
+ "integrity": "sha512-UFYkDm4HUahf2lnEyHvio51TNGiLK66mqP2JoATy7hRZeXaGMRDr00JiSF7m63vR5WKATF605yEggJKsw0JpMQ==",
+ "peerDependencies": {
+ "@xterm/xterm": "^5.0.0"
+ }
+ },
+ "node_modules/@xterm/xterm": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-5.5.0.tgz",
+ "integrity": "sha512-hqJHYaQb5OptNunnyAnkHyM8aCjZ1MEIDTQu1iIbbTD/xops91NB5yq1ZK/dC2JDbVWtF23zUtl9JE2NqwT87A=="
+ },
"node_modules/abab": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz",
@@ -2288,10 +2303,9 @@
}
},
"node_modules/express": {
- "version": "4.21.1",
- "resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz",
- "integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==",
- "license": "MIT",
+ "version": "4.21.2",
+ "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz",
+ "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==",
"dependencies": {
"accepts": "~1.3.8",
"array-flatten": "1.1.1",
@@ -2312,7 +2326,7 @@
"methods": "~1.1.2",
"on-finished": "2.4.1",
"parseurl": "~1.3.3",
- "path-to-regexp": "0.1.10",
+ "path-to-regexp": "0.1.12",
"proxy-addr": "~2.0.7",
"qs": "6.13.0",
"range-parser": "~1.2.1",
@@ -2327,6 +2341,10 @@
},
"engines": {
"node": ">= 0.10.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
}
},
"node_modules/express/node_modules/debug": {
@@ -3394,9 +3412,9 @@
"optional": true
},
"node_modules/nanoid": {
- "version": "3.3.6",
- "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz",
- "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==",
+ "version": "3.3.8",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz",
+ "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==",
"dev": true,
"funding": [
{
@@ -3404,6 +3422,7 @@
"url": "https://github.com/sponsors/ai"
}
],
+ "license": "MIT",
"bin": {
"nanoid": "bin/nanoid.cjs"
},
@@ -3539,10 +3558,9 @@
}
},
"node_modules/path-to-regexp": {
- "version": "0.1.10",
- "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz",
- "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==",
- "license": "MIT"
+ "version": "0.1.12",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz",
+ "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ=="
},
"node_modules/pathe": {
"version": "1.1.1",
@@ -5773,6 +5791,17 @@
}
}
},
+ "@xterm/addon-fit": {
+ "version": "0.10.0",
+ "resolved": "https://registry.npmjs.org/@xterm/addon-fit/-/addon-fit-0.10.0.tgz",
+ "integrity": "sha512-UFYkDm4HUahf2lnEyHvio51TNGiLK66mqP2JoATy7hRZeXaGMRDr00JiSF7m63vR5WKATF605yEggJKsw0JpMQ==",
+ "requires": {}
+ },
+ "@xterm/xterm": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-5.5.0.tgz",
+ "integrity": "sha512-hqJHYaQb5OptNunnyAnkHyM8aCjZ1MEIDTQu1iIbbTD/xops91NB5yq1ZK/dC2JDbVWtF23zUtl9JE2NqwT87A=="
+ },
"abab": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz",
@@ -6319,9 +6348,9 @@
}
},
"express": {
- "version": "4.21.1",
- "resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz",
- "integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==",
+ "version": "4.21.2",
+ "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz",
+ "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==",
"requires": {
"accepts": "~1.3.8",
"array-flatten": "1.1.1",
@@ -6342,7 +6371,7 @@
"methods": "~1.1.2",
"on-finished": "2.4.1",
"parseurl": "~1.3.3",
- "path-to-regexp": "0.1.10",
+ "path-to-regexp": "0.1.12",
"proxy-addr": "~2.0.7",
"qs": "6.13.0",
"range-parser": "~1.2.1",
@@ -7095,9 +7124,9 @@
"optional": true
},
"nanoid": {
- "version": "3.3.6",
- "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz",
- "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==",
+ "version": "3.3.8",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz",
+ "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==",
"dev": true
},
"negotiator": {
@@ -7184,9 +7213,9 @@
"dev": true
},
"path-to-regexp": {
- "version": "0.1.10",
- "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz",
- "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w=="
+ "version": "0.1.12",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz",
+ "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ=="
},
"pathe": {
"version": "1.1.1",
diff --git a/frontend/package.json b/frontend/package.json
index cbe8f407..d8441a0b 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -6,6 +6,8 @@
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^14.4.3",
+ "@xterm/addon-fit": "^0.10.0",
+ "@xterm/xterm": "^5.5.0",
"express": "^4.21.1",
"guacamole-common-js": "^1.5.0",
"http-proxy-middleware": "^3.0.3",
@@ -24,7 +26,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..b51f4ea3 100644
--- a/frontend/src/App.css
+++ b/frontend/src/App.css
@@ -22,7 +22,6 @@
flex-direction: column;
align-items: flex-start;
justify-content: space-between;
- border-radius: 5px;
position: relative;
}
@@ -63,7 +62,7 @@
.hide-sidebar-button {
position: fixed;
bottom: 10px;
- right: 10px;
+ right: 23px;
background-color: rgb(108, 108, 108);
color: white;
border: none;
diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx
index 3b7bf407..3d1e6649 100644
--- a/frontend/src/App.jsx
+++ b/frontend/src/App.jsx
@@ -1,7 +1,7 @@
import React, { useEffect, useRef, useState } from 'react';
-import { Terminal } from 'xterm';
+import { Terminal } from '@xterm/xterm';
import 'xterm/css/xterm.css';
-import { FitAddon } from 'xterm-addon-fit';
+import { FitAddon } from '@xterm/addon-fit';
import './App.css';
const App = () => {
@@ -10,13 +10,13 @@ const App = () => {
const fitAddon = useRef(null);
const socket = useRef(null);
const [host, setHost] = useState('');
+ const [port, setPort] = useState('22');
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const [isConnected, setIsConnected] = useState(false);
const [isSideBarHidden, setIsSideBarHidden] = useState(false);
useEffect(() => {
- // Initialize the terminal and the fit addon
terminal.current = new Terminal({
cursorBlink: true,
theme: {
@@ -25,23 +25,37 @@ const App = () => {
},
macOptionIsMeta: true,
allowProposedApi: true,
- scrollback: 1000, // Allow scrollback so the terminal doesn't lose state
+ scrollback: 5000,
});
- // Initialize and attach the fit addon to the terminal
fitAddon.current = new FitAddon();
terminal.current.loadAddon(fitAddon.current);
terminal.current.open(terminalRef.current);
-
- // Resize terminal to fit the container initially
fitAddon.current.fit();
- // Adjust terminal size on window resize
- const resizeListener = () => {
+ // Fit the terminal and send the size when needed
+ const fitAndNotifyResize = () => {
fitAddon.current.fit();
+ if (socket.current && socket.current.readyState === WebSocket.OPEN) {
+ socket.current.send(JSON.stringify({
+ cols: terminal.current.cols,
+ rows: terminal.current.rows,
+ }));
+ }
};
- window.addEventListener('resize', resizeListener);
+ window.addEventListener('resize', fitAndNotifyResize);
+
+ terminal.current.onResize(({ cols, rows }) => {
+ console.log(`Terminal resized to cols:${cols}, rows:${rows}`);
+ fitAndNotifyResize();
+ });
+
+ const handleConnectionEstablished = () => {
+ fitAndNotifyResize();
+ };
+
+ window.addEventListener('connection-established', handleConnectionEstablished);
// Monitor terminal data (activity)
terminal.current.onData((data) => {
@@ -50,50 +64,54 @@ const App = () => {
}
});
- // 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();
- };
-
- terminal.current.onData((data) => {
- if (data.includes('nano') || data.includes('vim')) {
- // Trigger resize immediately when these programs start
- resizeTerminalOnStart();
- }
- });
-
- // Cleanup on component unmount
return () => {
terminal.current.dispose();
if (socket.current) {
socket.current.close();
}
- window.removeEventListener('resize', resizeListener);
+ window.removeEventListener('resize', fitAndNotifyResize);
};
}, []);
const handleConnect = () => {
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
const wsUrl = `${protocol}//${window.location.host}/ws/`; // Use current host and "/ws/" endpoint
-
- socket.current = new WebSocket(wsUrl);
-
+
+ if (!host || !username || !password) {
+ terminal.current.writeln('Please fill in all fields.');
+ return;
+ }
+
+ socket.current = new WebSocket("ws://localhost:8081");
+
socket.current.onopen = () => {
terminal.current.writeln(`Connected to WebSocket server at ${wsUrl}`);
- socket.current.send(JSON.stringify({ host, username, password }));
+ socket.current.send(
+ JSON.stringify({
+ host,
+ port,
+ username,
+ password,
+ rows: terminal.current.rows,
+ cols: terminal.current.cols
+ })
+ );
+
+ // Dispatch a custom event when connection is open
+ const event = new Event('connection-established');
+ window.dispatchEvent(event);
+
setIsConnected(true);
};
-
+
socket.current.onmessage = (event) => {
terminal.current.write(event.data);
};
-
+
socket.current.onerror = (error) => {
terminal.current.writeln(`WebSocket error: ${error.message}`);
};
-
+
socket.current.onclose = () => {
terminal.current.writeln('Disconnected from WebSocket server.');
setIsConnected(false);
@@ -105,48 +123,80 @@ const App = () => {
};
const handleSideBarHiding = () => {
- setIsSideBarHidden((prevState) => !prevState);
+ setIsSideBarHidden((prevState) => {
+ const newState = !prevState;
+ if (newState) {
+ setTimeout(() => {
+ // Add a delay to ensure layout settles before resize action
+ fitAddon.current.fit();
+ if (socket.current && socket.current.readyState === WebSocket.OPEN) {
+ socket.current.send(JSON.stringify({
+ cols: terminal.current.cols,
+ rows: terminal.current.rows,
+ }));
+ }
+ }, 100); // Delay of 100 milliseconds
+ } else {
+ setTimeout(() => {
+ // Refit terminal when showing sidebar as well
+ fitAddon.current.fit();
+ if (socket.current && socket.current.readyState === WebSocket.OPEN) {
+ socket.current.send(JSON.stringify({
+ cols: terminal.current.cols,
+ rows: terminal.current.rows,
+ }));
+ }
+ }, 100); // Delay of 100 milliseconds
+ }
+ return newState;
+ });
};
return (
-