auth: add login page and shared auth.js

This commit is contained in:
Ivo Oskamp 2026-05-28 16:06:33 +02:00
parent 8b842f5d74
commit 646fa747ab
3 changed files with 87 additions and 0 deletions

View File

@ -0,0 +1,22 @@
(function (global) {
async function postJson(url, body) {
const r = await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'same-origin',
body: JSON.stringify(body),
});
let data = null;
try { data = await r.json(); } catch (_) {}
return { ok: r.ok, status: r.status, data };
}
async function getJson(url) {
const r = await fetch(url, { credentials: 'same-origin' });
let data = null;
try { data = await r.json(); } catch (_) {}
return { ok: r.ok, status: r.status, data };
}
global.ClearviewAuth = { postJson, getJson };
})(window);

View File

@ -0,0 +1,49 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Clearview — Sign in</title>
<link rel="stylesheet" href="/styles.css" />
</head>
<body class="auth-page">
<main class="auth-card">
<h1>Clearview</h1>
<p class="auth-sub">Sign in to continue</p>
<form id="loginForm">
<label>Username<input name="username" autocomplete="username" required autofocus /></label>
<label>Password<input name="password" type="password" autocomplete="current-password" required /></label>
<label class="auth-remember"><input name="remember" type="checkbox" /> Remember me for 30 days</label>
<button type="submit">Sign in</button>
<p id="loginError" class="auth-error" hidden></p>
</form>
</main>
<script src="/auth.js"></script>
<script>
(async function () {
const setup = await ClearviewAuth.getJson('/api/auth/setup-required');
if (setup.ok && setup.data && setup.data.setup_required) {
window.location.replace('/setup.html');
return;
}
const form = document.getElementById('loginForm');
const err = document.getElementById('loginError');
form.addEventListener('submit', async (ev) => {
ev.preventDefault();
err.hidden = true;
const fd = new FormData(form);
const res = await ClearviewAuth.postJson('/api/auth/login', {
username: fd.get('username'),
password: fd.get('password'),
remember: fd.get('remember') === 'on',
});
if (res.ok) {
window.location.replace('/');
} else {
err.textContent = (res.data && res.data.detail) || 'Sign-in failed';
err.hidden = false;
}
});
})();
</script>
</body>
</html>

View File

@ -752,3 +752,19 @@ strong {
display: none; display: none;
} }
} }
/* === Auth (login / setup) pages and header badge ============================== */
.auth-page { display: flex; align-items: center; justify-content: center; min-height: 100vh; background: #0f1115; margin: 0; }
.auth-card { width: 360px; max-width: 92vw; padding: 28px; background: #1a1d24; border-radius: 12px; box-shadow: 0 8px 28px rgba(0,0,0,.35); color: #e6e8ee; font-family: system-ui, sans-serif; }
.auth-card h1 { margin: 0 0 4px; font-size: 22px; }
.auth-sub { margin: 0 0 18px; opacity: .75; }
.auth-card form { display: flex; flex-direction: column; gap: 12px; }
.auth-card label { display: flex; flex-direction: column; gap: 4px; font-size: 13px; }
.auth-card input[type=text], .auth-card input[type=password], .auth-card input:not([type]) { padding: 8px 10px; background: #0e1116; border: 1px solid #2a2f3a; border-radius: 6px; color: inherit; }
.auth-card .auth-remember { flex-direction: row; align-items: center; gap: 8px; font-size: 13px; }
.auth-card button { padding: 10px; background: #3b82f6; border: 0; border-radius: 6px; color: #fff; font-weight: 600; cursor: pointer; }
.auth-card button:hover { background: #2563eb; }
.auth-error { background: #3a1f25; color: #fda4af; padding: 8px 10px; border-radius: 6px; font-size: 13px; }
.user-badge { display: inline-flex; align-items: center; gap: 8px; padding: 4px 10px; border: 1px solid #2a2f3a; border-radius: 999px; font-size: 12px; }
.user-badge button { background: transparent; border: 0; color: #93c5fd; cursor: pointer; padding: 0; }
.user-badge button:hover { text-decoration: underline; }