diff --git a/main.py b/main.py index 277f289..ba0eae5 100644 --- a/main.py +++ b/main.py @@ -80,17 +80,22 @@ def init_db(): ) ''') - # Create default admin if no users exist + # Create or update default admin if no users exist or only one user exists c.execute('SELECT COUNT(*) FROM users') - if c.fetchone()[0] == 0: + count = c.fetchone()[0] + if count == 0: print("Creating default admin user...") admin_hash = get_password_hash(WEB_PASSWORD) c.execute('INSERT INTO users (username, hashed_password, is_admin) VALUES (?, ?, ?)', ('admin', admin_hash, 1)) - - # Associate existing instances with the new admin user - admin_id = c.lastrowid - c.execute('UPDATE instances SET user_id = ? WHERE user_id IS NULL', (admin_id,)) + elif count == 1: + # If there's only one user and it's 'admin', ensure its password matches WEB_PASSWORD + c.execute('SELECT id, username FROM users LIMIT 1') + user = c.fetchone() + if user[1] == 'admin': + print("Ensuring admin password matches WEB_PASSWORD...") + admin_hash = get_password_hash(WEB_PASSWORD) + c.execute('UPDATE users SET hashed_password = ? WHERE id = ?', (admin_hash, user[0])) conn.commit() conn.close() diff --git a/static/app.js b/static/app.js index 81a3ede..2b37740 100644 --- a/static/app.js +++ b/static/app.js @@ -5,10 +5,47 @@ document.addEventListener('DOMContentLoaded', () => { const portInput = document.getElementById('port'); const ufwCmd = document.getElementById('ufw-cmd'); const iptablesCmd = document.getElementById('iptables-cmd'); + const loginOverlay = document.getElementById('login-overlay'); + const loginForm = document.getElementById('login-form'); + const loginError = document.getElementById('login-error'); + const logoutBtn = document.getElementById('logout-btn'); + const mainNav = document.getElementById('main-nav'); + const navAdmin = document.getElementById('nav-admin'); + const usersList = document.getElementById('users-list'); + const createUserForm = document.getElementById('create-user-form'); + const changePasswordForm = document.getElementById('change-password-form'); + const profileSuccess = document.getElementById('profile-success'); let config = { firewall_host_ip: null }; + let currentUser = null; + + const showLogin = () => { + loginOverlay.style.display = 'flex'; + logoutBtn.style.display = 'none'; + mainNav.style.display = 'none'; + }; + + const hideLogin = () => { + loginOverlay.style.display = 'none'; + logoutBtn.style.display = 'flex'; + mainNav.style.display = 'flex'; + }; + + // Tab Switching + document.querySelectorAll('.nav-item').forEach(item => { + item.addEventListener('click', () => { + document.querySelectorAll('.nav-item').forEach(i => i.classList.remove('active')); + document.querySelectorAll('.tab-content').forEach(i => i.classList.remove('active')); + item.classList.add('active'); + document.getElementById(`${item.dataset.tab}-section`).classList.add('active'); + + if (item.dataset.tab === 'admin') fetchUsers(); + if (item.dataset.tab === 'instances') fetchInstances(); + }); + }); const updateFirewallCmds = () => { + if (!ufwCmd || !iptablesCmd) return; const port = portInput.value || '4646'; const hostIp = config.firewall_host_ip || window.location.hostname; ufwCmd.textContent = `sudo ufw allow from ${hostIp} to any port ${port} proto tcp`; @@ -18,6 +55,7 @@ document.addEventListener('DOMContentLoaded', () => { const fetchConfig = async () => { try { const res = await fetch('/api/config'); + if (res.status === 401) return showLogin(); if (res.ok) { config = await res.json(); updateFirewallCmds(); @@ -27,18 +65,10 @@ document.addEventListener('DOMContentLoaded', () => { } }; - portInput.addEventListener('input', updateFirewallCmds); - - // Initial fetch of config and instances - fetchConfig(); - updateFirewallCmds(); - const fetchInstances = async () => { try { const res = await fetch('/api/instances'); - if (res.status === 401) { - return; // Let browser handle auth - } + if (res.status === 401) return showLogin(); const data = await res.json(); renderInstances(data); } catch (e) { @@ -46,6 +76,19 @@ document.addEventListener('DOMContentLoaded', () => { } }; + const fetchUsers = async () => { + try { + const res = await fetch('/api/users'); + if (res.status === 401) return showLogin(); + if (res.ok) { + const users = await res.json(); + renderUsers(users); + } + } catch (e) { + console.error('Failed to fetch users:', e); + } + }; + const renderInstances = (instances) => { if (instances.length === 0) { instancesList.innerHTML = '