Backend: - UserRecord Model + Users-Tabelle in DB - AuthService: SHA256-Passwort-Hashing, Token-Verwaltung (ConcurrentDictionary) - AuthMiddleware: Schützt alle /api/-Routes (außer /api/auth/login) - AuthEndpoint: Login, Logout, Me - Default-User admin/admin wird automatisch erstellt Frontend: - Login-Page mit Formular (Enter-Key-Support) - Globaler Fetch-Interceptor: 401 → automatisch Login-Redirect - Auth-Check beim App-Start und bei Hash-Änderungen - Logout-Link im Dashboard-Header - Login-CSS: Zentrierte Login-Box
50 lines
1.3 KiB
JavaScript
50 lines
1.3 KiB
JavaScript
// ── Login Page ──
|
|
|
|
registerPage('login', {
|
|
init() {
|
|
const usernameInput = $('#loginUsername');
|
|
const passwordInput = $('#loginPassword');
|
|
const loginBtn = $('#loginBtn');
|
|
const errorDiv = $('#loginError');
|
|
|
|
async function doLogin() {
|
|
errorDiv.hidden = true;
|
|
loginBtn.disabled = true;
|
|
loginBtn.textContent = 'Anmelden...';
|
|
|
|
try {
|
|
var username = usernameInput.value.trim();
|
|
var password = passwordInput.value;
|
|
|
|
if (!username || !password) {
|
|
errorDiv.textContent = 'Benutzername und Passwort eingeben';
|
|
errorDiv.hidden = false;
|
|
return;
|
|
}
|
|
|
|
await apiLogin(username, password);
|
|
navigateTo('dashboard');
|
|
} catch (err) {
|
|
errorDiv.textContent = err.message || 'Login fehlgeschlagen';
|
|
errorDiv.hidden = false;
|
|
passwordInput.value = '';
|
|
passwordInput.focus();
|
|
} finally {
|
|
loginBtn.disabled = false;
|
|
loginBtn.textContent = 'Anmelden';
|
|
}
|
|
}
|
|
|
|
loginBtn.addEventListener('click', doLogin);
|
|
|
|
usernameInput.addEventListener('keydown', function(e) {
|
|
if (e.key === 'Enter') passwordInput.focus();
|
|
});
|
|
|
|
passwordInput.addEventListener('keydown', function(e) {
|
|
if (e.key === 'Enter') doLogin();
|
|
});
|
|
},
|
|
destroy() {}
|
|
});
|