/** * Authentication utilities (shared across all pages) * Requires: window.API_BASE to be defined before loading this script */ (function() { 'use strict'; // Auth state window.authToken = localStorage.getItem('token') || ''; window.currentUser = JSON.parse(localStorage.getItem('user') || 'null'); /** * Generic API call helper with auth header */ window.api = async function(path, options = {}) { try { const res = await fetch((window.API_BASE || '') + path, { ...options, headers: { 'Content-Type': 'application/json', ...(window.authToken && { 'Authorization': 'Bearer ' + window.authToken }), ...options.headers } }); if (res.status === 401) { // Token expired - try refresh const refreshed = await refreshToken(); if (refreshed) { return window.api(path, options); } handleAuthExpired(); throw new Error('Unauthorized'); } return res; } catch (error) { console.error('API call failed:', error); throw error; } }; /** * Refresh token */ async function refreshToken() { const rt = localStorage.getItem('refreshToken'); if (!rt) return false; try { const res = await fetch((window.API_BASE || '') + '/api/auth/refresh', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ refreshToken: rt }) }); if (!res.ok) return false; const data = await res.json(); window.authToken = data.accessToken; localStorage.setItem('token', data.accessToken); localStorage.setItem('refreshToken', data.refreshToken); return true; } catch { return false; } } function handleAuthExpired() { localStorage.removeItem('token'); localStorage.removeItem('refreshToken'); localStorage.removeItem('user'); window.authToken = ''; window.currentUser = null; } /** * Show login modal */ window.showLoginModal = function() { var modal = document.getElementById('loginModal'); if (modal) modal.classList.remove('hidden'); }; window.showLogin = window.showLoginModal; /** * Hide login modal */ window.hideLoginModal = function() { var modal = document.getElementById('loginModal'); if (modal) modal.classList.add('hidden'); }; /** * Show login error message */ window.showLoginError = function(msg) { var el = document.getElementById('loginError'); if (!el) return; el.textContent = msg; el.classList.remove('hidden'); setTimeout(function() { el.classList.add('hidden'); }, 3000); }; /** * Send verification code */ window.sendCode = async function() { var phoneInput = document.getElementById('phoneInput'); if (!phoneInput) return; var phone = phoneInput.value.trim(); if (!/^1[3-9]\d{9}$/.test(phone)) { window.showLoginError('Please enter a valid phone number'); return; } try { var res = await fetch((window.API_BASE || '') + '/api/auth/send-code', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ phone: phone }) }); var data = await res.json(); if (!res.ok) { window.showLoginError(data.error || 'Failed to send code'); return; } // Start countdown var btn = document.getElementById('sendCodeBtn'); if (btn) { var seconds = 60; btn.disabled = true; var timer = setInterval(function() { seconds--; btn.textContent = seconds + 's'; if (seconds <= 0) { clearInterval(timer); btn.textContent = 'Send Code'; btn.disabled = false; } }, 1000); } } catch (err) { window.showLoginError('Network error'); } }; /** * Verify code and login */ window.verifyCode = async function() { var phoneInput = document.getElementById('phoneInput'); var codeInput = document.getElementById('codeInput'); if (!phoneInput || !codeInput) return; var phone = phoneInput.value.trim(); var code = codeInput.value.trim(); try { var res = await fetch((window.API_BASE || '') + '/api/auth/verify-code', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ phone: phone, code: code }) }); var data = await res.json(); if (!res.ok) { window.showLoginError(data.error || 'Verification failed'); return; } window.authToken = data.accessToken; window.currentUser = data.user; localStorage.setItem('token', data.accessToken); localStorage.setItem('refreshToken', data.refreshToken); localStorage.setItem('user', JSON.stringify(data.user)); window.hideLoginModal(); if (typeof window.onLoginSuccess === 'function') { window.onLoginSuccess(data.user); } else { location.reload(); } } catch (err) { window.showLoginError('Network error'); } }; /** * Logout */ window.logout = function() { localStorage.removeItem('token'); localStorage.removeItem('refreshToken'); localStorage.removeItem('user'); window.authToken = ''; window.currentUser = null; location.reload(); }; })();