From f728adb76d6ec46b594daf22b7516fefecb1bf7b Mon Sep 17 00:00:00 2001 From: gitomarcourt Date: Fri, 3 Jul 2026 18:28:45 +0200 Subject: [PATCH] ui: proper login page + inline SVG favicon Replaced window.prompt() with a real login screen: - Centered card, brand mark, hidden password-type input - Inline error state on invalid token - Overlay shown until token validated, then app rendered Favicon: inline data-URI SVG with concentric radio-wave rings, zero external requests, no /favicon.ico 404 in logs. --- dashboard/static/index.html | 70 ++++++++++++++++++++++++++++++------- 1 file changed, 58 insertions(+), 12 deletions(-) diff --git a/dashboard/static/index.html b/dashboard/static/index.html index c50fa8f..ddf65b7 100644 --- a/dashboard/static/index.html +++ b/dashboard/static/index.html @@ -4,6 +4,7 @@ SNEK Dashboard + @@ -18,7 +19,32 @@ -
+ + + + +
@@ -78,20 +104,40 @@ const TOKEN_KEY = 'snek_dashboard_token'; let token = localStorage.getItem(TOKEN_KEY) || ''; async function ensureToken() { - // Probe if the server requires a token const probe = await fetch('/api/devices', { headers: authHeaders() }); if (probe.ok) return; if (probe.status !== 401) return; - while (true) { - const t = window.prompt('Access token'); - if (t === null) return; - const r = await fetch('/api/devices', { headers: { Authorization: 'Bearer ' + t } }); - if (r.ok) { - token = t.trim(); - localStorage.setItem(TOKEN_KEY, token); - return; - } - } + + const overlay = document.getElementById('login-overlay'); + const form = document.getElementById('login-form'); + const input = document.getElementById('login-token'); + const error = document.getElementById('login-error'); + const app = document.getElementById('app'); + + overlay.className = 'fixed inset-0 bg-slate-100 z-50 flex items-center justify-center px-4'; + app.classList.add('hidden'); + input.focus(); + + return new Promise((resolve) => { + form.addEventListener('submit', async (e) => { + e.preventDefault(); + const t = input.value.trim(); + error.classList.add('hidden'); + const r = await fetch('/api/devices', { headers: { Authorization: 'Bearer ' + t } }); + if (r.ok) { + token = t; + localStorage.setItem(TOKEN_KEY, token); + overlay.className = 'hidden'; + app.classList.remove('hidden'); + resolve(); + } else { + error.textContent = 'Invalid token. Please try again.'; + error.classList.remove('hidden'); + input.value = ''; + input.focus(); + } + }); + }); } function authHeaders() {