Update authentication system, database models, and UI components
This commit is contained in:
275
static/auth.js
275
static/auth.js
@ -1,252 +1,31 @@
|
||||
import { showToast } from './toast.js';
|
||||
/**
|
||||
* Simplified Authentication Module
|
||||
*
|
||||
* This file now uses the centralized AuthManager for all authentication logic.
|
||||
* Legacy code has been replaced with the new consolidated approach.
|
||||
*/
|
||||
|
||||
import authManager from './auth-manager.js';
|
||||
import { loadProfileStream } from './personal-player.js';
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// Track previous authentication state
|
||||
let wasAuthenticated = null;
|
||||
// Debug flag - set to false to disable auth state change logs
|
||||
const DEBUG_AUTH_STATE = false;
|
||||
|
||||
// Track auth check calls and cache state
|
||||
let lastAuthCheckTime = 0;
|
||||
let authCheckCounter = 0;
|
||||
const AUTH_CHECK_DEBOUNCE = 1000; // 1 second
|
||||
let authStateCache = {
|
||||
timestamp: 0,
|
||||
value: null,
|
||||
ttl: 5000 // Cache TTL in milliseconds
|
||||
};
|
||||
|
||||
// Handle magic link login redirect
|
||||
function handleMagicLoginRedirect() {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
if (params.get('login') === 'success' && params.get('confirmed_uid')) {
|
||||
const username = params.get('confirmed_uid');
|
||||
console.log('Magic link login detected for user:', username);
|
||||
|
||||
// Update authentication state
|
||||
localStorage.setItem('uid', username);
|
||||
localStorage.setItem('confirmed_uid', username);
|
||||
localStorage.setItem('uid_time', Date.now().toString());
|
||||
document.cookie = `uid=${encodeURIComponent(username)}; path=/; SameSite=Lax`;
|
||||
|
||||
// Update UI state
|
||||
document.body.classList.add('authenticated');
|
||||
document.body.classList.remove('guest');
|
||||
|
||||
// Update local storage and cookies
|
||||
localStorage.setItem('isAuthenticated', 'true');
|
||||
document.cookie = `isAuthenticated=true; path=/; SameSite=Lax`;
|
||||
|
||||
// Update URL and history without reloading
|
||||
window.history.replaceState({}, document.title, window.location.pathname);
|
||||
|
||||
// Update navigation
|
||||
if (typeof injectNavigation === 'function') {
|
||||
console.log('Updating navigation after magic link login');
|
||||
injectNavigation(true);
|
||||
} else {
|
||||
console.warn('injectNavigation function not available after magic link login');
|
||||
}
|
||||
|
||||
// Navigate to user's profile page
|
||||
if (window.showOnly) {
|
||||
console.log('Navigating to me-page');
|
||||
window.showOnly('me-page');
|
||||
} else if (window.location.hash !== '#me') {
|
||||
window.location.hash = '#me';
|
||||
}
|
||||
|
||||
// Auth state will be updated by the polling mechanism
|
||||
}
|
||||
}
|
||||
|
||||
// Update the visibility of the account deletion section based on authentication state
|
||||
function updateAccountDeletionVisibility(isAuthenticated) {
|
||||
const authOnlyWrapper = document.querySelector('#privacy-page .auth-only');
|
||||
const accountDeletionSection = document.getElementById('account-deletion');
|
||||
|
||||
const showElement = (element) => {
|
||||
if (!element) return;
|
||||
element.classList.remove('hidden', 'auth-only-hidden');
|
||||
element.style.display = 'block';
|
||||
};
|
||||
|
||||
const hideElement = (element) => {
|
||||
if (!element) return;
|
||||
element.style.display = 'none';
|
||||
};
|
||||
|
||||
if (isAuthenticated) {
|
||||
const isPrivacyPage = window.location.hash === '#privacy-page';
|
||||
if (isPrivacyPage) {
|
||||
if (authOnlyWrapper) showElement(authOnlyWrapper);
|
||||
if (accountDeletionSection) showElement(accountDeletionSection);
|
||||
} else {
|
||||
if (accountDeletionSection) hideElement(accountDeletionSection);
|
||||
if (authOnlyWrapper) hideElement(authOnlyWrapper);
|
||||
}
|
||||
} else {
|
||||
if (accountDeletionSection) hideElement(accountDeletionSection);
|
||||
if (authOnlyWrapper) {
|
||||
const hasOtherContent = Array.from(authOnlyWrapper.children).some(
|
||||
child => child.id !== 'account-deletion' && child.offsetParent !== null
|
||||
);
|
||||
if (!hasOtherContent) {
|
||||
hideElement(authOnlyWrapper);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check authentication state and update UI with caching and debouncing
|
||||
function checkAuthState(force = false) {
|
||||
const now = Date.now();
|
||||
if (!force && authStateCache.value !== null && now - authStateCache.timestamp < authStateCache.ttl) {
|
||||
return authStateCache.value;
|
||||
}
|
||||
|
||||
if (now - lastAuthCheckTime < AUTH_CHECK_DEBOUNCE && !force) {
|
||||
return wasAuthenticated;
|
||||
}
|
||||
lastAuthCheckTime = now;
|
||||
authCheckCounter++;
|
||||
|
||||
const isAuthenticated =
|
||||
(document.cookie.includes('isAuthenticated=true') || localStorage.getItem('isAuthenticated') === 'true') &&
|
||||
(document.cookie.includes('uid=') || localStorage.getItem('uid')) &&
|
||||
!!localStorage.getItem('authToken');
|
||||
|
||||
authStateCache = {
|
||||
timestamp: now,
|
||||
value: isAuthenticated,
|
||||
ttl: isAuthenticated ? 30000 : 5000
|
||||
};
|
||||
|
||||
if (isAuthenticated !== wasAuthenticated) {
|
||||
if (DEBUG_AUTH_STATE) {
|
||||
console.log('Auth state changed, updating UI...');
|
||||
}
|
||||
|
||||
if (!isAuthenticated && wasAuthenticated) {
|
||||
console.log('User was authenticated, but is no longer. Triggering logout.');
|
||||
basicLogout();
|
||||
return; // Stop further processing after logout
|
||||
}
|
||||
|
||||
if (isAuthenticated) {
|
||||
document.body.classList.add('authenticated');
|
||||
document.body.classList.remove('guest');
|
||||
const uid = localStorage.getItem('uid');
|
||||
if (uid && (window.location.hash === '#me-page' || window.location.hash === '#me' || window.location.pathname.startsWith('/~'))) {
|
||||
loadProfileStream(uid);
|
||||
}
|
||||
} else {
|
||||
document.body.classList.remove('authenticated');
|
||||
document.body.classList.add('guest');
|
||||
}
|
||||
|
||||
updateAccountDeletionVisibility(isAuthenticated);
|
||||
wasAuthenticated = isAuthenticated;
|
||||
void document.body.offsetHeight; // Force reflow
|
||||
}
|
||||
|
||||
return isAuthenticated;
|
||||
}
|
||||
|
||||
// Periodically check authentication state with optimized polling
|
||||
function setupAuthStatePolling() {
|
||||
checkAuthState(true);
|
||||
|
||||
const checkAndUpdate = () => {
|
||||
checkAuthState(!document.hidden);
|
||||
};
|
||||
|
||||
const AUTH_CHECK_INTERVAL = 30000;
|
||||
setInterval(checkAndUpdate, AUTH_CHECK_INTERVAL);
|
||||
|
||||
const handleStorageEvent = (e) => {
|
||||
if (['isAuthenticated', 'authToken', 'uid'].includes(e.key)) {
|
||||
checkAuthState(true);
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('storage', handleStorageEvent);
|
||||
|
||||
const handleVisibilityChange = () => {
|
||||
if (!document.hidden) {
|
||||
checkAuthState(true);
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener('visibilitychange', handleVisibilityChange);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('storage', handleStorageEvent);
|
||||
document.removeEventListener('visibilitychange', handleVisibilityChange);
|
||||
};
|
||||
}
|
||||
|
||||
// --- ACCOUNT DELETION ---
|
||||
const deleteAccount = async (e) => {
|
||||
if (e) e.preventDefault();
|
||||
if (deleteAccount.inProgress) return;
|
||||
if (!confirm('Are you sure you want to delete your account?\nThis action is permanent.')) return;
|
||||
|
||||
deleteAccount.inProgress = true;
|
||||
const deleteBtn = e?.target.closest('button');
|
||||
const originalText = deleteBtn?.textContent;
|
||||
if (deleteBtn) {
|
||||
deleteBtn.disabled = true;
|
||||
deleteBtn.textContent = 'Deleting...';
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/delete-account', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
credentials: 'include',
|
||||
body: JSON.stringify({ uid: localStorage.getItem('uid') })
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({ detail: 'Failed to delete account.' }));
|
||||
throw new Error(errorData.detail);
|
||||
}
|
||||
|
||||
showToast('Account deleted successfully.', 'success');
|
||||
// Perform a full client-side logout and redirect
|
||||
basicLogout();
|
||||
} catch (error) {
|
||||
showToast(error.message, 'error');
|
||||
} finally {
|
||||
deleteAccount.inProgress = false;
|
||||
if (deleteBtn) {
|
||||
deleteBtn.disabled = false;
|
||||
deleteBtn.textContent = originalText;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// --- LOGOUT ---
|
||||
function basicLogout() {
|
||||
['isAuthenticated', 'uid', 'confirmed_uid', 'uid_time', 'authToken'].forEach(k => localStorage.removeItem(k));
|
||||
document.cookie.split(';').forEach(c => document.cookie = c.replace(/^ +/, '').replace(/=.*/, `=;expires=${new Date().toUTCString()};path=/`));
|
||||
window.location.href = '/';
|
||||
}
|
||||
|
||||
// --- DELEGATED EVENT LISTENERS ---
|
||||
document.addEventListener('click', (e) => {
|
||||
|
||||
// Delete Account Buttons
|
||||
if (e.target.closest('#delete-account') || e.target.closest('#delete-account-from-privacy')) {
|
||||
deleteAccount(e);
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
// --- INITIALIZATION ---
|
||||
handleMagicLoginRedirect();
|
||||
setupAuthStatePolling();
|
||||
// Initialize authentication manager when DOM is ready
|
||||
document.addEventListener('DOMContentLoaded', async () => {
|
||||
// Debug messages disabled
|
||||
|
||||
// Initialize the centralized auth manager
|
||||
await authManager.initialize();
|
||||
|
||||
// Make loadProfileStream available globally for auth manager
|
||||
window.loadProfileStream = loadProfileStream;
|
||||
|
||||
// Debug messages disabled
|
||||
});
|
||||
|
||||
// Export auth manager for other modules to use
|
||||
export { authManager };
|
||||
|
||||
// Legacy compatibility - expose some functions globally
|
||||
window.getCurrentUser = () => authManager.getCurrentUser();
|
||||
window.isAuthenticated = () => authManager.isAuthenticated();
|
||||
window.logout = () => authManager.logout();
|
||||
window.cleanupAuthState = (email) => authManager.cleanupAuthState(email);
|
||||
|
Reference in New Issue
Block a user