fix: improve UI and login functionality
Build and Push Multi-Arch Docker Image / build (push) Has been cancelled

* update UI components and layout
* fix login form functionality
* pin Docker base image to python:3.11-slim-bookworm
This commit is contained in:
dido committed 2026-09-11 10:41:43 +03:00
1 parent 21e03aa990
commit ae023525ec
4 files changed
+77 -33

No files matched your search

+2 -2
View File
@@ -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 \
+8 -1
View File
@@ -19,6 +19,7 @@
<div class="modal-overlay active" id="loginModal">
<div class="modal">
<h3>StrixWebSSH Login</h3>
<form onsubmit="event.preventDefault(); login()">
<div class="form-group">
<label>Username</label>
<input id="loginUsername" placeholder="admin">
@@ -29,8 +30,9 @@
</div>
<div id="loginError" style="color: #ef4444; font-size: 13px; margin-bottom: 10px;"></div>
<div class="modal-actions">
<button onclick="login()">Login</button>
<button type="submit">Login</button>
</div>
</form>
</div>
</div>
@@ -42,6 +44,11 @@
<span id="loggedInUser" style="font-size: 12px; color: #10b981;"></span>
<button class="btn-small" onclick="logout()" style="background: #374151; color: #9ca3af;">Logout</button>
</div>
<div class="sidebar-actions">
<button id="terminalToggleBtn" class="btn-sidebar-action btn-terminal-action active" onclick="hideSftpPanel()" title="Switch to Terminal">💻 Terminal</button>
<button id="sftpToggleBtn" class="btn-sidebar-action btn-sftp-action" onclick="toggleSftpView()" title="Switch to File Manager">📁 SFTP</button>
<button class="btn-sidebar-action" onclick="showHelp()" title="Open Help">❔ Help</button>
</div>
<button class="btn-add" onclick="showNewConnectionModal()">+ New Connection</button>
<button class="btn-recordings" onclick="showRecordings()" title="Session Recordings">🎥 Recordings</button>
</div>
+27
View File
@@ -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;
+40 -30
View File
@@ -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 = `
<div class="connection-heading">
@@ -456,7 +459,7 @@ function renderConnectionList() {
? `<button class="btn-small btn-disconnect" onclick="disconnectConnection('${conn.id}')">Disconnect</button>`
: `<button class="btn-small btn-connect" onclick="connectConnection('${conn.id}')">Connect</button>`
}
<button class="btn-small btn-settings" onclick="showEditConnectionModal('${conn.id}')">Settings</button>
<button class="btn-small btn-edit" onclick="showEditConnectionModal('${conn.id}')">Edit</button>
<button class="btn-small btn-delete" onclick="deleteConnection('${conn.id}')">Delete</button>
</div>
`;
@@ -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() {