/** * Membership System Frontend * Handles authentication, paywall checks, and download tracking */ const getApiBase = () => { const host = window.location.hostname; const isLocal = host === 'localhost' || host === '127.0.0.1' || host.startsWith('10.'); return isLocal ? 'http://' + host + ':5001' : window.location.origin.replace(/\/$/, ''); }; const API_BASE = getApiBase() + '/api'; let authState = { authenticated: false, user: null, token: null }; // Initialize on page load document.addEventListener('DOMContentLoaded', function() { checkAuthStatus(); setupDownloadTracking(); setupContactForm(); }); /** * Check authentication status */ async function checkAuthStatus() { const token = localStorage.getItem('membership_token'); if (!token) { updateAuthUI(false); return; } try { const response = await fetch(`${API_BASE}/auth/check`, { headers: { 'Authorization': `Bearer ${token}` } }); const data = await response.json(); if (data.authenticated) { authState = { authenticated: true, user: data, token: token }; updateAuthUI(true); checkPaywallAccess(); } else { localStorage.removeItem('membership_token'); updateAuthUI(false); } } catch (error) { console.error('Auth check failed:', error); updateAuthUI(false); } } /** * Update UI based on authentication state */ function updateAuthUI(authenticated) { const loginElements = document.querySelectorAll('.membership-login'); const logoutElements = document.querySelectorAll('.membership-logout'); const memberElements = document.querySelectorAll('.membership-only'); const guestElements = document.querySelectorAll('.guest-only'); if (authenticated) { loginElements.forEach(el => el.style.display = 'none'); logoutElements.forEach(el => el.style.display = 'block'); memberElements.forEach(el => el.style.display = 'block'); guestElements.forEach(el => el.style.display = 'none'); // Update user name display const userNameElements = document.querySelectorAll('.user-name'); userNameElements.forEach(el => { if (authState.user && authState.user.name) { el.textContent = authState.user.name; } }); } else { loginElements.forEach(el => el.style.display = 'block'); logoutElements.forEach(el => el.style.display = 'none'); memberElements.forEach(el => el.style.display = 'none'); guestElements.forEach(el => el.style.display = 'block'); } } /** * Check paywall access and show/hide content */ async function checkPaywallAccess() { if (!authState.authenticated) { // Check download count for IP try { const response = await fetch(`${API_BASE}/download/count`); const data = await response.json(); if (data.limit_reached || (data.count >= data.limit && !data.unlimited)) { showPaywall(); } } catch (error) { console.error('Download count check failed:', error); } } } /** * Show paywall message */ function showPaywall() { const paywallElements = document.querySelectorAll('.paywall-message'); paywallElements.forEach(el => { el.style.display = 'block'; }); const paywallContent = document.querySelectorAll('.paywall-content'); paywallContent.forEach(el => { el.style.display = 'none'; }); } /** * Hide paywall message */ function hidePaywall() { const paywallElements = document.querySelectorAll('.paywall-message'); paywallElements.forEach(el => { el.style.display = 'none'; }); const paywallContent = document.querySelectorAll('.paywall-content'); paywallContent.forEach(el => { el.style.display = 'block'; }); } /** * Setup download tracking */ function setupDownloadTracking() { document.addEventListener('click', async function(e) { const downloadLink = e.target.closest('a[download], a[href*="/library/"], a[href*="/pdfs/"]'); if (!downloadLink) return; const filePath = downloadLink.getAttribute('href') || downloadLink.getAttribute('download'); if (!filePath) return; // Check if user is authenticated if (!authState.authenticated) { // Check download count try { const response = await fetch(`${API_BASE}/download/count`); const data = await response.json(); if (data.count >= data.limit && !data.unlimited) { e.preventDefault(); alert('You have reached the free download limit. Please sign up for membership to download unlimited resources.'); window.location.href = '/membership/'; return; } } catch (error) { console.error('Download check failed:', error); } } // Track download try { await fetch(`${API_BASE}/download/track`, { method: 'POST', headers: { 'Content-Type': 'application/json', ...(authState.token ? { 'Authorization': `Bearer ${authState.token}` } : {}) }, body: JSON.stringify({ file_path: filePath }) }); } catch (error) { console.error('Download tracking failed:', error); // Don't prevent download if tracking fails } }); } /** * Setup contact form */ function setupContactForm() { const contactForm = document.getElementById('contact-form'); if (!contactForm) return; contactForm.addEventListener('submit', async function(e) { e.preventDefault(); if (!authState.authenticated) { alert('Please log in to submit the contact form.'); window.location.href = '/membership/'; return; } const formData = new FormData(contactForm); const data = { name: formData.get('name'), email: formData.get('email'), message: formData.get('message') }; try { const response = await fetch(`${API_BASE}/contact`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${authState.token}` }, body: JSON.stringify(data) }); const result = await response.json(); if (response.ok) { alert('Message sent successfully!'); contactForm.reset(); } else { alert(result.error || 'Failed to send message'); } } catch (error) { console.error('Contact form error:', error); alert('Failed to send message. Please try again.'); } }); } /** * User login */ async function login(email, password) { try { const response = await fetch(`${API_BASE}/auth/login`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email, password }) }); const data = await response.json(); if (response.ok) { localStorage.setItem('membership_token', data.token); authState = { authenticated: true, user: data, token: data.token }; updateAuthUI(true); hidePaywall(); return { success: true }; } else { return { success: false, error: data.error }; } } catch (error) { return { success: false, error: 'Login failed. Please try again.' }; } } /** * User logout */ async function logout() { const token = localStorage.getItem('membership_token'); if (token) { try { await fetch(`${API_BASE}/auth/logout`, { method: 'POST', headers: { 'Authorization': `Bearer ${token}` } }); } catch (error) { console.error('Logout error:', error); } } localStorage.removeItem('membership_token'); authState = { authenticated: false, user: null, token: null }; updateAuthUI(false); window.location.reload(); } /** * User registration */ async function register(email, password, name) { try { const response = await fetch(`${API_BASE}/auth/register`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email, password, name }) }); const data = await response.json(); if (response.ok) { return { success: true, message: data.message }; } else { return { success: false, error: data.error }; } } catch (error) { return { success: false, error: 'Registration failed. Please try again.' }; } } /** * Submit testimonial */ async function submitTestimonial(content, authorName) { if (!authState.authenticated) { return { success: false, error: 'Please log in to submit a testimonial' }; } try { const response = await fetch(`${API_BASE}/testimonials`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${authState.token}` }, body: JSON.stringify({ content, author_name: authorName }) }); const data = await response.json(); if (response.ok) { return { success: true, message: data.message }; } else { return { success: false, error: data.error }; } } catch (error) { return { success: false, error: 'Failed to submit testimonial' }; } } /** * Load testimonials */ async function loadTestimonials() { try { const response = await fetch(`${API_BASE}/testimonials`); const data = await response.json(); if (response.ok) { return { success: true, testimonials: data.testimonials }; } else { return { success: false, error: data.error }; } } catch (error) { return { success: false, error: 'Failed to load testimonials' }; } } /** * Check if the user has reached their download limit (guests only) */ async function checkDownloadLimit() { if (authState.authenticated) { document.querySelectorAll('.paywall-message').forEach(el => el.style.display = 'none'); document.querySelectorAll('.paywall-content').forEach(el => el.style.display = 'block'); document.querySelectorAll('.guest-only').forEach(el => el.style.display = 'none'); document.querySelectorAll('.membership-only').forEach(el => el.style.display = 'block'); return; } try { const response = await fetch(`${API_BASE}/download/count`); const data = await response.json(); if (data.count >= data.limit && !data.unlimited) { document.querySelectorAll('.paywall-message').forEach(el => el.style.display = 'block'); document.querySelectorAll('.paywall-content').forEach(el => el.style.display = 'none'); } else { document.querySelectorAll('.paywall-message').forEach(el => el.style.display = 'none'); document.querySelectorAll('.paywall-content').forEach(el => el.style.display = 'block'); } } catch (error) { console.error('Download limit check failed:', error); } } // Export functions for use in Alpine.js or other scripts window.MembershipAPI = { login, logout, register, submitTestimonial, loadTestimonials, checkAuthStatus, checkDownloadLimit, getAuthState: () => authState };