Full-stack implementation: FastAPI backend with PAM auth, WebSocket stats/terminal, and vanilla JS frontend with tiling desktop shell. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
53 lines
1.7 KiB
JavaScript
53 lines
1.7 KiB
JavaScript
/* Atlus — Login screen logic */
|
|
(function () {
|
|
'use strict';
|
|
|
|
// Redirect to desktop if already authenticated
|
|
const token = sessionStorage.getItem('atlus_token');
|
|
if (token) {
|
|
window.location.href = '/desktop';
|
|
return;
|
|
}
|
|
|
|
const form = document.getElementById('loginForm');
|
|
const usernameInput = document.getElementById('username');
|
|
const passwordInput = document.getElementById('password');
|
|
const loginBtn = document.getElementById('loginBtn');
|
|
const errorEl = document.getElementById('loginError');
|
|
|
|
form.addEventListener('submit', async (e) => {
|
|
e.preventDefault();
|
|
errorEl.textContent = '';
|
|
loginBtn.disabled = true;
|
|
loginBtn.textContent = 'Signing in…';
|
|
|
|
try {
|
|
const res = await fetch('/api/auth/login', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({
|
|
username: usernameInput.value.trim(),
|
|
password: passwordInput.value,
|
|
}),
|
|
});
|
|
|
|
if (!res.ok) {
|
|
const data = await res.json().catch(() => ({}));
|
|
throw new Error(data.detail || 'Invalid credentials');
|
|
}
|
|
|
|
const data = await res.json();
|
|
sessionStorage.setItem('atlus_token', data.token);
|
|
sessionStorage.setItem('atlus_user', data.username);
|
|
window.location.href = '/desktop';
|
|
} catch (err) {
|
|
errorEl.textContent = err.message;
|
|
loginBtn.disabled = false;
|
|
loginBtn.textContent = 'Sign In';
|
|
passwordInput.value = '';
|
|
passwordInput.focus();
|
|
}
|
|
});
|
|
|
|
usernameInput.focus();
|
|
})();
|