feat: implement user authentication and login modal, refactor backend

This commit is contained in:
Omar Sánchez Pizarro
2026-01-20 00:39:28 +01:00
parent 9a61f16959
commit e99424c9ba
29 changed files with 3061 additions and 855 deletions

View File

@@ -0,0 +1,95 @@
// Servicio de autenticación para gestionar credenciales
const AUTH_STORAGE_KEY = 'wallabicher_auth';
class AuthService {
constructor() {
this.credentials = this.loadCredentials();
}
// Cargar credenciales desde localStorage
loadCredentials() {
try {
const stored = localStorage.getItem(AUTH_STORAGE_KEY);
if (stored) {
const parsed = JSON.parse(stored);
return {
username: parsed.username || '',
password: parsed.password || '',
};
}
} catch (error) {
console.error('Error cargando credenciales:', error);
}
return { username: '', password: '' };
}
// Guardar credenciales en localStorage
saveCredentials(username, password) {
try {
this.credentials = { username, password };
localStorage.setItem(AUTH_STORAGE_KEY, JSON.stringify(this.credentials));
return true;
} catch (error) {
console.error('Error guardando credenciales:', error);
return false;
}
}
// Eliminar credenciales
clearCredentials() {
try {
this.credentials = { username: '', password: '' };
localStorage.removeItem(AUTH_STORAGE_KEY);
return true;
} catch (error) {
console.error('Error eliminando credenciales:', error);
return false;
}
}
// Obtener credenciales actuales
getCredentials() {
return { ...this.credentials };
}
// Verificar si hay credenciales guardadas
hasCredentials() {
return !!(this.credentials.username && this.credentials.password);
}
// Generar header de autenticación Basic
getAuthHeader() {
if (!this.hasCredentials()) {
return null;
}
const { username, password } = this.credentials;
const encoded = btoa(`${username}:${password}`);
return `Basic ${encoded}`;
}
// Validar credenciales (test básico)
async validateCredentials(username, password) {
try {
// Intentar hacer una petición simple para validar las credenciales
const encoded = btoa(`${username}:${password}`);
const response = await fetch('/api/stats', {
method: 'GET',
headers: {
'Authorization': `Basic ${encoded}`,
},
});
// Si la petición funciona, las credenciales son válidas
// Nota: stats no requiere auth, pero podemos usar cualquier endpoint
return response.ok || response.status !== 401;
} catch (error) {
return false;
}
}
}
// Exportar instancia singleton
const authService = new AuthService();
export default authService;