const API_URL = 'http://localhost:3000'; // --- DOM ELEMENTS --- const authView = document.getElementById('auth-view'); const dashboardView = document.getElementById('dashboard-view'); const loginForm = document.getElementById('login-form'); const registerForm = document.getElementById('register-form'); const entriesGrid = document.getElementById('entries-grid'); const modalOverlay = document.getElementById('modal-overlay'); const userEmailDisplay = document.getElementById('user-email-display'); // --- INITIALIZATION --- async function init() { // Hack pour vérifier si on est loggué ou non, afin d'éviter une erreur 401 sur /entries au démarrage. const isLoggedIn = localStorage.getItem('is_logged_in'); if (!isLoggedIn) { // Cas 1 : Première visite ou déconnecté proprement showAuth(); } else { // Cas 2 : On pense être connecté, on vérifie auprès du serveur // Petit hack UX : on stocke l'email dans localStorage (non sensible) // juste pour l'affichage, car on ne souhaite pas stocker le token JWT côté client. const savedEmail = localStorage.getItem('user_email_display'); if(savedEmail) userEmailDisplay.innerText = savedEmail; await loadEntries(true); } } // --- VIEW MANAGEMENT --- function showAuth() { authView.classList.add('active'); authView.classList.remove('hidden'); dashboardView.classList.remove('active'); dashboardView.classList.add('hidden'); } function showDashboard() { authView.classList.remove('active'); authView.classList.add('hidden'); dashboardView.classList.add('active'); dashboardView.classList.remove('hidden'); } function switchTab(tab) { const loginBtn = document.querySelector('.tab-btn:nth-child(1)'); const registerBtn = document.querySelector('.tab-btn:nth-child(2)'); if (tab === 'login') { loginForm.classList.remove('hidden'); registerForm.classList.add('hidden'); loginBtn.classList.add('active'); registerBtn.classList.remove('active'); } else { loginForm.classList.add('hidden'); registerForm.classList.remove('hidden'); loginBtn.classList.remove('active'); registerBtn.classList.add('active'); } } // --- API CALLS --- // LOGIN loginForm.addEventListener('submit', async (e) => { e.preventDefault(); const email = document.getElementById('login-email').value; const password = document.getElementById('login-password').value; try { const res = await fetch(`${API_URL}/login`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email, password }), credentials: 'include' // <--- INDISPENSABLE : Envoie/Reçoit les cookies }); const data = await res.json(); if (res.ok) { // On marque qu'on est loggué localStorage.setItem('is_logged_in', 'true'); // On sauvegarde l'email juste pour l'UI (pas de sécurité ici) localStorage.setItem('user_email_display', email); userEmailDisplay.innerText = email; showToast('Connexion réussie', 'success'); showDashboard(); loadEntries(); loginForm.reset(); } else { showToast(data.error || 'Erreur de connexion', 'error'); } } catch (error) { showToast('Erreur serveur', 'error'); } }); // REGISTER registerForm.addEventListener('submit', async (e) => { e.preventDefault(); const email = document.getElementById('register-email').value; const password = document.getElementById('register-password').value; try { const res = await fetch(`${API_URL}/register`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email, password }), credentials: 'include' }); if (res.ok) { showToast('Compte créé ! Connectez-vous.', 'success'); switchTab('login'); registerForm.reset(); } else { const data = await res.json(); showToast(data.error, 'error'); } } catch (error) { showToast('Erreur serveur', 'error'); } }); // LOGOUT (Nouvelle version Serveur) async function logout() { try { // On demande au serveur de supprimer le cookie httpOnly await fetch(`${API_URL}/logout`, { method: 'POST', credentials: 'include' }); } catch (e) { console.error("Erreur réseau logout", e); } finally { // Quoi qu'il arrive (succès ou erreur), on nettoie le client localStorage.removeItem('user_email_display'); localStorage.removeItem('is_logged_in'); showAuth(); showToast('Déconnecté', 'success'); } } // LOAD ENTRIES async function loadEntries(isInit = false) { if(!isInit) entriesGrid.innerHTML = '
Chargement de votre coffre...
'; try { const res = await fetch(`${API_URL}/entries`, { method: 'GET', credentials: 'include' // Envoie le cookie d'auth automatiquement }); if (res.status === 401 || res.status === 403) { // Si non autorisé, on affiche l'écran de login if (!isInit) logout(); else showAuth(); return; } if (isInit) showDashboard(); // Si init réussit, on bascule sur le dashboard const entries = await res.json(); renderEntries(entries); } catch (error) { console.error(error); if(!isInit) entriesGrid.innerHTML = '

Impossible de charger les données.

'; } } function renderEntries(entries) { entriesGrid.innerHTML = ''; if (!entries || entries.length === 0) { entriesGrid.innerHTML = '

Aucun mot de passe enregistré.

'; return; } entries.forEach(entry => { const card = document.createElement('div'); card.className = 'entry-card'; card.innerHTML = `
${entry.title}
${entry.url ? `Ouvrir ↗` : ''}
Identifiant
${entry.username_field}
Mot de passe
`; entriesGrid.appendChild(card); }); } // REVEAL PASSWORD (DECRYPT) async function revealPassword(id, btn) { const input = document.getElementById(`pwd-display-${id}`); if (input.type === 'text') { input.type = 'password'; input.value = '********'; btn.innerText = '👁️'; return; } btn.innerText = '⌛'; try { const res = await fetch(`${API_URL}/entries/${id}/password`, { credentials: 'include' // Le cookie est nécessaire pour déchiffrer }); if (res.ok) { const data = await res.json(); input.type = 'text'; input.value = data.password; btn.innerText = '🔒'; setTimeout(() => { if(input.type === 'text') { input.type = 'password'; input.value = '********'; btn.innerText = '👁️'; } }, 10000); } else { showToast('Erreur déchiffrement', 'error'); btn.innerText = '👁️'; } } catch (e) { showToast('Erreur réseau', 'error'); btn.innerText = '👁️'; } } // CREATE ENTRY document.getElementById('add-entry-form').addEventListener('submit', async (e) => { e.preventDefault(); const payload = { title: document.getElementById('entry-title').value, url: document.getElementById('entry-url').value, username_field: document.getElementById('entry-username').value, password: document.getElementById('entry-password').value }; try { const res = await fetch(`${API_URL}/entries`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload), credentials: 'include' // Important ! }); if (res.ok) { showToast('Mot de passe sécurisé !', 'success'); closeModal(); document.getElementById('add-entry-form').reset(); loadEntries(); } else { showToast('Erreur sauvegarde', 'error'); } } catch (e) { showToast('Erreur serveur', 'error'); } }); // --- UTILS --- function openModal() { modalOverlay.classList.remove('hidden'); } function closeModal() { modalOverlay.classList.add('hidden'); } function showToast(msg, type = 'success') { const container = document.getElementById('toast-container'); const toast = document.createElement('div'); toast.className = `toast ${type}`; toast.innerText = msg; container.appendChild(toast); setTimeout(() => toast.remove(), 3000); } function copyToClipboard(text) { navigator.clipboard.writeText(text).then(() => showToast('Copié !')); } async function copyPasswordFromId(id) { const input = document.getElementById(`pwd-display-${id}`); if (input.type === 'text' && input.value !== '********') { copyToClipboard(input.value); } else { showToast("Affichez d'abord le mot de passe", 'error'); } } // Lancer l'app init();