(() => {
const STORAGE_KEYS = {
theme: 'altin.theme',
token: 'altin.token',
serverUrl: 'altin.serverUrl',
savedServers: 'altin.savedServers',
};
const state = {
pendingLogin: null,
};
const els = {};
const readJSON = (key, fallback) => {
try {
const raw = localStorage.getItem(key);
return raw ? JSON.parse(raw) : fallback;
} catch {
return fallback;
}
};
const writeJSON = (key, value) => {
localStorage.setItem(key, JSON.stringify(value));
};
const normalizeServer = (value) => value.trim().replace(/\/+$/, '');
const getTheme = () => localStorage.getItem(STORAGE_KEYS.theme) || 'dark';
const setTheme = (theme) => {
const nextTheme = theme === 'light' ? 'light' : 'dark';
document.documentElement.setAttribute('data-theme', nextTheme);
localStorage.setItem(STORAGE_KEYS.theme, nextTheme);
if (els.themeToggle) {
els.themeToggle.textContent = nextTheme === 'dark' ? 'Light' : 'Dark';
els.themeToggle.setAttribute('aria-label', `Switch to ${nextTheme === 'dark' ? 'light' : 'dark'} theme`);
}
};
const showMessage = (message, tone = '') => {
if (!els.loginMessage) return;
els.loginMessage.textContent = message;
els.loginMessage.dataset.tone = tone;
};
const getSavedServers = () => {
const servers = readJSON(STORAGE_KEYS.savedServers, []);
return Array.isArray(servers) ? servers : [];
};
const saveServerEntry = ({ url, username }) => {
const servers = getSavedServers();
const normalizedUrl = normalizeServer(url);
const existingIndex = servers.findIndex((entry) => entry.url === normalizedUrl);
const entry = {
url: normalizedUrl,
label: normalizedUrl,
lastUsed: new Date().toISOString(),
};
if (existingIndex >= 0) {
servers[existingIndex] = entry;
} else {
servers.unshift(entry);
}
writeJSON(STORAGE_KEYS.savedServers, servers.slice(0, 10));
};
const renderSavedServers = () => {
const servers = getSavedServers();
if (!els.savedServerWrap || !els.savedServer) return;
if (!servers.length) {
els.savedServerWrap.classList.add('hidden');
els.savedServer.innerHTML = '';
return;
}
els.savedServerWrap.classList.remove('hidden');
els.savedServer.innerHTML = [
'',
...servers.map((entry, index) => ``),
].join('');
};
const applyServerSelection = (index) => {
const servers = getSavedServers();
if (index === '' || index == null) return;
const entry = servers[Number(index)];
if (!entry) return;
els.serverUrl.value = entry.url;
showMessage(`Loaded ${entry.label || entry.url}`, '');
};
const goHome = () => {
window.location.href = '/index.html';
};
const persistAuth = ({ token, url, username }) => {
localStorage.setItem(STORAGE_KEYS.token, token);
localStorage.setItem(STORAGE_KEYS.serverUrl, normalizeServer(url));
};
const login = async (event) => {
event.preventDefault();
showMessage('Signing in…');
const url = normalizeServer(els.serverUrl.value);
const username = els.username.value.trim();
const password = els.password.value;
if (!url || !username || !password) {
showMessage('All fields are required.', 'error');
return;
}
try {
const response = await fetch(`${url}/api/login`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ username, password }),
});
const payload = await response.json().catch(() => ({}));
if (!response.ok) {
throw new Error(payload.message || 'Login failed.');
}
persistAuth({ token: payload.token, url, username });
if (els.rememberServer?.checked) {
saveServerEntry({ url, username });
renderSavedServers();
}
showMessage('Login successful.', 'success');
goHome();
} catch (error) {
showMessage(error.message || 'Unable to reach the server.', 'error');
}
};
const initializeThemeToggle = () => {
setTheme(getTheme());
els.themeToggle?.addEventListener('click', () => {
setTheme(getTheme() === 'dark' ? 'light' : 'dark');
});
};
const initializeSavedServerPicker = () => {
renderSavedServers();
els.savedServer?.addEventListener('change', (event) => applyServerSelection(event.target.value));
};
const initializeLoginHandlers = () => {
els.loginForm?.addEventListener('submit', login);
};
const restoreDefaults = () => {
const serverUrl = localStorage.getItem(STORAGE_KEYS.serverUrl);
if (serverUrl) {
els.serverUrl.value = serverUrl;
}
};
const cacheElements = () => {
els.themeToggle = document.getElementById('themeToggle');
els.loginForm = document.getElementById('loginForm');
els.serverUrl = document.getElementById('serverUrl');
els.username = document.getElementById('username');
els.password = document.getElementById('password');
els.loginMessage = document.getElementById('loginMessage');
els.savedServerWrap = document.getElementById('savedServerWrap');
els.savedServer = document.getElementById('savedServer');
els.rememberServer = document.getElementById('rememberServer');
};
document.addEventListener('DOMContentLoaded', () => {
cacheElements();
initializeThemeToggle();
restoreDefaults();
initializeSavedServerPicker();
initializeLoginHandlers();
showMessage('Choose a server and sign in to continue.');
});
})();