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.
This commit was merged in pull request #3.
This commit is contained in:
Karmaa
2024-12-12 18:46:43 -06:00
committed by GitHub
parent b6a3f881a8
commit 15e1fd215e
37 changed files with 926 additions and 278 deletions

View File

@@ -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

View File

View File

@@ -1 +0,0 @@
{"Key":"unix:///var/run/docker.sock","Name":"","Global":false}

View File

@@ -1,4 +1,5 @@
name: Build and Push Docker Image name: Build and Push Docker Image
on: on:
push: push:
branches: branches:
@@ -8,7 +9,7 @@ on:
tag_name: tag_name:
description: "Custom tag name for the Docker image" description: "Custom tag name for the Docker image"
required: false required: false
default: "development-latest" default: ""
jobs: jobs:
build: build:
@@ -25,7 +26,7 @@ jobs:
- name: Install Dependencies and Build Frontend - name: Install Dependencies and Build Frontend
run: | run: |
cd frontend cd frontend
npm install npm ci
npm run build npm run build
- name: Setup Docker Buildx - name: Setup Docker Buildx
@@ -44,10 +45,10 @@ jobs:
- name: Determine Docker image tag - name: Determine Docker image tag
run: | run: |
echo "REPO_OWNER=$(echo ${{ github.repository_owner }} | tr '[:upper:]' '[:lower:]')" >> $GITHUB_ENV echo "REPO_OWNER=$(echo ${{ github.repository_owner }} | tr '[:upper:]' '[:lower:]')" >> $GITHUB_ENV
if [ -n "${{ github.event.inputs.tag_name }}" ]; then if [ "${{ github.event.inputs.tag_name }}" == "" ]; then
IMAGE_TAG="${{ github.event.inputs.tag_name }}" IMAGE_TAG="${{ github.ref_name }}-development-latest"
else else
IMAGE_TAG="development-latest" IMAGE_TAG="${{ github.event.inputs.tag_name }}"
fi fi
echo "IMAGE_TAG=$IMAGE_TAG" >> $GITHUB_ENV echo "IMAGE_TAG=$IMAGE_TAG" >> $GITHUB_ENV
@@ -57,8 +58,23 @@ jobs:
context: . context: .
file: ./docker/Dockerfile file: ./docker/Dockerfile
push: true 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 }} labels: org.opencontainers.image.source=https://github.com/${{ github.repository }}
- name: Cleanup Docker Images - name: Notify via ntfy
run: docker image prune -af 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

2
.gitignore vendored
View File

@@ -133,6 +133,8 @@ yarn-error.log*
.sudo_as_admin_successful .sudo_as_admin_successful
.wget-hsts .wget-hsts
.git-credentials .git-credentials
.docker/
.bash_logout
# VSCode Files # VSCode Files
.vscode-server/ .vscode-server/

8
.idea/.gitignore generated vendored Normal file
View File

@@ -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

1
.idea/.name generated Normal file
View File

@@ -0,0 +1 @@
Termix

12
.idea/Termix.iml generated Normal file
View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
<excludeFolder url="file://$MODULE_DIR$/temp" />
<excludeFolder url="file://$MODULE_DIR$/tmp" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

1
.idea/icon.svg generated Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 47 KiB

8
.idea/modules.xml generated Normal file
View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/Termix.iml" filepath="$PROJECT_DIR$/.idea/Termix.iml" />
</modules>
</component>
</project>

6
.idea/vcs.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

455
.idea/workspace.xml generated Normal file
View File

@@ -0,0 +1,455 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AutoImportSettings">
<option name="autoReloadType" value="SELECTIVE" />
</component>
<component name="ChangeListManager">
<list default="true" id="8497df64-d86b-4c98-ac58-c157d9d3fb1e" name="Changes" comment="Fix web-socket timeout." />
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="Git.Settings">
<option name="RECENT_BRANCH_BY_REPOSITORY">
<map>
<entry key="$PROJECT_DIR$" value="release-1.0" />
</map>
</option>
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
</component>
<component name="GitHubPullRequestSearchHistory">{
&quot;lastFilter&quot;: {}
}</component>
<component name="GithubPullRequestsUISettings"><![CDATA[{
"selectedUrlAndAccountId": {
"url": "https://github.com/LukeGus/Termix",
"accountId": "f107d505-1915-4c73-a3f4-e0440737e1dc"
}
}]]></component>
<component name="ProjectColorInfo">{
&quot;customColor&quot;: &quot;636378de&quot;,
&quot;associatedIndex&quot;: 8
}</component>
<component name="ProjectId" id="2pojjnjB8kTL4lo1P2No1yvZp4c" />
<component name="ProjectLevelVcsManager" settingsEditedManually="true">
<ConfirmationsSetting value="1" id="Add" />
</component>
<component name="ProjectViewState">
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent"><![CDATA[{
"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"
}
}]]></component>
<component name="RecentsManager">
<key name="CopyFile.RECENT_KEYS">
<recent name="C:\Users\Luke\Documents\Personal Projects\Termix\frontend" />
<recent name="C:\Users\Luke\Documents\Personal Projects\Termix" />
</key>
<key name="MoveFile.RECENT_KEYS">
<recent name="C:\Users\Luke\Documents\Personal Projects\Termix\frontend\src" />
<recent name="D:\Programming Projects\Termix\repo-images" />
<recent name="D:\Programming Projects\SSH-Project-JB\public" />
</key>
</component>
<component name="RunManager" selected="Shell Script.run_backend_frontend">
<configuration name="run_backend_frontend" type="ShConfigurationType">
<option name="SCRIPT_TEXT" value="Start-Process &quot;powershell&quot; -ArgumentList &quot;-NoProfile&quot;, &quot;-Command npm run start-vite&quot;; Start-Process &quot;powershell&quot; -ArgumentList &quot;-NoProfile&quot;, &quot;-Command npm run start-server&quot; " />
<option name="INDEPENDENT_SCRIPT_PATH" value="false" />
<option name="SCRIPT_PATH" value="$PROJECT_DIR$/../../../../../Windows/System32/WindowsPowerShell/v1.0/powershell.exe" />
<option name="SCRIPT_OPTIONS" value="" />
<option name="INDEPENDENT_SCRIPT_WORKING_DIRECTORY" value="false" />
<option name="SCRIPT_WORKING_DIRECTORY" value="$PROJECT_DIR$/frontend" />
<option name="INDEPENDENT_INTERPRETER_PATH" value="false" />
<option name="INTERPRETER_PATH" value="$PROJECT_DIR$/../../../../../Windows/System32/WindowsPowerShell/v1.0/powershell.exe" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="EXECUTE_IN_TERMINAL" value="false" />
<option name="EXECUTE_SCRIPT_FILE" value="false" />
<envs />
<method v="2" />
</configuration>
<configuration name="run_start_frontend" type="js.build_tools.npm">
<package-json value="$PROJECT_DIR$/frontend/package.json" />
<command value="start" />
<node-interpreter value="project" />
<envs />
<EXTENSION ID="com.intellij.lang.javascript.buildTools.npm.rc.StartBrowserRunConfigurationExtension">
<browser name="a7bb68e0-33c0-4d6f-a81a-aac1fdb870c8" start="true" url="http://localhost:8080" />
</EXTENSION>
<method v="2" />
</configuration>
<list>
<item itemvalue="npm.run_start_frontend" />
<item itemvalue="Shell Script.run_backend_frontend" />
</list>
</component>
<component name="SharedIndexes">
<attachedChunks>
<set>
<option value="bundled-js-predefined-d6986cc7102b-deb605915726-JavaScript-WS-243.22562.112" />
</set>
</attachedChunks>
</component>
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
<component name="TaskManager">
<task active="true" id="Default" summary="Default task">
<changelist id="8497df64-d86b-4c98-ac58-c157d9d3fb1e" name="Changes" comment="" />
<created>1733439468142</created>
<option name="number" value="Default" />
<option name="presentableId" value="Default" />
<updated>1733439468142</updated>
<workItem from="1733439479708" duration="5489000" />
<workItem from="1733448523969" duration="3535000" />
<workItem from="1733549186397" duration="8748000" />
<workItem from="1733599078854" duration="1895000" />
<workItem from="1733601633451" duration="10190000" />
<workItem from="1733644355292" duration="59000" />
<workItem from="1733644430242" duration="42000" />
<workItem from="1733688686708" duration="3433000" />
<workItem from="1733804654516" duration="339000" />
<workItem from="1733805003742" duration="9000" />
<workItem from="1733883751079" duration="757000" />
<workItem from="1733884522510" duration="2933000" />
</task>
<task id="LOCAL-00001" summary="Nano zoom fix #11">
<option name="closed" value="true" />
<created>1733447944692</created>
<option name="number" value="00001" />
<option name="presentableId" value="LOCAL-00001" />
<option name="project" value="LOCAL" />
<updated>1733447944692</updated>
</task>
<task id="LOCAL-00002" summary="Nano zoom fix #11">
<option name="closed" value="true" />
<created>1733448864234</created>
<option name="number" value="00002" />
<option name="presentableId" value="LOCAL-00002" />
<option name="project" value="LOCAL" />
<updated>1733448864234</updated>
</task>
<task id="LOCAL-00003" summary="Nano zoom fix #11">
<option name="closed" value="true" />
<created>1733449234908</created>
<option name="number" value="00003" />
<option name="presentableId" value="LOCAL-00003" />
<option name="project" value="LOCAL" />
<updated>1733449234908</updated>
</task>
<task id="LOCAL-00004" summary="Nano zoom fix #12">
<option name="closed" value="true" />
<created>1733449331913</created>
<option name="number" value="00004" />
<option name="presentableId" value="LOCAL-00004" />
<option name="project" value="LOCAL" />
<updated>1733449331913</updated>
</task>
<task id="LOCAL-00005" summary="Nano zoom fix #13">
<option name="closed" value="true" />
<created>1733449760767</created>
<option name="number" value="00005" />
<option name="presentableId" value="LOCAL-00005" />
<option name="project" value="LOCAL" />
<updated>1733449760767</updated>
</task>
<task id="LOCAL-00006" summary="Nano zoom fix #14">
<option name="closed" value="true" />
<created>1733450304640</created>
<option name="number" value="00006" />
<option name="presentableId" value="LOCAL-00006" />
<option name="project" value="LOCAL" />
<updated>1733450304640</updated>
</task>
<task id="LOCAL-00007" summary="Nano zoom fix #15">
<option name="closed" value="true" />
<created>1733450681262</created>
<option name="number" value="00007" />
<option name="presentableId" value="LOCAL-00007" />
<option name="project" value="LOCAL" />
<updated>1733450681262</updated>
</task>
<task id="LOCAL-00008" summary="Nano zoom fix #16 (ssh-2-promise)">
<option name="closed" value="true" />
<created>1733451925922</created>
<option name="number" value="00008" />
<option name="presentableId" value="LOCAL-00008" />
<option name="project" value="LOCAL" />
<updated>1733451925922</updated>
</task>
<task id="LOCAL-00009" summary="Full return back to old code with minor fixes">
<option name="closed" value="true" />
<created>1733532896448</created>
<option name="number" value="00009" />
<option name="presentableId" value="LOCAL-00009" />
<option name="project" value="LOCAL" />
<updated>1733532896448</updated>
</task>
<task id="LOCAL-00010" summary="Terminal size fix #18 (rows &amp; cols fix)">
<option name="closed" value="true" />
<created>1733538097802</created>
<option name="number" value="00010" />
<option name="presentableId" value="LOCAL-00010" />
<option name="project" value="LOCAL" />
<updated>1733538097802</updated>
</task>
<task id="LOCAL-00011" summary="Test ntfy build notification system">
<option name="closed" value="true" />
<created>1733539381433</created>
<option name="number" value="00011" />
<option name="presentableId" value="LOCAL-00011" />
<option name="project" value="LOCAL" />
<updated>1733539381433</updated>
</task>
<task id="LOCAL-00012" summary="Silent resize cmds &amp; Auto resize terminal #1">
<option name="closed" value="true" />
<created>1733553128900</created>
<option name="number" value="00012" />
<option name="presentableId" value="LOCAL-00012" />
<option name="project" value="LOCAL" />
<updated>1733553128900</updated>
</task>
<task id="LOCAL-00013" summary="Silent resize cmds &amp; Auto resize terminal #2">
<option name="closed" value="true" />
<created>1733553902375</created>
<option name="number" value="00013" />
<option name="presentableId" value="LOCAL-00013" />
<option name="project" value="LOCAL" />
<updated>1733553902375</updated>
</task>
<task id="LOCAL-00014" summary="Docker build update">
<option name="closed" value="true" />
<created>1733555933987</created>
<option name="number" value="00014" />
<option name="presentableId" value="LOCAL-00014" />
<option name="project" value="LOCAL" />
<updated>1733555933987</updated>
</task>
<task id="LOCAL-00015" summary="Docker build update #2">
<option name="closed" value="true" />
<created>1733556269270</created>
<option name="number" value="00015" />
<option name="presentableId" value="LOCAL-00015" />
<option name="project" value="LOCAL" />
<updated>1733556269270</updated>
</task>
<task id="LOCAL-00016" summary="Docker build update #3">
<option name="closed" value="true" />
<created>1733556428424</created>
<option name="number" value="00016" />
<option name="presentableId" value="LOCAL-00016" />
<option name="project" value="LOCAL" />
<updated>1733556428424</updated>
</task>
<task id="LOCAL-00017" summary="Docker build update #4 (cache and cleanup)">
<option name="closed" value="true" />
<created>1733556699034</created>
<option name="number" value="00017" />
<option name="presentableId" value="LOCAL-00017" />
<option name="project" value="LOCAL" />
<updated>1733556699034</updated>
</task>
<task id="LOCAL-00018" summary="Docker build update #5 (cache and cleanup)">
<option name="closed" value="true" />
<created>1733556917800</created>
<option name="number" value="00018" />
<option name="presentableId" value="LOCAL-00018" />
<option name="project" value="LOCAL" />
<updated>1733556917800</updated>
</task>
<task id="LOCAL-00019" summary="Docker build update #5 (cache and cleanup)">
<option name="closed" value="true" />
<created>1733557286584</created>
<option name="number" value="00019" />
<option name="presentableId" value="LOCAL-00019" />
<option name="project" value="LOCAL" />
<updated>1733557286584</updated>
</task>
<task id="LOCAL-00020" summary="Docker build update #6 (cache and cleanup)">
<option name="closed" value="true" />
<created>1733557337662</created>
<option name="number" value="00020" />
<option name="presentableId" value="LOCAL-00020" />
<option name="project" value="LOCAL" />
<updated>1733557337662</updated>
</task>
<task id="LOCAL-00021" summary="Docker build update #7 (final)">
<option name="closed" value="true" />
<created>1733557558085</created>
<option name="number" value="00021" />
<option name="presentableId" value="LOCAL-00021" />
<option name="project" value="LOCAL" />
<updated>1733557558085</updated>
</task>
<task id="LOCAL-00022" summary="Docker build update #8 (nevermind not finanl)">
<option name="closed" value="true" />
<created>1733557773727</created>
<option name="number" value="00022" />
<option name="presentableId" value="LOCAL-00022" />
<option name="project" value="LOCAL" />
<updated>1733557773727</updated>
</task>
<task id="LOCAL-00023" summary="Docker build update #9 (nevermind not finanl)">
<option name="closed" value="true" />
<created>1733558138278</created>
<option name="number" value="00023" />
<option name="presentableId" value="LOCAL-00023" />
<option name="project" value="LOCAL" />
<updated>1733558138278</updated>
</task>
<task id="LOCAL-00024" summary="Docker build update #10">
<option name="closed" value="true" />
<created>1733558347319</created>
<option name="number" value="00024" />
<option name="presentableId" value="LOCAL-00024" />
<option name="project" value="LOCAL" />
<updated>1733558347319</updated>
</task>
<task id="LOCAL-00025" summary="Docker build update #10 (I hope final)">
<option name="closed" value="true" />
<created>1733558781194</created>
<option name="number" value="00025" />
<option name="presentableId" value="LOCAL-00025" />
<option name="project" value="LOCAL" />
<updated>1733558781194</updated>
</task>
<task id="LOCAL-00026" summary="Docker build update #10 (actual final)">
<option name="closed" value="true" />
<created>1733558952696</created>
<option name="number" value="00026" />
<option name="presentableId" value="LOCAL-00026" />
<option name="project" value="LOCAL" />
<updated>1733558952696</updated>
</task>
<task id="LOCAL-00027" summary="Release 1.0!!!!">
<option name="closed" value="true" />
<created>1733603759658</created>
<option name="number" value="00027" />
<option name="presentableId" value="LOCAL-00027" />
<option name="project" value="LOCAL" />
<updated>1733603759658</updated>
</task>
<task id="LOCAL-00028" summary="Update README.md #4">
<option name="closed" value="true" />
<created>1733688870582</created>
<option name="number" value="00028" />
<option name="presentableId" value="LOCAL-00028" />
<option name="project" value="LOCAL" />
<updated>1733688870582</updated>
</task>
<task id="LOCAL-00029" summary="Add images">
<option name="closed" value="true" />
<created>1733691729582</created>
<option name="number" value="00029" />
<option name="presentableId" value="LOCAL-00029" />
<option name="project" value="LOCAL" />
<updated>1733691729582</updated>
</task>
<task id="LOCAL-00030" summary="Update image name">
<option name="closed" value="true" />
<created>1733691982293</created>
<option name="number" value="00030" />
<option name="presentableId" value="LOCAL-00030" />
<option name="project" value="LOCAL" />
<updated>1733691982293</updated>
</task>
<task id="LOCAL-00031" summary="Final changes to release-1.0">
<option name="closed" value="true" />
<created>1733692181011</created>
<option name="number" value="00031" />
<option name="presentableId" value="LOCAL-00031" />
<option name="project" value="LOCAL" />
<updated>1733692181011</updated>
</task>
<task id="LOCAL-00032" summary="Update name (need to fix logo)">
<option name="closed" value="true" />
<created>1733692962039</created>
<option name="number" value="00032" />
<option name="presentableId" value="LOCAL-00032" />
<option name="project" value="LOCAL" />
<updated>1733692962039</updated>
</task>
<task id="LOCAL-00033" summary="Final updates (I hope for real this time)">
<option name="closed" value="true" />
<created>1733886236040</created>
<option name="number" value="00033" />
<option name="presentableId" value="LOCAL-00033" />
<option name="project" value="LOCAL" />
<updated>1733886236040</updated>
</task>
<task id="LOCAL-00034" summary="Fix web-socket timeout.">
<option name="closed" value="true" />
<created>1733887722496</created>
<option name="number" value="00034" />
<option name="presentableId" value="LOCAL-00034" />
<option name="project" value="LOCAL" />
<updated>1733887722496</updated>
</task>
<option name="localTasksCounter" value="35" />
<servers />
</component>
<component name="TypeScriptGeneratedFilesManager">
<option name="version" value="3" />
</component>
<component name="Vcs.Log.Tabs.Properties">
<option name="TAB_STATES">
<map>
<entry key="MAIN">
<value>
<State />
</value>
</entry>
</map>
</option>
</component>
<component name="VcsManagerConfiguration">
<MESSAGE value="Full return back to old code with minor fixes" />
<MESSAGE value="Terminal size fix #18 (rows &amp; cols fix)" />
<MESSAGE value="Test ntfy build notification system" />
<MESSAGE value="Silent resize cmds &amp; Auto resize terminal #1" />
<MESSAGE value="Silent resize cmds &amp; Auto resize terminal #2" />
<MESSAGE value="Docker build update" />
<MESSAGE value="Docker build update #2" />
<MESSAGE value="Docker build update #3" />
<MESSAGE value="Docker build update #4 (cache and cleanup)" />
<MESSAGE value="Docker build update #5 (cache and cleanup)" />
<MESSAGE value="Docker build update #6 (cache and cleanup)" />
<MESSAGE value="Docker build update #7 (final)" />
<MESSAGE value="Docker build update #8 (nevermind not finanl)" />
<MESSAGE value="Docker build update #9 (nevermind not finanl)" />
<MESSAGE value="Docker build update #10" />
<MESSAGE value="Docker build update #10 (I hope final)" />
<MESSAGE value="Docker build update #10 (actual final)" />
<MESSAGE value="Release 1.0!!!!" />
<MESSAGE value="Update README.md #4" />
<MESSAGE value="Add images" />
<MESSAGE value="Update image name" />
<MESSAGE value="Final changes to release-1.0" />
<MESSAGE value="Update name (need to fix logo)" />
<MESSAGE value="Final updates (I hope for real this time)" />
<MESSAGE value="Fix web-socket timeout." />
<option name="LAST_COMMIT_MESSAGE" value="Fix web-socket timeout." />
</component>
</project>

21
LICENSE Normal file
View File

@@ -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.

View File

@@ -1 +1,49 @@
SSH is silly # 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)](#)
<br />
<p align="center">
<a href="https://github.com/LukeGus/Termix">
<img alt="Termimx Banner" src=./repo-images/TermixLogo.png style="width: 125px; height: auto;"> </a>
</p>
# 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.

View File

@@ -11,6 +11,8 @@
"@testing-library/jest-dom": "^5.16.5", "@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0", "@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^14.4.3", "@testing-library/user-event": "^14.4.3",
"@xterm/addon-fit": "^0.10.0",
"@xterm/xterm": "^5.5.0",
"express": "^4.21.1", "express": "^4.21.1",
"guacamole-common-js": "^1.5.0", "guacamole-common-js": "^1.5.0",
"http-proxy-middleware": "^3.0.3", "http-proxy-middleware": "^3.0.3",
@@ -1501,6 +1503,19 @@
"integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==",
"dev": true "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": { "node_modules/abab": {
"version": "2.0.6", "version": "2.0.6",
"resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz",
@@ -2288,10 +2303,9 @@
} }
}, },
"node_modules/express": { "node_modules/express": {
"version": "4.21.1", "version": "4.21.2",
"resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz", "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz",
"integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==", "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==",
"license": "MIT",
"dependencies": { "dependencies": {
"accepts": "~1.3.8", "accepts": "~1.3.8",
"array-flatten": "1.1.1", "array-flatten": "1.1.1",
@@ -2312,7 +2326,7 @@
"methods": "~1.1.2", "methods": "~1.1.2",
"on-finished": "2.4.1", "on-finished": "2.4.1",
"parseurl": "~1.3.3", "parseurl": "~1.3.3",
"path-to-regexp": "0.1.10", "path-to-regexp": "0.1.12",
"proxy-addr": "~2.0.7", "proxy-addr": "~2.0.7",
"qs": "6.13.0", "qs": "6.13.0",
"range-parser": "~1.2.1", "range-parser": "~1.2.1",
@@ -2327,6 +2341,10 @@
}, },
"engines": { "engines": {
"node": ">= 0.10.0" "node": ">= 0.10.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
} }
}, },
"node_modules/express/node_modules/debug": { "node_modules/express/node_modules/debug": {
@@ -3394,9 +3412,9 @@
"optional": true "optional": true
}, },
"node_modules/nanoid": { "node_modules/nanoid": {
"version": "3.3.6", "version": "3.3.8",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz",
"integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==",
"dev": true, "dev": true,
"funding": [ "funding": [
{ {
@@ -3404,6 +3422,7 @@
"url": "https://github.com/sponsors/ai" "url": "https://github.com/sponsors/ai"
} }
], ],
"license": "MIT",
"bin": { "bin": {
"nanoid": "bin/nanoid.cjs" "nanoid": "bin/nanoid.cjs"
}, },
@@ -3539,10 +3558,9 @@
} }
}, },
"node_modules/path-to-regexp": { "node_modules/path-to-regexp": {
"version": "0.1.10", "version": "0.1.12",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz",
"integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==", "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ=="
"license": "MIT"
}, },
"node_modules/pathe": { "node_modules/pathe": {
"version": "1.1.1", "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": { "abab": {
"version": "2.0.6", "version": "2.0.6",
"resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz",
@@ -6320,9 +6349,9 @@
} }
}, },
"express": { "express": {
"version": "4.21.1", "version": "4.21.2",
"resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz", "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz",
"integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==", "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==",
"requires": { "requires": {
"accepts": "~1.3.8", "accepts": "~1.3.8",
"array-flatten": "1.1.1", "array-flatten": "1.1.1",
@@ -6343,7 +6372,7 @@
"methods": "~1.1.2", "methods": "~1.1.2",
"on-finished": "2.4.1", "on-finished": "2.4.1",
"parseurl": "~1.3.3", "parseurl": "~1.3.3",
"path-to-regexp": "0.1.10", "path-to-regexp": "0.1.12",
"proxy-addr": "~2.0.7", "proxy-addr": "~2.0.7",
"qs": "6.13.0", "qs": "6.13.0",
"range-parser": "~1.2.1", "range-parser": "~1.2.1",
@@ -7096,9 +7125,9 @@
"optional": true "optional": true
}, },
"nanoid": { "nanoid": {
"version": "3.3.6", "version": "3.3.8",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz",
"integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==",
"dev": true "dev": true
}, },
"negotiator": { "negotiator": {
@@ -7185,9 +7214,9 @@
"dev": true "dev": true
}, },
"path-to-regexp": { "path-to-regexp": {
"version": "0.1.10", "version": "0.1.12",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz",
"integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==" "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ=="
}, },
"pathe": { "pathe": {
"version": "1.1.1", "version": "1.1.1",

View File

@@ -6,6 +6,8 @@
"@testing-library/jest-dom": "^5.16.5", "@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0", "@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^14.4.3", "@testing-library/user-event": "^14.4.3",
"@xterm/addon-fit": "^0.10.0",
"@xterm/xterm": "^5.5.0",
"express": "^4.21.1", "express": "^4.21.1",
"guacamole-common-js": "^1.5.0", "guacamole-common-js": "^1.5.0",
"http-proxy-middleware": "^3.0.3", "http-proxy-middleware": "^3.0.3",

View File

@@ -2,97 +2,104 @@ const WebSocket = require('ws');
const ssh2 = require('ssh2'); const ssh2 = require('ssh2');
const http = require('http'); const http = require('http');
// Create an HTTP server to serve WebSocket connections
const server = http.createServer((req, res) => { const server = http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' }); res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('WebSocket server is running\n'); res.end('WebSocket server is running\n');
}); });
// Create a WebSocket server attached to the HTTP server
const wss = new WebSocket.Server({ server }); const wss = new WebSocket.Server({ server });
// WebSocket connection handling
wss.on('connection', (ws) => { 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;
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}`);
}
};
ws.on('message', (message) => { ws.on('message', (message) => {
const messageStr = message.toString();
let data;
try { try {
const data = JSON.parse(message); // Try parsing the incoming message as JSON if (messageStr.trim().startsWith('{')) {
data = JSON.parse(messageStr);
// Check if message contains SSH connection details } else if (stream && stream.writable) {
if (data.host && data.username && data.password) { stream.write(messageStr);
if (conn) { return;
conn.end(); // Close any previous connection before starting a new one
} }
} catch (error) {
conn = new ssh2.Client(); // Create a new SSH connection instance console.error('Failed to process message:', error);
// 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; return;
} }
// Handle data from SSH session if (data?.host && data.port && data.username && data.password) {
stream.on('data', (data) => { conn.on('ready', () => {
console.log(`SSH Output: ${data}`); console.log('SSH Connection established');
ws.send(data.toString()); // Send the SSH output back to WebSocket client
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());
}); });
// Handle stream close event
stream.on('close', () => { stream.on('close', () => {
console.log('SSH stream closed'); console.log('SSH stream closed');
conn.end(); 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) => { }).on('error', (err) => {
console.log('SSH Connection Error:', err); console.log('SSH Connection Error:', err);
ws.send(`SSH Error: ${err}`); ws.send(`SSH Connection Error: ${err.message}`);
}).connect({ }).connect({
host: data.host, // Host provided from the client host: data.host,
port: 22, // Default SSH port port: data.port,
username: data.username, // Username provided from the client username: data.username,
password: data.password, // Password provided from the client password: data.password,
keepaliveInterval: 10000,
keepaliveCountMax: 5,
}); });
} } else if (data?.cols && data.rows) {
} catch (error) { currentCols = data.cols;
// If message is not valid JSON (i.e., terminal input), treat it as raw text and send it to SSH currentRows = data.rows;
console.log('Received non-JSON message, sending to SSH session:', message); resizeTerminal(currentCols, currentRows);
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
}
} else {
console.error('SSH connection is not established yet.');
}
} }
}); });
// Handle WebSocket close event
ws.on('close', () => { ws.on('close', () => {
console.log('WebSocket closed'); console.log('WebSocket closed');
if (interval) {
clearInterval(interval);
}
if (conn) { if (conn) {
conn.end(); // Close SSH connection when WebSocket client disconnects conn.end();
} }
}); });
}); });
// Start the WebSocket server on port 8081
server.listen(8081, () => { server.listen(8081, () => {
console.log('WebSocket server is listening on ws://localhost:8081'); console.log('WebSocket server is listening on ws://localhost:8081');
}); });

BIN
frontend/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@@ -2,12 +2,12 @@
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<link rel="icon" href="/favicon.ico" /> <link rel="icon" href="/favicon.ico?v=2" />
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" /> <meta name="theme-color" content="#000000" />
<meta <meta
name="description" name="description"
content="Web site created using @vitejs/plugin-react" content="Sever management software for SSH!"
/> />
<link rel="apple-touch-icon" href="/logo192.png" /> <link rel="apple-touch-icon" href="/logo192.png" />
<!-- <!--
@@ -15,7 +15,7 @@
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/ user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
--> -->
<link rel="manifest" href="/manifest.json" /> <link rel="manifest" href="/manifest.json" />
<title>React App</title> <title>Termix</title>
</head> </head>
<body> <body>
<noscript>You need to enable JavaScript to run this app.</noscript> <noscript>You need to enable JavaScript to run this app.</noscript>

View File

@@ -11,6 +11,8 @@
"@testing-library/jest-dom": "^5.16.5", "@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0", "@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^14.4.3", "@testing-library/user-event": "^14.4.3",
"@xterm/addon-fit": "^0.10.0",
"@xterm/xterm": "^5.5.0",
"express": "^4.21.1", "express": "^4.21.1",
"guacamole-common-js": "^1.5.0", "guacamole-common-js": "^1.5.0",
"http-proxy-middleware": "^3.0.3", "http-proxy-middleware": "^3.0.3",
@@ -1501,6 +1503,19 @@
"integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==",
"dev": true "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": { "node_modules/abab": {
"version": "2.0.6", "version": "2.0.6",
"resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz",
@@ -2288,10 +2303,9 @@
} }
}, },
"node_modules/express": { "node_modules/express": {
"version": "4.21.1", "version": "4.21.2",
"resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz", "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz",
"integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==", "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==",
"license": "MIT",
"dependencies": { "dependencies": {
"accepts": "~1.3.8", "accepts": "~1.3.8",
"array-flatten": "1.1.1", "array-flatten": "1.1.1",
@@ -2312,7 +2326,7 @@
"methods": "~1.1.2", "methods": "~1.1.2",
"on-finished": "2.4.1", "on-finished": "2.4.1",
"parseurl": "~1.3.3", "parseurl": "~1.3.3",
"path-to-regexp": "0.1.10", "path-to-regexp": "0.1.12",
"proxy-addr": "~2.0.7", "proxy-addr": "~2.0.7",
"qs": "6.13.0", "qs": "6.13.0",
"range-parser": "~1.2.1", "range-parser": "~1.2.1",
@@ -2327,6 +2341,10 @@
}, },
"engines": { "engines": {
"node": ">= 0.10.0" "node": ">= 0.10.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
} }
}, },
"node_modules/express/node_modules/debug": { "node_modules/express/node_modules/debug": {
@@ -3394,9 +3412,9 @@
"optional": true "optional": true
}, },
"node_modules/nanoid": { "node_modules/nanoid": {
"version": "3.3.6", "version": "3.3.8",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz",
"integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==",
"dev": true, "dev": true,
"funding": [ "funding": [
{ {
@@ -3404,6 +3422,7 @@
"url": "https://github.com/sponsors/ai" "url": "https://github.com/sponsors/ai"
} }
], ],
"license": "MIT",
"bin": { "bin": {
"nanoid": "bin/nanoid.cjs" "nanoid": "bin/nanoid.cjs"
}, },
@@ -3539,10 +3558,9 @@
} }
}, },
"node_modules/path-to-regexp": { "node_modules/path-to-regexp": {
"version": "0.1.10", "version": "0.1.12",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz",
"integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==", "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ=="
"license": "MIT"
}, },
"node_modules/pathe": { "node_modules/pathe": {
"version": "1.1.1", "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": { "abab": {
"version": "2.0.6", "version": "2.0.6",
"resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz",
@@ -6319,9 +6348,9 @@
} }
}, },
"express": { "express": {
"version": "4.21.1", "version": "4.21.2",
"resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz", "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz",
"integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==", "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==",
"requires": { "requires": {
"accepts": "~1.3.8", "accepts": "~1.3.8",
"array-flatten": "1.1.1", "array-flatten": "1.1.1",
@@ -6342,7 +6371,7 @@
"methods": "~1.1.2", "methods": "~1.1.2",
"on-finished": "2.4.1", "on-finished": "2.4.1",
"parseurl": "~1.3.3", "parseurl": "~1.3.3",
"path-to-regexp": "0.1.10", "path-to-regexp": "0.1.12",
"proxy-addr": "~2.0.7", "proxy-addr": "~2.0.7",
"qs": "6.13.0", "qs": "6.13.0",
"range-parser": "~1.2.1", "range-parser": "~1.2.1",
@@ -7095,9 +7124,9 @@
"optional": true "optional": true
}, },
"nanoid": { "nanoid": {
"version": "3.3.6", "version": "3.3.8",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz",
"integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==",
"dev": true "dev": true
}, },
"negotiator": { "negotiator": {
@@ -7184,9 +7213,9 @@
"dev": true "dev": true
}, },
"path-to-regexp": { "path-to-regexp": {
"version": "0.1.10", "version": "0.1.12",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz",
"integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==" "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ=="
}, },
"pathe": { "pathe": {
"version": "1.1.1", "version": "1.1.1",

View File

@@ -6,6 +6,8 @@
"@testing-library/jest-dom": "^5.16.5", "@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0", "@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^14.4.3", "@testing-library/user-event": "^14.4.3",
"@xterm/addon-fit": "^0.10.0",
"@xterm/xterm": "^5.5.0",
"express": "^4.21.1", "express": "^4.21.1",
"guacamole-common-js": "^1.5.0", "guacamole-common-js": "^1.5.0",
"http-proxy-middleware": "^3.0.3", "http-proxy-middleware": "^3.0.3",
@@ -24,7 +26,7 @@
}, },
"scripts": { "scripts": {
"start-vite": "cross-env BROWSER=none WDS_SOCKET_PORT=0 vite --port 8080", "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", "start": "npm run start-vite && npm run start-server",
"build": "vite build", "build": "vite build",
"preview": "vite preview", "preview": "vite preview",

View File

@@ -22,7 +22,6 @@
flex-direction: column; flex-direction: column;
align-items: flex-start; align-items: flex-start;
justify-content: space-between; justify-content: space-between;
border-radius: 5px;
position: relative; position: relative;
} }
@@ -63,7 +62,7 @@
.hide-sidebar-button { .hide-sidebar-button {
position: fixed; position: fixed;
bottom: 10px; bottom: 10px;
right: 10px; right: 23px;
background-color: rgb(108, 108, 108); background-color: rgb(108, 108, 108);
color: white; color: white;
border: none; border: none;

View File

@@ -1,7 +1,7 @@
import React, { useEffect, useRef, useState } from 'react'; import React, { useEffect, useRef, useState } from 'react';
import { Terminal } from 'xterm'; import { Terminal } from '@xterm/xterm';
import 'xterm/css/xterm.css'; import 'xterm/css/xterm.css';
import { FitAddon } from 'xterm-addon-fit'; import { FitAddon } from '@xterm/addon-fit';
import './App.css'; import './App.css';
const App = () => { const App = () => {
@@ -10,13 +10,13 @@ const App = () => {
const fitAddon = useRef(null); const fitAddon = useRef(null);
const socket = useRef(null); const socket = useRef(null);
const [host, setHost] = useState(''); const [host, setHost] = useState('');
const [port, setPort] = useState('22');
const [username, setUsername] = useState(''); const [username, setUsername] = useState('');
const [password, setPassword] = useState(''); const [password, setPassword] = useState('');
const [isConnected, setIsConnected] = useState(false); const [isConnected, setIsConnected] = useState(false);
const [isSideBarHidden, setIsSideBarHidden] = useState(false); const [isSideBarHidden, setIsSideBarHidden] = useState(false);
useEffect(() => { useEffect(() => {
// Initialize the terminal and the fit addon
terminal.current = new Terminal({ terminal.current = new Terminal({
cursorBlink: true, cursorBlink: true,
theme: { theme: {
@@ -25,23 +25,37 @@ const App = () => {
}, },
macOptionIsMeta: true, macOptionIsMeta: true,
allowProposedApi: 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(); fitAddon.current = new FitAddon();
terminal.current.loadAddon(fitAddon.current); terminal.current.loadAddon(fitAddon.current);
terminal.current.open(terminalRef.current); terminal.current.open(terminalRef.current);
// Resize terminal to fit the container initially
fitAddon.current.fit(); fitAddon.current.fit();
// Adjust terminal size on window resize // Fit the terminal and send the size when needed
const resizeListener = () => { const fitAndNotifyResize = () => {
fitAddon.current.fit(); 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) // Monitor terminal data (activity)
terminal.current.onData((data) => { terminal.current.onData((data) => {
@@ -50,27 +64,12 @@ 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 () => { return () => {
terminal.current.dispose(); terminal.current.dispose();
if (socket.current) { if (socket.current) {
socket.current.close(); socket.current.close();
} }
window.removeEventListener('resize', resizeListener); window.removeEventListener('resize', fitAndNotifyResize);
}; };
}, []); }, []);
@@ -78,11 +77,30 @@ const App = () => {
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; 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/`; // 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 = () => { socket.current.onopen = () => {
terminal.current.writeln(`Connected to WebSocket server at ${wsUrl}`); 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); setIsConnected(true);
}; };
@@ -105,7 +123,33 @@ const App = () => {
}; };
const handleSideBarHiding = () => { 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 ( return (
@@ -119,6 +163,12 @@ const App = () => {
value={host} value={host}
onChange={(e) => handleInputChange(e, setHost)} onChange={(e) => handleInputChange(e, setHost)}
/> />
<input
type="text"
placeholder="Port"
value={port}
onChange={(e) => handleInputChange(e, setPort)}
/>
<input <input
type="text" type="text"
placeholder="Username" placeholder="Username"

View File

@@ -5,7 +5,7 @@ body {
sans-serif; sans-serif;
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
overflow: hidden; /* Prevent scrolling */ overflow: hidden;
} }
code { code {

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 49 KiB

12
frontend/start.js Normal file
View File

@@ -0,0 +1,12 @@
// start.js
const { spawn } = require('child_process');
const child = spawn('node', ["\"D:/Programming Projects/Termix/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}`);
});

0
git_push.sh Executable file → Normal file
View File

View File

@@ -1,11 +1,2 @@
Currently: Currently:
Fix issue after nano where the input no longer goes down to the bottom. Done for release!
Fix issue where SSH randomly disconnects
Post inital build.
Overall Features:
SSH/RDP(?)/VNC(?)
Split Screen & Tabs
ChatGPT/Ollama Stuff
SMTP(?)
Login Page

38
package-lock.json generated
View File

@@ -1,38 +0,0 @@
{
"name": "ssh-project",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"dependencies": {
"xterm-addon-fit": "^0.8.0"
}
},
"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": {
"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": {}
}
}
}

View File

@@ -1,5 +0,0 @@
{
"dependencies": {
"xterm-addon-fit": "^0.8.0"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.4 KiB

After

Width:  |  Height:  |  Size: 39 KiB

View File

@@ -1,6 +1,6 @@
{ {
"short_name": "React App", "short_name": "Termix",
"name": "Create React App Sample", "name": "Termix",
"icons": [ "icons": [
{ {
"src": "favicon.ico", "src": "favicon.ico",

BIN
repo-images/DemoImage1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

BIN
repo-images/TermixLogo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB