Merge branch 'release-1.0'

This commit was merged in pull request #6.
This commit is contained in:
Karmaa
2024-12-12 19:44:27 -06:00
36 changed files with 881 additions and 391 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
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
- 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

2
.gitignore vendored
View File

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

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

2
.idea/modules.xml generated
View File

@@ -2,7 +2,7 @@
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/SSH-Project-JB.iml" filepath="$PROJECT_DIR$/.idea/SSH-Project-JB.iml" />
<module fileurl="file://$PROJECT_DIR$/.idea/Termix.iml" filepath="$PROJECT_DIR$/.idea/Termix.iml" />
</modules>
</component>
</project>

2
.idea/vcs.xml generated
View File

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

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

@@ -0,0 +1,467 @@
<?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 timeout on close.">
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/frontend/src/App.jsx" beforeDir="false" afterPath="$PROJECT_DIR$/frontend/src/App.jsx" afterDir="false" />
</list>
<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">{
&quot;selectedUrlAndAccountId&quot;: {
&quot;url&quot;: &quot;https://github.com/LukeGus/Termix&quot;,
&quot;accountId&quot;: &quot;f107d505-1915-4c73-a3f4-e0440737e1dc&quot;
}
}</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">{
&quot;keyToString&quot;: {
&quot;ASKED_SHARE_PROJECT_CONFIGURATION_FILES&quot;: &quot;true&quot;,
&quot;RunOnceActivity.ShowReadmeOnStart&quot;: &quot;true&quot;,
&quot;RunOnceActivity.git.unshallow&quot;: &quot;true&quot;,
&quot;Shell Script.Node Server.js Start.executor&quot;: &quot;Run&quot;,
&quot;Shell Script.Run backend and frontend.executor&quot;: &quot;Run&quot;,
&quot;Shell Script.run_backend_frontend.executor&quot;: &quot;Run&quot;,
&quot;git-widget-placeholder&quot;: &quot;release-1.0&quot;,
&quot;ignore.virus.scanning.warn.message&quot;: &quot;true&quot;,
&quot;last_opened_file_path&quot;: &quot;C:/Users/Luke/Documents/Personal Projects/Termix/frontend&quot;,
&quot;node.js.detected.package.eslint&quot;: &quot;true&quot;,
&quot;node.js.detected.package.tslint&quot;: &quot;true&quot;,
&quot;node.js.selected.package.eslint&quot;: &quot;(autodetect)&quot;,
&quot;node.js.selected.package.tslint&quot;: &quot;(autodetect)&quot;,
&quot;nodejs_package_manager_path&quot;: &quot;npm&quot;,
&quot;npm.run_start.executor&quot;: &quot;Run&quot;,
&quot;npm.run_start_frontend.executor&quot;: &quot;Run&quot;,
&quot;npm.run_start_node_backend.executor&quot;: &quot;Run&quot;,
&quot;npm.run_start_vite.executor&quot;: &quot;Run&quot;,
&quot;npm.start.executor&quot;: &quot;Run&quot;,
&quot;settings.editor.selected.configurable&quot;: &quot;ml.llm.LLMConfigurable&quot;,
&quot;ts.external.directory.path&quot;: &quot;D:\\Program Files (x86)\\Applications\\Jetbrains Webstorm\\WebStorm 2024.3.1\\plugins\\javascript-plugin\\jsLanguageServicesImpl\\external&quot;,
&quot;vue.rearranger.settings.migration&quot;: &quot;true&quot;
}
}</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="Shell Script.run_backend_frontend" />
<item itemvalue="npm.run_start_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="3958000" />
<workItem from="1734052874393" duration="34000" />
</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>
<task id="LOCAL-00035" summary="Fix timeout on close.">
<option name="closed" value="true" />
<created>1733887980750</created>
<option name="number" value="00035" />
<option name="presentableId" value="LOCAL-00035" />
<option name="project" value="LOCAL" />
<updated>1733887980750</updated>
</task>
<option name="localTasksCounter" value="36" />
<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="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." />
<MESSAGE value="Fix timeout on close." />
<option name="LAST_COMMIT_MESSAGE" value="Fix timeout on close." />
</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/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",

View File

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

View File

@@ -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');
});

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">
<head>
<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="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using @vitejs/plugin-react"
content="Sever management software for SSH!"
/>
<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/
-->
<link rel="manifest" href="/manifest.json" />
<title>React App</title>
<title>Termix</title>
</head>
<body>
<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/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",

View File

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

View File

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

View File

@@ -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)}
/>
<input
type="text"
placeholder="Port"
value={port}
onChange={(e) => handleInputChange(e, setPort)}
/>
<input
type="text"
placeholder="Username"
@@ -152,7 +189,11 @@ const App = () => {
<div ref={terminalRef} className="terminal-container"></div>
</div>
<button className="hide-sidebar-button" onClick={handleSideBarHiding}>
{/* Hide button always positioned in the bottom-right corner */}
<button
className="hide-sidebar-button"
onClick={handleSideBarHiding}
>
{isSideBarHidden ? '+' : '-'}
</button>
</div>

View File

@@ -5,7 +5,7 @@ body {
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
overflow: hidden; /* Prevent scrolling */
overflow: hidden;
}
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

View File

@@ -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
});

0
git_push.sh Executable file → Normal file
View File

View File

@@ -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
Done for release!

164
package-lock.json generated
View File

@@ -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": {}
}
}
}

View File

@@ -1,6 +0,0 @@
{
"dependencies": {
"cross-env": "^7.0.3",
"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",
"name": "Create React App Sample",
"short_name": "Termix",
"name": "Termix",
"icons": [
{
"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