courrier multi account

This commit is contained in:
alma 2025-04-27 16:05:04 +02:00
parent b66081643f
commit 1aa9608722
4 changed files with 113 additions and 11 deletions

View File

@ -3,7 +3,11 @@ import { getServerSession } from 'next-auth';
import { authOptions } from '@/app/api/auth/[...nextauth]/route';
import { getUserEmailCredentials } from '@/lib/services/email-service';
import { prefetchUserEmailData } from '@/lib/services/prefetch-service';
import { getCachedEmailCredentials, getRedisStatus, warmupRedisCache } from '@/lib/redis';
import { getCachedEmailCredentials, getRedisStatus, warmupRedisCache, getCachedImapSession, cacheImapSession } from '@/lib/redis';
// Keep track of last prefetch time for each user
const lastPrefetchMap = new Map<string, number>();
const PREFETCH_COOLDOWN_MS = 30000; // 30 seconds cooldown between prefetches
/**
* This endpoint is called when the app initializes to check if the user has email credentials
@ -31,6 +35,14 @@ export async function GET() {
const userId = session.user.id;
// Check when we last prefetched for this user
const lastPrefetchTime = lastPrefetchMap.get(userId) || 0;
const now = Date.now();
const shouldPrefetch = now - lastPrefetchTime > PREFETCH_COOLDOWN_MS;
// Check if we have a cached session
const cachedSession = await getCachedImapSession(userId);
// First, check Redis cache for credentials
let credentials = await getCachedEmailCredentials(userId);
let credentialsSource = 'cache';
@ -51,11 +63,33 @@ export async function GET() {
});
}
// Start prefetching email data in the background
// We don't await this to avoid blocking the response
prefetchUserEmailData(userId).catch(err => {
console.error('Background prefetch error:', err);
});
let prefetchStarted = false;
// Only prefetch if the cooldown period has elapsed
if (shouldPrefetch) {
// Update the last prefetch time
lastPrefetchMap.set(userId, now);
// Start prefetching email data in the background
// We don't await this to avoid blocking the response
prefetchUserEmailData(userId).catch(err => {
console.error('Background prefetch error:', err);
});
prefetchStarted = true;
} else {
console.log(`Skipping prefetch for ${userId}, last prefetch was ${Math.round((now - lastPrefetchTime)/1000)}s ago`);
}
// Store last visit time in session data
if (cachedSession) {
await cacheImapSession(userId, {
...cachedSession,
lastVisit: now
});
} else {
await cacheImapSession(userId, { lastVisit: now });
}
// Return session info without sensitive data
return NextResponse.json({
@ -63,8 +97,10 @@ export async function GET() {
hasEmailCredentials: true,
email: credentials.email,
redisStatus,
prefetchStarted: true,
credentialsSource
prefetchStarted,
credentialsSource,
lastVisit: cachedSession?.lastVisit,
mailboxes: cachedSession?.mailboxes || []
});
} catch (error) {
console.error("Error checking session:", error);

View File

@ -179,10 +179,10 @@ export default function CourrierPage() {
setAccounts(prev => {
const updated = [...prev];
updated[1] = {
...updated[1],
...updated[1],
name: data.email,
email: data.email,
folders: mailboxes
folders: data.mailboxes || mailboxes
};
return updated;
});
@ -228,7 +228,7 @@ export default function CourrierPage() {
return () => {
isMounted = false;
};
}, [session?.user?.id, loadEmails, currentFolder, mailboxes]);
}, [session?.user?.id, loadEmails]);
// Helper to get folder icons
const getFolderIcon = (folder: string) => {

View File

@ -102,6 +102,7 @@ interface ImapSessionData {
connectionId?: string;
lastActive: number;
mailboxes?: string[];
lastVisit?: number;
}
/**

View File

@ -10,6 +10,46 @@ import {
warmupRedisCache
} from '@/lib/redis';
// Keep track of ongoing prefetch operations to prevent duplicates
const prefetchInProgress = new Map<string, boolean>();
const lastPrefetchTime = new Map<string, number>();
const PREFETCH_COOLDOWN_MS = 30000; // 30 seconds between prefetch operations
/**
* Check if we should prefetch for a user based on cooldown
*/
function shouldPrefetch(userId: string, key: string = 'general'): boolean {
const prefetchKey = `${userId}:${key}`;
// Check if prefetch is already in progress
if (prefetchInProgress.get(prefetchKey)) {
console.log(`Prefetch already in progress for ${prefetchKey}`);
return false;
}
// Check cooldown
const lastTime = lastPrefetchTime.get(prefetchKey) || 0;
const now = Date.now();
if (now - lastTime < PREFETCH_COOLDOWN_MS) {
console.log(`Prefetch cooldown active for ${prefetchKey}, last was ${Math.round((now - lastTime)/1000)}s ago`);
return false;
}
// Mark as in progress and update last time
prefetchInProgress.set(prefetchKey, true);
lastPrefetchTime.set(prefetchKey, now);
return true;
}
/**
* Mark prefetch as completed
*/
function markPrefetchCompleted(userId: string, key: string = 'general'): void {
const prefetchKey = `${userId}:${key}`;
prefetchInProgress.set(prefetchKey, false);
}
/**
* Get cached emails with timeout to ensure fast UI response
* If cache access takes longer than timeout, return null to use regular IMAP fetch
@ -73,6 +113,13 @@ export async function refreshEmailsInBackground(
page: number = 1,
perPage: number = 20
): Promise<void> {
const prefetchKey = `refresh:${folder}:${page}`;
// Skip if already in progress or in cooldown
if (!shouldPrefetch(userId, prefetchKey)) {
return;
}
// Use setTimeout to ensure this runs after current execution context
setTimeout(async () => {
try {
@ -81,6 +128,8 @@ export async function refreshEmailsInBackground(
console.log(`Background refresh completed for ${userId}:${folder}`);
} catch (error) {
console.error('Background refresh error:', error);
} finally {
markPrefetchCompleted(userId, prefetchKey);
}
}, 100);
}
@ -90,6 +139,11 @@ export async function refreshEmailsInBackground(
* This function should be called when a user logs in
*/
export async function prefetchUserEmailData(userId: string): Promise<void> {
// Skip if already in progress or in cooldown
if (!shouldPrefetch(userId)) {
return;
}
console.log(`Starting email prefetch for user ${userId}`);
const startTime = Date.now();
@ -159,6 +213,8 @@ export async function prefetchUserEmailData(userId: string): Promise<void> {
console.log(`Email prefetch completed for user ${userId} in ${duration.toFixed(2)}s`);
} catch (error) {
console.error('Error during email prefetch:', error);
} finally {
markPrefetchCompleted(userId);
}
}
@ -172,6 +228,13 @@ export async function prefetchFolderEmails(
pages: number = 3,
startPage: number = 1
): Promise<void> {
const prefetchKey = `folder:${folder}:${startPage}`;
// Skip if already in progress or in cooldown
if (!shouldPrefetch(userId, prefetchKey)) {
return;
}
try {
console.log(`Prefetching ${pages} pages of emails for folder ${folder} starting from page ${startPage}`);
@ -201,5 +264,7 @@ export async function prefetchFolderEmails(
console.log(`Completed prefetching ${pages} pages of ${folder}`);
} catch (error) {
console.error(`Error prefetching folder ${folder}:`, error);
} finally {
markPrefetchCompleted(userId, prefetchKey);
}
}