courrier multi account restore compose

This commit is contained in:
alma 2025-04-29 11:24:39 +02:00
parent 764f194a72
commit 0368bd1069
3 changed files with 57 additions and 23 deletions

View File

@ -82,20 +82,31 @@ export async function POST(request: Request) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }); return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
} }
const { emailId, folderName } = await request.json(); const { emailId, folderName, accountId } = await request.json();
if (!emailId) { if (!emailId) {
return NextResponse.json({ error: 'Missing emailId parameter' }, { status: 400 }); return NextResponse.json({ error: 'Missing emailId parameter' }, { status: 400 });
} }
// Use account ID or default if not provided
const effectiveAccountId = accountId || 'default';
// Normalize folder name by removing account prefix if present
const normalizedFolder = folderName && folderName.includes(':')
? folderName.split(':')[1]
: folderName;
// Log the cache invalidation operation
console.log(`Invalidating cache for user ${session.user.id}, account ${effectiveAccountId}, folder ${normalizedFolder || 'all folders'}`);
// Invalidate Redis cache for the folder // Invalidate Redis cache for the folder
if (folderName) { if (normalizedFolder) {
await invalidateFolderCache(session.user.id, 'default', folderName); await invalidateFolderCache(session.user.id, effectiveAccountId, normalizedFolder);
} else { } else {
// If no folder specified, invalidate all folders (using a wildcard pattern) // If no folder specified, invalidate all folders (using a wildcard pattern)
const folders = ['INBOX', 'Sent', 'Drafts', 'Trash', 'Junk']; const folders = ['INBOX', 'Sent', 'Drafts', 'Trash', 'Junk'];
for (const folder of folders) { for (const folder of folders) {
await invalidateFolderCache(session.user.id, 'default', folder); await invalidateFolderCache(session.user.id, effectiveAccountId, folder);
} }
} }

View File

@ -184,7 +184,13 @@ export const useCourrier = () => {
setIsLoading(false); setIsLoading(false);
// Still refresh in background for fresh data // Still refresh in background for fresh data
refreshEmailsInBackground(session.user.id, currentFolder, currentRequestPage, perPage).catch(err => { refreshEmailsInBackground(
session.user.id,
currentFolder,
currentRequestPage,
perPage,
accountId // Make sure accountId is passed
).catch(err => {
console.error('Background refresh error:', err); console.error('Background refresh error:', err);
}); });
return; return;

View File

@ -68,28 +68,35 @@ export async function getCachedEmailsWithTimeout(
return null; return null;
} }
// Extract account ID from folder name if present and none was explicitly provided
const folderAccountId = folder.includes(':') ? folder.split(':')[0] : accountId;
// Use the most specific account ID available
const effectiveAccountId = folderAccountId || accountId || 'default';
// Normalize folder name by removing account prefix if present // Normalize folder name by removing account prefix if present
// This ensures consistent cache key format regardless of how folder name is passed // This ensures consistent cache key format regardless of how folder name is passed
const normalizedFolder = folder.includes(':') ? folder.split(':')[1] : folder; const normalizedFolder = folder.includes(':') ? folder.split(':')[1] : folder;
// Log the normalization for debugging // Log the normalization for debugging
if (folder !== normalizedFolder) { if (folder !== normalizedFolder) {
console.log(`Normalized folder name from ${folder} to ${normalizedFolder} for cache lookup`); console.log(`Normalized folder name from ${folder} to ${normalizedFolder} for cache lookup with account ID ${effectiveAccountId}`);
} }
return new Promise((resolve) => { return new Promise((resolve) => {
const timeoutId = setTimeout(() => { const timeoutId = setTimeout(() => {
console.log(`Cache access timeout for ${userId}:${normalizedFolder}:${page}:${perPage}${accountId ? ` for account ${accountId}` : ''}`); console.log(`Cache access timeout for ${userId}:${normalizedFolder}:${page}:${perPage} for account ${effectiveAccountId}`);
resolve(null); resolve(null);
}, timeoutMs); }, timeoutMs);
getCachedEmailList(userId, accountId || 'default', normalizedFolder, page, perPage) getCachedEmailList(userId, effectiveAccountId, normalizedFolder, page, perPage)
.then(result => { .then(result => {
clearTimeout(timeoutId); clearTimeout(timeoutId);
if (result) { if (result) {
console.log(`Using cached data for ${userId}:${normalizedFolder}:${page}:${perPage}${accountId ? ` for account ${accountId}` : ''}`); console.log(`Using cached data for ${userId}:${normalizedFolder}:${page}:${perPage} for account ${effectiveAccountId}`);
resolve(result); resolve(result);
} else { } else {
console.log(`Redis cache miss for ${userId}:${effectiveAccountId}:${normalizedFolder}:${page}:${perPage}, fetching emails from IMAP`);
resolve(null); resolve(null);
} }
}) })
@ -112,11 +119,16 @@ export async function refreshEmailsInBackground(
perPage: number = 20, perPage: number = 20,
accountId?: string accountId?: string
): Promise<void> { ): Promise<void> {
// Normalize folder name by removing account prefix if present // Extract account ID from folder name if present and none was explicitly provided
const normalizedFolder = folder.includes(':') ? folder.split(':')[1] : folder;
const folderAccountId = folder.includes(':') ? folder.split(':')[0] : accountId; const folderAccountId = folder.includes(':') ? folder.split(':')[0] : accountId;
const prefetchKey = `refresh:${normalizedFolder}:${page}:${folderAccountId || ''}`; // Use the most specific account ID available
const effectiveAccountId = folderAccountId || accountId || 'default';
// Normalize folder name by removing account prefix if present
const normalizedFolder = folder.includes(':') ? folder.split(':')[1] : folder;
const prefetchKey = `refresh:${normalizedFolder}:${page}:${effectiveAccountId}`;
// Skip if already in progress or in cooldown // Skip if already in progress or in cooldown
if (!shouldPrefetch(userId, prefetchKey)) { if (!shouldPrefetch(userId, prefetchKey)) {
@ -126,9 +138,9 @@ export async function refreshEmailsInBackground(
// Use setTimeout to ensure this runs after current execution context // Use setTimeout to ensure this runs after current execution context
setTimeout(async () => { setTimeout(async () => {
try { try {
console.log(`Background refresh for ${userId}:${normalizedFolder}:${page}:${perPage}${folderAccountId ? ` for account ${folderAccountId}` : ''}`); console.log(`Background refresh for ${userId}:${normalizedFolder}:${page}:${perPage} for account ${effectiveAccountId}`);
const freshData = await getEmails(userId, normalizedFolder, page, perPage, folderAccountId); const freshData = await getEmails(userId, normalizedFolder, page, perPage, effectiveAccountId);
console.log(`Background refresh completed for ${userId}:${normalizedFolder}${folderAccountId ? ` for account ${folderAccountId}` : ''}`); console.log(`Background refresh completed for ${userId}:${normalizedFolder} for account ${effectiveAccountId}`);
} catch (error) { } catch (error) {
console.error('Background refresh error:', error); console.error('Background refresh error:', error);
} finally { } finally {
@ -232,11 +244,16 @@ export async function prefetchFolderEmails(
startPage: number = 1, startPage: number = 1,
accountId?: string accountId?: string
): Promise<void> { ): Promise<void> {
// Normalize folder name by removing account prefix if present // Extract account ID from folder name if present and none was explicitly provided
const normalizedFolder = folder.includes(':') ? folder.split(':')[1] : folder;
const folderAccountId = folder.includes(':') ? folder.split(':')[0] : accountId; const folderAccountId = folder.includes(':') ? folder.split(':')[0] : accountId;
const prefetchKey = `folder:${normalizedFolder}:${startPage}:${folderAccountId || ''}`; // Use the most specific account ID available
const effectiveAccountId = folderAccountId || accountId || 'default';
// Normalize folder name by removing account prefix if present
const normalizedFolder = folder.includes(':') ? folder.split(':')[1] : folder;
const prefetchKey = `folder:${normalizedFolder}:${startPage}:${effectiveAccountId}`;
// Skip if already in progress or in cooldown // Skip if already in progress or in cooldown
if (!shouldPrefetch(userId, prefetchKey)) { if (!shouldPrefetch(userId, prefetchKey)) {
@ -244,7 +261,7 @@ export async function prefetchFolderEmails(
} }
try { try {
console.log(`Prefetching ${pages} pages of emails for folder ${normalizedFolder} starting from page ${startPage}${folderAccountId ? ` for account ${folderAccountId}` : ''}`); console.log(`Prefetching ${pages} pages of emails for folder ${normalizedFolder} starting from page ${startPage} for account ${effectiveAccountId}`);
// Calculate the range of pages to prefetch // Calculate the range of pages to prefetch
const pagesToFetch = Array.from( const pagesToFetch = Array.from(
@ -257,19 +274,19 @@ export async function prefetchFolderEmails(
// Fetch multiple pages in parallel // Fetch multiple pages in parallel
await Promise.allSettled( await Promise.allSettled(
pagesToFetch.map(page => pagesToFetch.map(page =>
getEmails(userId, normalizedFolder, page, 20, folderAccountId) getEmails(userId, normalizedFolder, page, 20, effectiveAccountId)
.then(result => { .then(result => {
console.log(`Successfully prefetched and cached page ${page} of ${normalizedFolder} with ${result.emails.length} emails`); console.log(`Successfully prefetched and cached page ${page} of ${normalizedFolder} with ${result.emails.length} emails for account ${effectiveAccountId}`);
return result; return result;
}) })
.catch(err => { .catch(err => {
console.error(`Error prefetching page ${page} of ${normalizedFolder}:`, err); console.error(`Error prefetching page ${page} of ${normalizedFolder} for account ${effectiveAccountId}:`, err);
return null; return null;
}) })
) )
); );
console.log(`Completed prefetching ${pages} pages for ${normalizedFolder}`); console.log(`Completed prefetching ${pages} pages for ${normalizedFolder} in account ${effectiveAccountId}`);
} catch (error) { } catch (error) {
console.error(`Error during folder prefetch:`, error); console.error(`Error during folder prefetch:`, error);
} finally { } finally {