courrier multi account
This commit is contained in:
parent
b66081643f
commit
1aa9608722
@ -3,7 +3,11 @@ import { getServerSession } from 'next-auth';
|
|||||||
import { authOptions } from '@/app/api/auth/[...nextauth]/route';
|
import { authOptions } from '@/app/api/auth/[...nextauth]/route';
|
||||||
import { getUserEmailCredentials } from '@/lib/services/email-service';
|
import { getUserEmailCredentials } from '@/lib/services/email-service';
|
||||||
import { prefetchUserEmailData } from '@/lib/services/prefetch-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
|
* 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;
|
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
|
// First, check Redis cache for credentials
|
||||||
let credentials = await getCachedEmailCredentials(userId);
|
let credentials = await getCachedEmailCredentials(userId);
|
||||||
let credentialsSource = 'cache';
|
let credentialsSource = 'cache';
|
||||||
@ -51,11 +63,33 @@ export async function GET() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start prefetching email data in the background
|
let prefetchStarted = false;
|
||||||
// We don't await this to avoid blocking the response
|
|
||||||
prefetchUserEmailData(userId).catch(err => {
|
// Only prefetch if the cooldown period has elapsed
|
||||||
console.error('Background prefetch error:', err);
|
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 session info without sensitive data
|
||||||
return NextResponse.json({
|
return NextResponse.json({
|
||||||
@ -63,8 +97,10 @@ export async function GET() {
|
|||||||
hasEmailCredentials: true,
|
hasEmailCredentials: true,
|
||||||
email: credentials.email,
|
email: credentials.email,
|
||||||
redisStatus,
|
redisStatus,
|
||||||
prefetchStarted: true,
|
prefetchStarted,
|
||||||
credentialsSource
|
credentialsSource,
|
||||||
|
lastVisit: cachedSession?.lastVisit,
|
||||||
|
mailboxes: cachedSession?.mailboxes || []
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error checking session:", error);
|
console.error("Error checking session:", error);
|
||||||
|
|||||||
@ -182,7 +182,7 @@ export default function CourrierPage() {
|
|||||||
...updated[1],
|
...updated[1],
|
||||||
name: data.email,
|
name: data.email,
|
||||||
email: data.email,
|
email: data.email,
|
||||||
folders: mailboxes
|
folders: data.mailboxes || mailboxes
|
||||||
};
|
};
|
||||||
return updated;
|
return updated;
|
||||||
});
|
});
|
||||||
@ -228,7 +228,7 @@ export default function CourrierPage() {
|
|||||||
return () => {
|
return () => {
|
||||||
isMounted = false;
|
isMounted = false;
|
||||||
};
|
};
|
||||||
}, [session?.user?.id, loadEmails, currentFolder, mailboxes]);
|
}, [session?.user?.id, loadEmails]);
|
||||||
|
|
||||||
// Helper to get folder icons
|
// Helper to get folder icons
|
||||||
const getFolderIcon = (folder: string) => {
|
const getFolderIcon = (folder: string) => {
|
||||||
|
|||||||
@ -102,6 +102,7 @@ interface ImapSessionData {
|
|||||||
connectionId?: string;
|
connectionId?: string;
|
||||||
lastActive: number;
|
lastActive: number;
|
||||||
mailboxes?: string[];
|
mailboxes?: string[];
|
||||||
|
lastVisit?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -10,6 +10,46 @@ import {
|
|||||||
warmupRedisCache
|
warmupRedisCache
|
||||||
} from '@/lib/redis';
|
} 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
|
* Get cached emails with timeout to ensure fast UI response
|
||||||
* If cache access takes longer than timeout, return null to use regular IMAP fetch
|
* 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,
|
page: number = 1,
|
||||||
perPage: number = 20
|
perPage: number = 20
|
||||||
): Promise<void> {
|
): 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
|
// Use setTimeout to ensure this runs after current execution context
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
try {
|
try {
|
||||||
@ -81,6 +128,8 @@ export async function refreshEmailsInBackground(
|
|||||||
console.log(`Background refresh completed for ${userId}:${folder}`);
|
console.log(`Background refresh completed for ${userId}:${folder}`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Background refresh error:', error);
|
console.error('Background refresh error:', error);
|
||||||
|
} finally {
|
||||||
|
markPrefetchCompleted(userId, prefetchKey);
|
||||||
}
|
}
|
||||||
}, 100);
|
}, 100);
|
||||||
}
|
}
|
||||||
@ -90,6 +139,11 @@ export async function refreshEmailsInBackground(
|
|||||||
* This function should be called when a user logs in
|
* This function should be called when a user logs in
|
||||||
*/
|
*/
|
||||||
export async function prefetchUserEmailData(userId: string): Promise<void> {
|
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}`);
|
console.log(`Starting email prefetch for user ${userId}`);
|
||||||
const startTime = Date.now();
|
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`);
|
console.log(`Email prefetch completed for user ${userId} in ${duration.toFixed(2)}s`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error during email prefetch:', error);
|
console.error('Error during email prefetch:', error);
|
||||||
|
} finally {
|
||||||
|
markPrefetchCompleted(userId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -172,6 +228,13 @@ export async function prefetchFolderEmails(
|
|||||||
pages: number = 3,
|
pages: number = 3,
|
||||||
startPage: number = 1
|
startPage: number = 1
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
|
const prefetchKey = `folder:${folder}:${startPage}`;
|
||||||
|
|
||||||
|
// Skip if already in progress or in cooldown
|
||||||
|
if (!shouldPrefetch(userId, prefetchKey)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
console.log(`Prefetching ${pages} pages of emails for folder ${folder} starting from page ${startPage}`);
|
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}`);
|
console.log(`Completed prefetching ${pages} pages of ${folder}`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`Error prefetching folder ${folder}:`, error);
|
console.error(`Error prefetching folder ${folder}:`, error);
|
||||||
|
} finally {
|
||||||
|
markPrefetchCompleted(userId, prefetchKey);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue
Block a user