diff --git a/static/dashboard.js b/static/dashboard.js
index 2c75e7e..1c931fb 100644
--- a/static/dashboard.js
+++ b/static/dashboard.js
@@ -13,8 +13,12 @@ function getCookie(name) {
let isLoggingOut = false;
async function handleLogout(event) {
+ console.log('[LOGOUT] Logout initiated');
// Prevent multiple simultaneous logout attempts
- if (isLoggingOut) return;
+ if (isLoggingOut) {
+ console.log('[LOGOUT] Logout already in progress');
+ return;
+ }
isLoggingOut = true;
// Prevent default button behavior
@@ -23,44 +27,145 @@ async function handleLogout(event) {
event.stopPropagation();
}
+ // Get auth token before we clear it
+ const authToken = localStorage.getItem('authToken');
+
+ // 1. First try to invalidate the server session (but don't block on it)
+ if (authToken) {
+ try {
+ // We'll use a timeout to prevent hanging on the server request
+ const controller = new AbortController();
+ const timeoutId = setTimeout(() => controller.abort(), 2000);
+
+ try {
+ await fetch('/api/logout', {
+ method: 'POST',
+ credentials: 'include',
+ signal: controller.signal,
+ headers: {
+ 'Content-Type': 'application/json',
+ 'Authorization': `Bearer ${authToken}`
+ },
+ });
+ clearTimeout(timeoutId);
+ } catch (error) {
+ clearTimeout(timeoutId);
+ // Silently handle any errors during server logout
+ }
+ } catch (error) {
+ // Silently handle any unexpected errors
+ }
+ }
+
+ // 2. Clear all client-side state
+ function clearClientState() {
+ console.log('[LOGOUT] Clearing client state');
+
+ // Clear all authentication-related data from localStorage
+ const keysToRemove = [
+ 'uid', 'uid_time', 'confirmed_uid', 'last_page',
+ 'isAuthenticated', 'authToken', 'user', 'token', 'sessionid'
+ ];
+
+ keysToRemove.forEach(key => {
+ localStorage.removeItem(key);
+ sessionStorage.removeItem(key);
+ });
+
+ // Get current cookies for debugging
+ const cookies = document.cookie.split(';');
+ console.log('[LOGOUT] Current cookies before clearing:', cookies);
+
+ // Function to clear a cookie by name
+ const clearCookie = (name) => {
+ console.log(`[LOGOUT] Attempting to clear cookie: ${name}`);
+ const baseOptions = 'Path=/; Expires=Thu, 01 Jan 1970 00:00:01 GMT; SameSite=Lax';
+ // Try with current domain
+ document.cookie = `${name}=; ${baseOptions}`;
+ // Try with domain
+ document.cookie = `${name}=; ${baseOptions}; domain=${window.location.hostname}`;
+ // Try with leading dot for subdomains
+ document.cookie = `${name}=; ${baseOptions}; domain=.${window.location.hostname}`;
+ };
+
+ // Clear all authentication-related cookies
+ const authCookies = [
+ 'uid', 'authToken', 'isAuthenticated', 'sessionid', 'session_id',
+ 'token', 'remember_token', 'auth', 'authentication'
+ ];
+
+ // Clear specific auth cookies
+ authCookies.forEach(clearCookie);
+
+ // Also clear any existing cookies that match our patterns
+ cookies.forEach(cookie => {
+ const [name] = cookie.trim().split('=');
+ if (name && authCookies.some(authName => name.trim() === authName)) {
+ clearCookie(name.trim());
+ }
+ });
+
+ // Clear all cookies by setting them to expire in the past
+ document.cookie.split(';').forEach(cookie => {
+ const [name] = cookie.trim().split('=');
+ if (name) {
+ clearCookie(name.trim());
+ }
+ });
+
+ console.log('[LOGOUT] Cookies after clearing:', document.cookie);
+
+ // Update UI state
+ document.body.classList.remove('authenticated');
+ document.body.classList.add('guest');
+
+ // Force a hard reload to ensure all state is reset
+ setTimeout(() => {
+ // Clear all storage again before redirecting
+ keysToRemove.forEach(key => {
+ localStorage.removeItem(key);
+ sessionStorage.removeItem(key);
+ });
+
+ // Redirect to home with a cache-busting parameter
+ window.location.href = '/?logout=' + Date.now();
+ }, 100);
+ }
+
try {
- console.log('[LOGOUT] Starting logout process');
+ // Clear client state immediately to prevent any race conditions
+ clearClientState();
- // Clear user data from localStorage
- localStorage.removeItem('uid');
- localStorage.removeItem('uid_time');
- localStorage.removeItem('confirmed_uid');
- localStorage.removeItem('last_page');
-
- // Clear session cookie with SameSite attribute to match how it was set
- const isLocalhost = window.location.hostname === 'localhost';
- const secureFlag = !isLocalhost ? '; Secure' : '';
- document.cookie = `sessionid=; Path=/; Expires=Thu, 01 Jan 1970 00:00:01 GMT; SameSite=Lax${secureFlag};`;
-
- // Update UI state immediately
- const userDashboard = document.getElementById('user-dashboard');
- const guestDashboard = document.getElementById('guest-dashboard');
- const logoutButton = document.getElementById('logout-button');
- const deleteAccountButton = document.getElementById('delete-account-button');
-
- if (userDashboard) userDashboard.style.display = 'none';
- if (guestDashboard) guestDashboard.style.display = 'block';
- if (logoutButton) logoutButton.style.display = 'none';
- if (deleteAccountButton) deleteAccountButton.style.display = 'none';
-
- // Show success message (only once)
- if (window.showToast) {
- showToast('Logged out successfully');
- } else {
- console.log('Logged out successfully');
+ // 2. Try to invalidate the server session (but don't block on it)
+ console.log('[LOGOUT] Auth token exists:', !!authToken);
+ if (authToken) {
+ try {
+ console.log('[LOGOUT] Attempting to invalidate server session');
+
+ const response = await fetch('/api/logout', {
+ method: 'POST',
+ credentials: 'include',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'Authorization': `Bearer ${authToken}`
+ },
+ });
+
+ if (!response.ok && response.status !== 401) {
+ console.warn(`[LOGOUT] Server returned ${response.status} during logout`);
+ // Don't throw - we've already cleared client state
+ } else {
+ console.log('[LOGOUT] Server session invalidated successfully');
+ }
+ } catch (error) {
+ console.warn('[LOGOUT] Error during server session invalidation (non-critical):', error);
+ // Continue with logout process
+ }
}
- // Navigate to register page
- if (window.showOnly) {
- window.showOnly('register-page');
- } else {
- // Fallback to URL change if showOnly isn't available
- window.location.href = '/#register-page';
+ // 3. Update navigation if the function exists
+ if (typeof injectNavigation === 'function') {
+ injectNavigation(false);
}
console.log('[LOGOUT] Logout completed');
@@ -72,6 +177,11 @@ async function handleLogout(event) {
}
} finally {
isLoggingOut = false;
+
+ // 4. Redirect to home page after a short delay to ensure state is cleared
+ setTimeout(() => {
+ window.location.href = '/';
+ }, 100);
}
}
@@ -194,6 +304,15 @@ async function initDashboard() {
const hasAuthToken = localStorage.getItem('authToken') !== null;
const isAuthenticated = hasAuthCookie || hasUidCookie || hasLocalStorageAuth || hasAuthToken;
+ // Ensure body class reflects authentication state
+ if (isAuthenticated) {
+ document.body.classList.add('authenticated');
+ document.body.classList.remove('guest-mode');
+ } else {
+ document.body.classList.remove('authenticated');
+ document.body.classList.add('guest-mode');
+ }
+
// Debug authentication state
console.log('[AUTH] Authentication state:', {
hasAuthCookie,
@@ -485,52 +604,16 @@ async function initDashboard() {
}
}
-// Function to delete a file - only define if not already defined
-if (typeof window.deleteFile === 'undefined') {
- window.deleteFile = async function(uid, fileName, listItem) {
- if (!confirm(`Are you sure you want to delete "${fileName}"? This cannot be undone.`)) {
- return;
- }
+// Delete file function is defined below with more complete implementation
- try {
- console.log(`[FILES] Deleting file: ${fileName} for user: ${uid}`);
-
- const response = await fetch(`/delete-file/${uid}/${encodeURIComponent(fileName)}`, {
- method: 'DELETE',
- credentials: 'include',
- headers: {
- 'Accept': 'application/json'
- }
- });
-
- console.log('[FILES] Delete response:', response.status, response.statusText);
-
- if (!response.ok) {
- const errorText = await response.text();
- console.error('[FILES] Delete error:', errorText);
- showToast(`Error deleting file: ${response.statusText}`, 'error');
- return;
- }
-
- // Remove the file from the UI
- if (listItem && listItem.parentNode) {
- listItem.parentNode.removeChild(listItem);
- }
-
- showToast('File deleted successfully', 'success');
-
- // If no files left, show "no files" message
- const fileList = document.getElementById('file-list');
- if (fileList && fileList.children.length === 0) {
- fileList.innerHTML = '
No files uploaded yet.';
- }
-
- } catch (error) {
- console.error('[FILES] Error deleting file:', error);
- showToast('Error deleting file. Please try again.', 'error');
- }
- }; // Close the function expression
-} // Close the if statement
+// Helper function to format file size
+function formatFileSize(bytes) {
+ if (bytes === 0) return '0 Bytes';
+ const k = 1024;
+ const sizes = ['Bytes', 'KB', 'MB', 'GB'];
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
+}
// Function to fetch and display user's uploaded files
async function fetchAndDisplayFiles(uid) {
@@ -560,8 +643,8 @@ async function fetchAndDisplayFiles(uid) {
try {
// The backend should handle authentication via session cookies
// We include the auth token in headers if available, but don't rely on it for auth
- console.log('[FILES] Making request to /me with credentials...');
- const response = await fetch('/me', {
+ console.log(`[FILES] Making request to /me/${uid} with credentials...`);
+ const response = await fetch(`/me/${uid}`, {
method: 'GET',
credentials: 'include', // Important: include cookies for session auth
headers: headers
@@ -617,14 +700,29 @@ async function fetchAndDisplayFiles(uid) {
// Clear the loading message
fileList.innerHTML = '';
+ // Track displayed files to prevent duplicates using stored filenames as unique identifiers
+ const displayedFiles = new Set();
+
// Add each file to the list
- files.forEach(fileName => {
- const fileExt = fileName.split('.').pop().toLowerCase();
- const fileUrl = `/data/${uid}/${encodeURIComponent(fileName)}`;
- const fileSize = 'N/A'; // We don't have size info in the current API response
+ files.forEach(file => {
+ // Get the stored filename (with UUID) - this is our unique identifier
+ const storedFileName = file.stored_name || file.name || file;
+
+ // Skip if we've already displayed this file
+ if (displayedFiles.has(storedFileName)) {
+ console.log(`[FILES] Skipping duplicate file with stored name: ${storedFileName}`);
+ return;
+ }
+
+ displayedFiles.add(storedFileName);
+
+ const fileExt = storedFileName.split('.').pop().toLowerCase();
+ const fileUrl = `/data/${uid}/${encodeURIComponent(storedFileName)}`;
+ const fileSize = file.size ? formatFileSize(file.size) : 'N/A';
const listItem = document.createElement('li');
listItem.className = 'file-item';
+ listItem.setAttribute('data-uid', uid);
// Create file icon based on file extension
let fileIcon = '📄'; // Default icon
@@ -636,11 +734,14 @@ async function fetchAndDisplayFiles(uid) {
fileIcon = '📄';
}
+ // Use original_name if available, otherwise use the stored filename for display
+ const displayName = file.original_name || storedFileName;
+
listItem.innerHTML = `
@@ -649,18 +750,15 @@ async function fetchAndDisplayFiles(uid) {
⬇️
Download
-