fix: improve UI and login functionality
Build and Push Multi-Arch Docker Image / build (push) Has been cancelled
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:
1 parent
21e03aa990
commit
ae023525ec
4 files changed
+88
-44
No files matched your search
+19
-12
@@ -19,18 +19,20 @@
|
||||
<div class="modal-overlay active" id="loginModal">
|
||||
<div class="modal">
|
||||
<h3>StrixWebSSH Login</h3>
|
||||
<div class="form-group">
|
||||
<label>Username</label>
|
||||
<input id="loginUsername" placeholder="admin">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Password</label>
|
||||
<input type="password" id="loginPassword" placeholder="Password">
|
||||
</div>
|
||||
<div id="loginError" style="color: #ef4444; font-size: 13px; margin-bottom: 10px;"></div>
|
||||
<div class="modal-actions">
|
||||
<button onclick="login()">Login</button>
|
||||
</div>
|
||||
<form onsubmit="event.preventDefault(); login()">
|
||||
<div class="form-group">
|
||||
<label>Username</label>
|
||||
<input id="loginUsername" placeholder="admin">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Password</label>
|
||||
<input type="password" id="loginPassword" placeholder="Password">
|
||||
</div>
|
||||
<div id="loginError" style="color: #ef4444; font-size: 13px; margin-bottom: 10px;"></div>
|
||||
<div class="modal-actions">
|
||||
<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>
|
||||
|
||||
@@ -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
@@ -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() {
|
||||
|
||||
Reference in new issue
Block a user