/* Login page — external script, no inline handlers. * Loaded by the /login route. Reads data attributes from the form for * i18n strings so the server does not need to inject JS literals. */ interface LoginForm extends HTMLFormElement { 'data-invalid-pw': string; 'data-conn-failed': string; } document.addEventListener('DOMContentLoaded', function () { const form = document.getElementById('login-form') as LoginForm | null; const input = document.getElementById('pw') as HTMLInputElement | null; if (!form || !input) return; const invalidPw = form.getAttribute('data-invalid-pw') || 'Invalid password'; const connFailed = form.getAttribute('data-conn-failed') || 'Connection failed'; function showErr(msg: string): void { const err = document.getElementById('err'); if (err) { err.textContent = msg; err.style.display = 'block'; } } function hideErr(): void { const err = document.getElementById('err'); if (err) { err.style.display = 'none'; } } async function doLogin(e: Event): Promise { e.preventDefault(); const pw = input!.value; hideErr(); try { const res = await fetch('api/auth/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ password: pw }), credentials: 'include', }); let data: { ok?: boolean; error?: string } = {}; try { data = await res.json(); } catch (_) { /* ignore */ } if (res.ok && data.ok) { window.location.href = './'; } else { showErr(data.error || invalidPw); } } catch (ex) { showErr(connFailed); } } form.addEventListener('submit', doLogin); input.addEventListener('keydown', function (e: KeyboardEvent) { if (e.key === 'Enter') { e.preventDefault(); doLogin(e); } }); });