/* FluentGerman.ai — API client with auth */ const API_BASE = '/api'; function getToken() { return localStorage.getItem('fg_token'); } function setToken(token) { localStorage.setItem('fg_token', token); } function clearToken() { localStorage.removeItem('fg_token'); } function getUser() { const data = localStorage.getItem('fg_user'); return data ? JSON.parse(data) : null; } function setUser(user) { localStorage.setItem('fg_user', JSON.stringify(user)); } function clearUser() { localStorage.removeItem('fg_user'); } async function api(path, options = {}) { const token = getToken(); const headers = { ...options.headers }; if (token) { headers['Authorization'] = `Bearer ${token}`; } // Don't set Content-Type for FormData (browser sets it with boundary) if (!(options.body instanceof FormData)) { headers['Content-Type'] = headers['Content-Type'] || 'application/json'; } const response = await fetch(`${API_BASE}${path}`, { ...options, headers }); if (response.status === 401) { clearToken(); clearUser(); window.location.href = '/'; return; } return response; } async function apiJSON(path, options = {}) { const response = await api(path, options); if (!response || !response.ok) { const error = await response?.json().catch(() => ({ detail: 'Request failed' })); throw new Error(error.detail || 'Request failed'); } return response.json(); } function requireAuth() { if (!getToken()) { window.location.href = '/'; return false; } return true; } function requireAdmin() { const user = getUser(); if (!user?.is_admin) { window.location.href = '/chat.html'; return false; } return true; } function logout() { clearToken(); clearUser(); window.location.href = '/'; } /* Toast notifications */ function showToast(message, type = 'success') { let container = document.querySelector('.toast-container'); if (!container) { container = document.createElement('div'); container.className = 'toast-container'; document.body.appendChild(container); } const toast = document.createElement('div'); toast.className = `toast toast-${type}`; toast.textContent = message; container.appendChild(toast); setTimeout(() => { toast.style.opacity = '0'; setTimeout(() => toast.remove(), 300); }, 3000); }