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/.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 index f7db5cae..a12919a6 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -2,7 +2,7 @@ - + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml index 35eb1ddf..94a25f7f 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -1,6 +1,6 @@ - + \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml new file mode 100644 index 00000000..dde1be2b --- /dev/null +++ b/.idea/workspace.xml @@ -0,0 +1,467 @@ + + + + + + + + + + + + + + { + "lastFilter": {} +} + { + "selectedUrlAndAccountId": { + "url": "https://github.com/LukeGus/Termix", + "accountId": "f107d505-1915-4c73-a3f4-e0440737e1dc" + } +} + { + "customColor": "636378de", + "associatedIndex": 8 +} + + + + + + + { + "keyToString": { + "ASKED_SHARE_PROJECT_CONFIGURATION_FILES": "true", + "RunOnceActivity.ShowReadmeOnStart": "true", + "RunOnceActivity.git.unshallow": "true", + "Shell Script.Node Server.js Start.executor": "Run", + "Shell Script.Run backend and frontend.executor": "Run", + "Shell Script.run_backend_frontend.executor": "Run", + "git-widget-placeholder": "release-1.0", + "ignore.virus.scanning.warn.message": "true", + "last_opened_file_path": "C:/Users/Luke/Documents/Personal Projects/Termix/frontend", + "node.js.detected.package.eslint": "true", + "node.js.detected.package.tslint": "true", + "node.js.selected.package.eslint": "(autodetect)", + "node.js.selected.package.tslint": "(autodetect)", + "nodejs_package_manager_path": "npm", + "npm.run_start.executor": "Run", + "npm.run_start_frontend.executor": "Run", + "npm.run_start_node_backend.executor": "Run", + "npm.run_start_vite.executor": "Run", + "npm.start.executor": "Run", + "settings.editor.selected.configurable": "ml.llm.LLMConfigurable", + "ts.external.directory.path": "D:\\Program Files (x86)\\Applications\\Jetbrains Webstorm\\WebStorm 2024.3.1\\plugins\\javascript-plugin\\jsLanguageServicesImpl\\external", + "vue.rearranger.settings.migration": "true" + } +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1733439468142 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ 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 +![GitHub Repo stars](https://img.shields.io/github/stars/LukeGus/Termix?style=flat&label=Stars) +![GitHub forks](https://img.shields.io/github/forks/LukeGus/Termix?style=flat&label=Forks) +![GitHub Release](https://img.shields.io/github/v/release/LukeGus/Termix?style=flat&label=Release) +#### Top Technologies +[![React Badge](https://img.shields.io/badge/-React-61DBFB?style=flat-square&labelColor=black&logo=react&logoColor=61DBFB)](#) +[![Javascript Badge](https://img.shields.io/badge/-Javascript-F0DB4F?style=flat-square&labelColor=black&logo=javascript&logoColor=F0DB4F)](#) +[![Nodejs Badge](https://img.shields.io/badge/-Nodejs-3C873A?style=flat-square&labelColor=black&logo=node.js&logoColor=3C873A)](#) +[![HTML Badge](https://img.shields.io/badge/-HTML-E34F26?style=flat-square&labelColor=black&logo=html5&logoColor=E34F26)](#) +[![CSS Badge](https://img.shields.io/badge/-CSS-1572B6?style=flat-square&labelColor=black&logo=css3&logoColor=1572B6)](#) +[![Docker Badge](https://img.shields.io/badge/-Docker-2496ED?style=flat-square&labelColor=black&logo=docker&logoColor=2496ED)](#) + +
+

+ + Termimx Banner +

+ +# 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 + +![Demo Image](repo-images/DemoImage1.png) + +# 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 00000000..9dfe8173 Binary files /dev/null and b/frontend/favicon.ico differ 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 45136381..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", diff --git a/frontend/src/App.css b/frontend/src/App.css index 99b631b2..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: 25px; + 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 69dada08..c7d21ece 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,98 +10,109 @@ 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(() => { - console.log('Initializing terminal...'); terminal.current = new Terminal({ cursorBlink: true, - theme: { background: '#1e1e1e', foreground: '#ffffff' }, + theme: { + background: '#1e1e1e', + foreground: '#ffffff', + }, macOptionIsMeta: true, allowProposedApi: true, - fontSize: 14, + scrollback: 5000, }); fitAddon.current = new FitAddon(); terminal.current.loadAddon(fitAddon.current); - if (terminalRef.current) { - terminal.current.open(terminalRef.current); - console.log('Terminal opened successfully.'); - } else { - console.error('Terminal reference is not valid!'); - } + terminal.current.open(terminalRef.current); + fitAddon.current.fit(); + // 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', 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) => { if (socket.current && socket.current.readyState === WebSocket.OPEN) { socket.current.send(data); } }); - const resizeTerminal = () => { - if (terminalRef.current) { - fitAddon.current.fit(); - notifyServerOfResize(); - } - }; - - 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); - return () => { terminal.current.dispose(); if (socket.current) { socket.current.close(); } - window.removeEventListener('resize', resizeTerminal); + window.removeEventListener('resize', fitAndNotifyResize); }; }, []); const handleConnect = () => { - console.log('Connecting...'); const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; - const wsUrl = `${protocol}//${window.location.host}/ws/`; - console.log(`WebSocket URL: ${wsUrl}`); + const wsUrl = `${protocol}//${window.location.host}/ws/`; // Use current host and "/ws/" endpoint + + if (!host || !username || !password) { + terminal.current.writeln('Please fill in all fields.'); + return; + } 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 })); + 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) => { - 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); }; @@ -112,13 +123,33 @@ const App = () => { }; const handleSideBarHiding = () => { - setIsSideBarHidden((prevState) => !prevState); - if (!isSideBarHidden) { - setTimeout(() => { - fitAddon.current.fit(); - notifyServerOfResize(); - }, 100); - } + 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 ( @@ -132,6 +163,12 @@ const App = () => { value={host} onChange={(e) => handleInputChange(e, setHost)} /> + handleInputChange(e, setPort)} + /> {
- diff --git a/frontend/src/index.css b/frontend/src/index.css index c0fac97b..565dd547 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -5,7 +5,7 @@ body { sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; - overflow: hidden; /* Prevent scrolling */ + overflow: hidden; } code { diff --git a/frontend/src/logo.svg b/frontend/src/logo.svg index 9dfc1c05..60db3b65 100644 --- a/frontend/src/logo.svg +++ b/frontend/src/logo.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/frontend/start.js b/frontend/start.js index c491af14..3e619239 100644 --- a/frontend/start.js +++ b/frontend/start.js @@ -1,7 +1,7 @@ // start.js const { spawn } = require('child_process'); -const child = spawn('node', ["\"D:/Programming Projects/SSH-Project-JB/backend/server.js\""], { +const child = spawn('node', ["\"D:/Programming Projects/Termix/backend/server.js\""], { stdio: 'inherit', // this is key for interactivity shell: true, // use system shell }); diff --git a/git_push.sh b/git_push.sh old mode 100755 new mode 100644 diff --git a/info.txt b/info.txt index 17810552..4fba4450 100644 --- a/info.txt +++ b/info.txt @@ -1,11 +1,2 @@ Currently: -Fix issue after nano where the input no longer goes down to the bottom. -Fix issue where SSH randomly disconnects -Post inital build. - -Overall Features: -SSH/RDP(?)/VNC(?) -Split Screen & Tabs -ChatGPT/Ollama Stuff -SMTP(?) -Login Page \ No newline at end of file +Done for release! \ No newline at end of file diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 79fc05ec..00000000 --- a/package-lock.json +++ /dev/null @@ -1,164 +0,0 @@ -{ - "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", - "peer": true - }, - "node_modules/xterm-addon-fit": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/xterm-addon-fit/-/xterm-addon-fit-0.8.0.tgz", - "integrity": "sha512-yj3Np7XlvxxhYF/EJ7p3KHaMt6OdwQ+HDu573Vx1lRXsVxOcnVJs51RgjZOouIZOczTsskaS+CpXspK81/DLqw==", - "deprecated": "This package is now deprecated. Move to @xterm/addon-fit instead.", - "peerDependencies": { - "xterm": "^5.0.0" - } - } - }, - "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 - }, - "xterm-addon-fit": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/xterm-addon-fit/-/xterm-addon-fit-0.8.0.tgz", - "integrity": "sha512-yj3Np7XlvxxhYF/EJ7p3KHaMt6OdwQ+HDu573Vx1lRXsVxOcnVJs51RgjZOouIZOczTsskaS+CpXspK81/DLqw==", - "requires": {} - } - } -} diff --git a/package.json b/package.json deleted file mode 100644 index 6546f2d6..00000000 --- a/package.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "dependencies": { - "cross-env": "^7.0.3", - "xterm-addon-fit": "^0.8.0" - } -} diff --git a/public/Octocat.png b/public/Octocat.png deleted file mode 100644 index 91057da4..00000000 Binary files a/public/Octocat.png and /dev/null differ diff --git a/public/favicon.ico b/public/favicon.ico index a11777cc..9dfe8173 100644 Binary files a/public/favicon.ico and b/public/favicon.ico differ diff --git a/public/logo192.png b/public/logo192.png index fc44b0a3..229e95f0 100644 Binary files a/public/logo192.png and b/public/logo192.png differ diff --git a/public/logo512.png b/public/logo512.png index a4e47a65..48dbc4f3 100644 Binary files a/public/logo512.png and b/public/logo512.png differ diff --git a/public/manifest.json b/public/manifest.json index 080d6c77..4660c910 100644 --- a/public/manifest.json +++ b/public/manifest.json @@ -1,6 +1,6 @@ { - "short_name": "React App", - "name": "Create React App Sample", + "short_name": "Termix", + "name": "Termix", "icons": [ { "src": "favicon.ico", diff --git a/repo-images/DemoImage1.png b/repo-images/DemoImage1.png new file mode 100644 index 00000000..f1641293 Binary files /dev/null and b/repo-images/DemoImage1.png differ diff --git a/repo-images/TermixLogo.png b/repo-images/TermixLogo.png new file mode 100644 index 00000000..48373054 Binary files /dev/null and b/repo-images/TermixLogo.png differ