From ae023525ec6200f54e2c7082c5443178de42a217 Mon Sep 17 00:00:00 2001 From: DanailDR Date: Fri, 11 Sep 2026 10:41:43 +0300 Subject: [PATCH] fix: improve UI and login functionality * update UI components and layout * fix login form functionality * pin Docker base image to python:3.11-slim-bookworm --- deployment/Dockerfile | 4 +-- src/index.html | 31 +++++++++++-------- src/static/webssh.css | 27 +++++++++++++++++ src/static/webssh.js | 70 ++++++++++++++++++++++++------------------- 4 files changed, 88 insertions(+), 44 deletions(-) diff --git a/deployment/Dockerfile b/deployment/Dockerfile index c5dd072..2bc1e16 100644 --- a/deployment/Dockerfile +++ b/deployment/Dockerfile @@ -1,6 +1,6 @@ # Multi-stage Dockerfile for WebSSH - Optimized for small image size # Stage 1: Builder - compiles Python packages with build dependencies -FROM python:3.11-slim AS builder +FROM python:3.11-slim-bookworm AS builder # Install build dependencies needed for compiling cryptography/pynacl on arm/v7 RUN apt-get update && apt-get install -y --no-install-recommends \ @@ -95,7 +95,7 @@ RUN set -eux; \ if command -v strip >/dev/null 2>&1; then strip --strip-unneeded /tmp/caddy_bin_dir/caddy || true; fi # Stage 2: Final - minimal runtime image -FROM python:3.11-slim +FROM python:3.11-slim-bookworm # Install only runtime dependencies (no build tools) RUN apt-get update && apt-get install -y --no-install-recommends \ diff --git a/src/index.html b/src/index.html index 713e62f..05776aa 100644 --- a/src/index.html +++ b/src/index.html @@ -19,18 +19,20 @@ @@ -42,6 +44,11 @@ + diff --git a/src/static/webssh.css b/src/static/webssh.css index 5f66d96..fbfe263 100644 --- a/src/static/webssh.css +++ b/src/static/webssh.css @@ -45,6 +45,33 @@ body { .btn-add:hover { background: #059669; } +.sidebar-actions { + display: flex; + gap: 5px; + margin-bottom: 14px; + flex-wrap: nowrap; +} + +.btn-sidebar-action { + flex: 1; + min-width: 0; + padding: 7px 6px; + color: #e5e7eb; + font-size: 12px; + white-space: nowrap; +} + +.btn-terminal-action { background: #2563eb; } +.btn-terminal-action:hover, +.btn-terminal-action.active { background: #1d4ed8; } + +.btn-sftp-action { background: #d97706; } +.btn-sftp-action:hover, +.btn-sftp-action.active { background: #b45309; } + +.btn-sidebar-action:not(.btn-terminal-action):not(.btn-sftp-action) { background: #7c3aed; } +.btn-sidebar-action:not(.btn-terminal-action):not(.btn-sftp-action):hover { background: #6d28d9; } + .connection-list { flex: 1; overflow-y: auto; diff --git a/src/static/webssh.js b/src/static/webssh.js index 6f5a82a..a8b42ff 100644 --- a/src/static/webssh.js +++ b/src/static/webssh.js @@ -5,19 +5,16 @@ let currentConnectionId = null; let authToken = localStorage.getItem('strixwebssh_token'); let currentUser = localStorage.getItem('strixwebssh_user'); let storedUsers = {}; // Will be loaded from /api/config +let maxUploadSize = localStorage.getItem('maxUploadSize') ? parseInt(localStorage.getItem('maxUploadSize')) : 100 * 1024 * 1024; // Dynamically determine API URL based on current host (same origin since everything is in container) let apiBaseUrl = window.location.origin; let backendWsPort = null; let backendWsHost = null; -// Use the backend WebSocket port reported by the API config. +// Use the frontend proxy so the browser does not need direct access to the backend port. function getWsUrl() { const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; - const host = backendWsHost || window.location.hostname || 'localhost'; - if (backendWsPort) { - return `${protocol}//${host}:${backendWsPort}`; - } - return `${protocol}//${host}:8081`; + return `${protocol}//${window.location.host}/ws`; } const CONNECTIONS_KEY = 'strixwebssh_connections'; @@ -380,10 +377,15 @@ function toggleEditModalAuth() { } function toggleAuthFields() { - const authType = document.getElementById('authType').value; - document.getElementById('password').style.display = authType === 'key' ? 'none' : 'block'; + const authTypeField = document.getElementById('authType'); + const passwordField = document.getElementById('password'); + if (!authTypeField || !passwordField) return; + + const authType = authTypeField.value; + passwordField.style.display = authType === 'key' ? 'none' : 'block'; // Don't auto-show key section - let user control it with toggle button - document.getElementById('keySection').classList.remove('active'); + const keySection = document.getElementById('keySection'); + if (keySection) keySection.classList.remove('active'); } function toggleKeyField() { @@ -441,6 +443,7 @@ function renderConnectionList() { : (conn.socket && conn.socket.readyState === WebSocket.CONNECTING ? 'Loading...' : '—'); const item = document.createElement('div'); item.className = `connection-item ${conn.id === currentConnectionId ? 'active' : ''} ${conn.connected ? 'connected' : 'disconnected'}`; + item.dataset.connectionId = conn.id; item.onclick = () => selectConnection(conn.id); item.innerHTML = `
@@ -456,7 +459,7 @@ function renderConnectionList() { ? `` : `` } - +
`; @@ -468,16 +471,22 @@ function selectConnection(id) { currentConnectionId = id; const conn = getCurrentConnection(); if (conn) { - document.getElementById('connName').value = conn.name; - document.getElementById('host').value = conn.host; - document.getElementById('port').value = conn.port; - document.getElementById('user').value = conn.username; - document.getElementById('authType').value = conn.authType || 'password'; - document.getElementById('password').value = conn.password || ''; - document.getElementById('sshKey').value = conn.key || ''; - document.getElementById('lockTimeout').value = conn.lockTimeout || 15; - document.getElementById('disconnectTimeout').value = conn.disconnectTimeout || 60; - toggleAuthFields(); + const quickConnectFields = { + connName: conn.name, + host: conn.host, + port: conn.port, + user: conn.username, + authType: conn.authType || 'password', + password: conn.password || '', + sshKey: conn.key || '', + lockTimeout: conn.lockTimeout || 15, + disconnectTimeout: conn.disconnectTimeout || 60 + }; + Object.entries(quickConnectFields).forEach(([fieldId, value]) => { + const field = document.getElementById(fieldId); + if (field) field.value = value; + }); + if (document.getElementById('authType')) toggleAuthFields(); document.getElementById('terminalTitle').textContent = `${conn.name} - ${conn.username}@${conn.host}:${conn.port}`; updateTerminalStatus(conn.connected ? 'connected' : 'disconnected'); @@ -776,8 +785,6 @@ let sftpSelectedFile = null; let sftpSelectedFileIsDir = false; let sftpUploadQueue = []; let currentSftpConnection = null; -let maxUploadSize = localStorage.getItem('maxUploadSize') ? parseInt(localStorage.getItem('maxUploadSize')) : 100 * 1024 * 1024; // Try to get from localStorage, default 100MB - // Load configuration from API function loadConfig() { console.log('loadConfig() called, token exists:', !!localStorage.getItem('strixwebssh_token')); @@ -1325,20 +1332,25 @@ function toggleSftpView() { const sftpView = document.getElementById('sftpView'); const sftpToggleBtn = document.getElementById('sftpToggleBtn'); - if (sftpView.style.display === 'none') { + if (window.getComputedStyle(sftpView).display === 'none') { // Show SFTP, hide terminal - const conn = getCurrentConnection(); + const conn = getCurrentConnection() || connections.find(connection => + connection.id === document.querySelector('.connection-item.active')?.dataset.connectionId + ); if (!conn) { alert('Please select a connection first'); return; } + + if (currentConnectionId !== conn.id) { + selectConnection(conn.id); + } currentSftpConnection = conn; terminalView.style.display = 'none'; sftpView.style.display = 'flex'; sftpToggleBtn.classList.add('active'); - sftpToggleBtn.textContent = '💻 Terminal'; - sftpToggleBtn.title = 'Switch to Terminal'; + document.getElementById('terminalToggleBtn').classList.remove('active'); // Connect SFTP sftpConnect(conn); @@ -1347,8 +1359,7 @@ function toggleSftpView() { terminalView.style.display = 'flex'; sftpView.style.display = 'none'; sftpToggleBtn.classList.remove('active'); - sftpToggleBtn.textContent = '📁 SFTP'; - sftpToggleBtn.title = 'Switch to File Manager'; + document.getElementById('terminalToggleBtn').classList.add('active'); } } @@ -1361,8 +1372,7 @@ function hideSftpPanel() { terminalView.style.display = 'flex'; sftpView.style.display = 'none'; sftpToggleBtn.classList.remove('active'); - sftpToggleBtn.textContent = '📁 SFTP'; - sftpToggleBtn.title = 'Switch to File Manager'; + document.getElementById('terminalToggleBtn').classList.add('active'); } function showSftpPanel() {