Initial Commit

This commit is contained in:
LukeGus
2024-12-04 21:04:46 -06:00
commit b6a3f881a8
36 changed files with 16998 additions and 0 deletions

7
.bash_logout Normal file
View File

@@ -0,0 +1,7 @@
# ~/.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

0
.docker/buildx/.lock Normal file
View File

1
.docker/buildx/current Normal file
View File

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

2
.gitconfig Normal file
View File

@@ -0,0 +1,2 @@
[credential]
helper = store

64
.github/workflows/docker-image.yml vendored Normal file
View File

@@ -0,0 +1,64 @@
name: Build and Push Docker Image
on:
push:
branches:
- development
workflow_dispatch:
inputs:
tag_name:
description: "Custom tag name for the Docker image"
required: false
default: "development-latest"
jobs:
build:
runs-on: ubuntu-latest
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 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 }}"
else
IMAGE_TAG="development-latest"
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 }}
labels: org.opencontainers.image.source=https://github.com/${{ github.repository }}
- name: Cleanup Docker Images
run: docker image prune -af

147
.gitignore vendored Normal file
View File

@@ -0,0 +1,147 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# TypeScript v1 declaration files
typings/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
.env.test
# parcel-bundler cache (https://parceljs.org/)
.cache
# Next.js build output
.next
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and *not* Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.bash_history
.bashrc
.init_done
.profile
.sudo_as_admin_successful
.wget-hsts
.git-credentials
# VSCode Files
.vscode-server/
# Configs
.config/
# .dotnet
.dotnet/
# .local
.local/

20
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,20 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Run application",
"type": "node",
"request": "launch",
"cwd": "D:/Programming Projects/SSH-Project/ssh-project/frontend",
"console": "integratedTerminal",
"runtimeExecutable": "npm",
"runtimeArgs": [
"run-script",
"start"
],
"skipFiles": [
"<node_internals>/**"
]
}
]
}

1
README.md Normal file
View File

@@ -0,0 +1 @@
SSH is silly

7
backend/entrypoint.sh Normal file
View File

@@ -0,0 +1,7 @@
#!/bin/sh
# Start the backend server
node /backend/server.js &
# Start nginx in the foreground
exec nginx -g 'daemon off;'

7986
backend/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

58
backend/package.json Normal file
View File

@@ -0,0 +1,58 @@
{
"name": "codespaces-react",
"version": "0.1.0",
"private": true,
"dependencies": {
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^14.4.3",
"express": "^4.21.1",
"guacamole-common-js": "^1.5.0",
"http-proxy-middleware": "^3.0.3",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"ssh2": "^1.16.0",
"web-vitals": "^3.1.0",
"ws": "^8.18.0",
"xterm": "^5.3.0",
"xterm-addon-fit": "^0.8.0"
},
"overrides": {
"@svgr/webpack": "^8.0.1",
"@adobe/css-tools": "^4.3.1",
"postcss": "^8.4.31"
},
"scripts": {
"start-vite": "cross-env BROWSER=none WDS_SOCKET_PORT=0 vite --port 8080",
"start-server": "node 'D:/Programming Projects/SSH-Project/ssh-project/backend/server.js'",
"start": "npm run start-vite && npm run start-server",
"build": "vite build",
"preview": "vite preview",
"test": "vitest"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"@vitejs/plugin-react": "^4.1.1",
"cross-env": "^7.0.3",
"jsdom": "^22.1.0",
"vite": "^4.5.5",
"vitest": "^0.34.6"
}
}

98
backend/server.js Normal file
View File

@@ -0,0 +1,98 @@
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');
});
// 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');
let conn = null; // Declare SSH client outside to manage lifecycle
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
}
conn = new ssh2.Client(); // Create a new SSH connection instance
// 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;
}
// 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
}
} 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
}
});
});
// Start the WebSocket server on port 8081
server.listen(8081, () => {
console.log('WebSocket server is listening on ws://localhost:8081');
});

30
docker/Dockerfile Normal file
View File

@@ -0,0 +1,30 @@
# Build frontend
FROM node:18-alpine AS frontend-build
WORKDIR /app
COPY frontend/package*.json ./frontend/
RUN npm --prefix frontend install
COPY frontend/ ./frontend/
RUN npm --prefix frontend run build
# Build backend
FROM node:18-alpine AS backend-build
WORKDIR /backend
COPY backend/package*.json ./
RUN npm install
COPY backend/ .
# Configure nginx
FROM nginx:alpine
RUN apk add --no-cache nodejs npm
COPY docker/nginx.conf /etc/nginx/nginx.conf
COPY --from=frontend-build /app/frontend/dist /usr/share/nginx/html
COPY --from=backend-build /backend /backend
COPY --from=backend-build /backend/entrypoint.sh /backend/entrypoint.sh
# Configure start-up
RUN chmod +x /backend/entrypoint.sh
ENTRYPOINT ["/backend/entrypoint.sh"]
EXPOSE 8080
EXPOSE 8081

38
docker/nginx.conf Normal file
View File

@@ -0,0 +1,38 @@
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
server {
listen 8080;
server_name localhost;
# Serve the React app
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
# Proxy WebSocket requests
location /ws/ {
proxy_pass http://localhost:8081; # Backend WebSocket server
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
# Error pages
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
}

35
frontend/index.html Normal file
View File

@@ -0,0 +1,35 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="/favicon.ico" />
<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"
/>
<link rel="apple-touch-icon" href="/logo192.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
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>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
<script type="module" src="/src/index.jsx"></script>
</html>

7985
frontend/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

58
frontend/package.json Normal file
View File

@@ -0,0 +1,58 @@
{
"name": "codespaces-react",
"version": "0.1.0",
"private": true,
"dependencies": {
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^14.4.3",
"express": "^4.21.1",
"guacamole-common-js": "^1.5.0",
"http-proxy-middleware": "^3.0.3",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"ssh2": "^1.16.0",
"web-vitals": "^3.1.0",
"ws": "^8.18.0",
"xterm": "^5.3.0",
"xterm-addon-fit": "^0.8.0"
},
"overrides": {
"@svgr/webpack": "^8.0.1",
"@adobe/css-tools": "^4.3.1",
"postcss": "^8.4.31"
},
"scripts": {
"start-vite": "cross-env BROWSER=none WDS_SOCKET_PORT=0 vite --port 8080",
"start-server": "node 'D:/Programming Projects/SSH-Project/ssh-project/backend/server.js'",
"start": "npm run start-vite && npm run start-server",
"build": "vite build",
"preview": "vite preview",
"test": "vitest"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"@vitejs/plugin-react": "^4.1.1",
"cross-env": "^7.0.3",
"jsdom": "^22.1.0",
"vite": "^4.5.5",
"vitest": "^0.34.6"
}
}

90
frontend/src/App.css Normal file
View File

@@ -0,0 +1,90 @@
.app-container {
display: flex;
flex-direction: column;
min-height: 100vh;
background-color: #000000;
font-family: Arial, sans-serif;
}
.main-content {
display: flex;
height: 100vh;
width: 100%;
margin: 0;
}
.sidebar {
background-color: #1a1a1a;
padding: 35px 35px 35px 20px;
width: 250px;
color: white;
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: space-between;
border-radius: 5px;
position: relative;
}
.sidebar.hidden {
display: none;
}
.sidebar h2 {
margin-bottom: 15px;
}
.sidebar input {
margin-bottom: 10px;
padding: 10px;
width: 100%;
background-color: rgb(108, 108, 108);
border: 1px solid #bdc3c7;
border-radius: 5px;
margin-left: -4px;
color: white;
}
.sidebar button {
padding: 10px;
background-color: rgb(108, 108, 108);
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
font-size: 16px;
margin-left: -4px;
}
.sidebar button:disabled {
background-color: #141414;
}
.hide-sidebar-button {
position: fixed;
bottom: 10px;
right: 10px;
background-color: rgb(108, 108, 108);
color: white;
border: none;
border-radius: 5px;
width: 30px;
height: 30px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
font-size: 20px;
z-index: 10;
}
.terminal-container {
background-color: #1e1e1e;
border-radius: 0px;
box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.2);
overflow: auto;
height: 100%;
flex-grow: 1;
overflow: hidden;
position: relative;
}

153
frontend/src/App.jsx Normal file
View File

@@ -0,0 +1,153 @@
import React, { useEffect, useRef, useState } from 'react';
import { Terminal } from 'xterm';
import 'xterm/css/xterm.css';
import { FitAddon } from 'xterm-addon-fit';
import './App.css';
const App = () => {
const terminalRef = useRef(null);
const terminal = useRef(null);
const fitAddon = useRef(null);
const socket = useRef(null);
const [host, setHost] = useState('');
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const [isConnected, setIsConnected] = useState(false);
const [isSideBarHidden, setIsSideBarHidden] = useState(false);
useEffect(() => {
// Initialize the terminal and the fit addon
terminal.current = new Terminal({
cursorBlink: true,
theme: {
background: '#1e1e1e',
foreground: '#ffffff',
},
macOptionIsMeta: true,
allowProposedApi: true,
scrollback: 1000, // Allow scrollback so the terminal doesn't lose state
});
// Initialize and attach the fit addon to the terminal
fitAddon.current = new FitAddon();
terminal.current.loadAddon(fitAddon.current);
terminal.current.open(terminalRef.current);
// Resize terminal to fit the container initially
fitAddon.current.fit();
// Adjust terminal size on window resize
const resizeListener = () => {
fitAddon.current.fit();
};
window.addEventListener('resize', resizeListener);
// Monitor terminal data (activity)
terminal.current.onData((data) => {
if (socket.current && socket.current.readyState === WebSocket.OPEN) {
socket.current.send(data);
}
});
// Add specific resize call for certain programs like nano or vim
const resizeTerminalOnStart = () => {
// Resize immediately after starting vim/nano or other programs
fitAddon.current.fit();
terminal.current.clear();
};
terminal.current.onData((data) => {
if (data.includes('nano') || data.includes('vim')) {
// Trigger resize immediately when these programs start
resizeTerminalOnStart();
}
});
// Cleanup on component unmount
return () => {
terminal.current.dispose();
if (socket.current) {
socket.current.close();
}
window.removeEventListener('resize', resizeListener);
};
}, []);
const handleConnect = () => {
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
const wsUrl = `${protocol}//${window.location.host}/ws/`; // Use current host and "/ws/" endpoint
socket.current = new WebSocket(wsUrl);
socket.current.onopen = () => {
terminal.current.writeln(`Connected to WebSocket server at ${wsUrl}`);
socket.current.send(JSON.stringify({ host, username, password }));
setIsConnected(true);
};
socket.current.onmessage = (event) => {
terminal.current.write(event.data);
};
socket.current.onerror = (error) => {
terminal.current.writeln(`WebSocket error: ${error.message}`);
};
socket.current.onclose = () => {
terminal.current.writeln('Disconnected from WebSocket server.');
setIsConnected(false);
};
};
const handleInputChange = (event, setState) => {
setState(event.target.value);
};
const handleSideBarHiding = () => {
setIsSideBarHidden((prevState) => !prevState);
};
return (
<div className="app-container">
<div className="main-content">
<div className={`sidebar ${isSideBarHidden ? 'hidden' : ''}`}>
<h2>Connection Details</h2>
<input
type="text"
placeholder="Host"
value={host}
onChange={(e) => handleInputChange(e, setHost)}
/>
<input
type="text"
placeholder="Username"
value={username}
onChange={(e) => handleInputChange(e, setUsername)}
/>
<input
type="password"
placeholder="Password"
value={password}
onChange={(e) => handleInputChange(e, setPassword)}
/>
<button onClick={handleConnect} disabled={isConnected}>
{isConnected ? 'Connected' : 'Start Session'}
</button>
</div>
<div ref={terminalRef} className="terminal-container"></div>
</div>
{/* Hide button always positioned in the bottom-right corner */}
<button
className="hide-sidebar-button"
onClick={handleSideBarHiding}
>
{isSideBarHidden ? '+' : '-'}
</button>
</div>
);
};
export default App;

14
frontend/src/index.css Normal file
View File

@@ -0,0 +1,14 @@
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
overflow: hidden; /* Prevent scrolling */
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}

17
frontend/src/index.jsx Normal file
View File

@@ -0,0 +1,17 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

1
frontend/src/logo.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3"><g fill="#61DAFB"><path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/><circle cx="420.9" cy="296.5" r="45.7"/><path d="M520.5 78.1z"/></g></svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@@ -0,0 +1,13 @@
const reportWebVitals = onPerfEntry => {
if (onPerfEntry && onPerfEntry instanceof Function) {
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
getCLS(onPerfEntry);
getFID(onPerfEntry);
getFCP(onPerfEntry);
getLCP(onPerfEntry);
getTTFB(onPerfEntry);
});
}
};
export default reportWebVitals;

View File

@@ -0,0 +1,5 @@
// jest-dom adds custom jest matchers for asserting on DOM nodes.
// allows you to do things like:
// expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom
import '@testing-library/jest-dom';

64
git_push.sh Executable file
View File

@@ -0,0 +1,64 @@
#!/bin/bash
# Check if the correct number of parameters is provided
if [ "$#" -ne 2 ]; then
echo "Usage: $0 <commit-message> <branch-name>"
exit 1
fi
# Assign parameters to variables
COMMIT_MESSAGE=$1
BRANCH_NAME=$2
# Ensure Git is using the credential helper for HTTPS
git config --global credential.helper store
# Check if the branch exists
BRANCH_EXISTS=$(git branch --list "$BRANCH_NAME")
if [ -z "$BRANCH_EXISTS" ]; then
echo "Branch '$BRANCH_NAME' does not exist. Creating it now..."
git checkout -b "$BRANCH_NAME"
if [ $? -ne 0 ]; then
echo "Failed to create branch '$BRANCH_NAME'."
exit 1
fi
else
echo "Branch '$BRANCH_NAME' exists. Checking it out..."
git checkout "$BRANCH_NAME"
if [ $? -ne 0 ]; then
echo "Failed to checkout branch '$BRANCH_NAME'."
exit 1
fi
fi
# Check if there are any changes to commit
if git diff --quiet; then
echo "No changes to commit."
exit 0
fi
# Perform git operations
echo "Adding all changes..."
git add .
if [ $? -ne 0 ]; then
echo "Failed to add changes."
exit 1
fi
echo "Committing changes with message: $COMMIT_MESSAGE"
git commit -m "$COMMIT_MESSAGE"
if [ $? -ne 0 ]; then
echo "Commit failed."
exit 1
fi
echo "Pushing changes forcefully to branch: $BRANCH_NAME"
export GIT_ASKPASS=echo
git push -f origin "$BRANCH_NAME"
if [ $? -ne 0 ]; then
echo "Push failed."
exit 1
fi
echo "Git operations completed successfully."

11
info.txt Normal file
View File

@@ -0,0 +1,11 @@
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

5
jsconfig.json Normal file
View File

@@ -0,0 +1,5 @@
{
"compilerOptions": {
"moduleResolution": "node"
}
}

38
package-lock.json generated Normal file
View File

@@ -0,0 +1,38 @@
{
"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": {}
}
}
}

5
package.json Normal file
View File

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

BIN
public/Octocat.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

BIN
public/logo192.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

BIN
public/logo512.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

25
public/manifest.json Normal file
View File

@@ -0,0 +1,25 @@
{
"short_name": "React App",
"name": "Create React App Sample",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
},
{
"src": "logo192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "logo512.png",
"type": "image/png",
"sizes": "512x512"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}

3
public/robots.txt Normal file
View File

@@ -0,0 +1,3 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:

17
vite.config.js Normal file
View File

@@ -0,0 +1,17 @@
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
export default defineConfig({
plugins: [react()],
test: {
globals: true,
environment: 'jsdom',
},
build: {
outDir: "dist", // Output directory for the build
rollupOptions: {
// Use a relative path from the project root (adjusted to reflect Docker WORKDIR)
input: 'index.html',
},
},
});