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
+
Sign in with your access token
+
+
+
+
+
+
+
@@ -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() {