courrier multi account restore compose
This commit is contained in:
parent
60cb617d7f
commit
363e999dcd
@ -15,8 +15,6 @@ import {
|
|||||||
} from '@/lib/services/prefetch-service';
|
} from '@/lib/services/prefetch-service';
|
||||||
import { Email, EmailData } from './use-courrier';
|
import { Email, EmailData } from './use-courrier';
|
||||||
import { formatEmailForReplyOrForward } from '@/lib/utils/email-formatter';
|
import { formatEmailForReplyOrForward } from '@/lib/utils/email-formatter';
|
||||||
import { createClient } from 'redis';
|
|
||||||
import { createImapConnection } from '@/lib/services/imap-service';
|
|
||||||
|
|
||||||
// Add a global dispatcher for compatibility with older code
|
// Add a global dispatcher for compatibility with older code
|
||||||
// This is a temporary solution until we fully migrate to the reducer pattern
|
// This is a temporary solution until we fully migrate to the reducer pattern
|
||||||
@ -27,45 +25,6 @@ declare global {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a dedicated Redis service module
|
|
||||||
const redisClient = createClient({
|
|
||||||
url: process.env.REDIS_URL,
|
|
||||||
socket: {
|
|
||||||
reconnectStrategy: (retries) => Math.min(retries * 50, 1000)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Initialize once and export
|
|
||||||
let clientPromise = null;
|
|
||||||
export async function getRedisClient() {
|
|
||||||
if (!clientPromise) {
|
|
||||||
clientPromise = redisClient.connect().then(() => redisClient);
|
|
||||||
clientPromise.catch(() => clientPromise = null);
|
|
||||||
}
|
|
||||||
return clientPromise;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Server-side IMAP connection management
|
|
||||||
const imapConnections = new Map();
|
|
||||||
|
|
||||||
async function getImapConnection(userId, accountId) {
|
|
||||||
const key = `${userId}:${accountId}`;
|
|
||||||
|
|
||||||
if (imapConnections.has(key)) {
|
|
||||||
const conn = imapConnections.get(key);
|
|
||||||
// Check if connection is still alive
|
|
||||||
if (conn.isConnected()) {
|
|
||||||
return conn;
|
|
||||||
}
|
|
||||||
imapConnections.delete(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create new connection and store it
|
|
||||||
const conn = await createImapConnection(userId, accountId);
|
|
||||||
imapConnections.set(key, conn);
|
|
||||||
return conn;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const useEmailState = () => {
|
export const useEmailState = () => {
|
||||||
const [state, dispatch] = useReducer(emailReducer, initialState);
|
const [state, dispatch] = useReducer(emailReducer, initialState);
|
||||||
const { data: session } = useSession();
|
const { data: session } = useSession();
|
||||||
@ -663,16 +622,75 @@ export const useEmailState = () => {
|
|||||||
// Don't fetch if we're already fetching
|
// Don't fetch if we're already fetching
|
||||||
if (state.isLoadingUnreadCounts) return;
|
if (state.isLoadingUnreadCounts) return;
|
||||||
|
|
||||||
// Implement exponential backoff with higher thresholds
|
// Skip fetching if an email was viewed recently (within last 5 seconds)
|
||||||
// Current implementation does this but needs tuning
|
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
const lastViewedTimestamp = (window as any).__lastViewedEmailTimestamp || 0;
|
const lastViewedTimestamp = (window as any).__lastViewedEmailTimestamp || 0;
|
||||||
if (lastViewedTimestamp && now - lastViewedTimestamp < 5000) { // Increase from 2000ms
|
if (lastViewedTimestamp && now - lastViewedTimestamp < 5000) { // Increased from 2000ms for better performance
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cache unread counts with longer TTL (30-60 minutes)
|
// Reset failure tracking if it's been more than 1 minute since last failure
|
||||||
// Implement batch fetching for all folders at once
|
if ((window as any).__unreadCountFailures && now - (window as any).__unreadCountFailures > 60000) {
|
||||||
|
(window as any).__unreadCountFailures = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exponential backoff for failures
|
||||||
|
if ((window as any).__unreadCountFailures > 0) {
|
||||||
|
const backoffMs = Math.min(30000, 1000 * Math.pow(2, (window as any).__unreadCountFailures - 1));
|
||||||
|
if ((window as any).__unreadCountFailures && now - (window as any).__unreadCountFailures < backoffMs) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
dispatch({ type: 'SET_LOADING_UNREAD_COUNTS', payload: true });
|
||||||
|
|
||||||
|
const timeBeforeCall = performance.now();
|
||||||
|
logEmailOp('FETCH_UNREAD', 'Fetching unread counts from API');
|
||||||
|
|
||||||
|
const response = await fetch('/api/courrier/unread-counts', {
|
||||||
|
method: 'GET',
|
||||||
|
headers: { 'Content-Type': 'application/json' }
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
// If request failed, increment failure count but cap it
|
||||||
|
(window as any).__unreadCountFailures = Math.min((window as any).__unreadCountFailures || 0 + 1, 10);
|
||||||
|
const failures = (window as any).__unreadCountFailures;
|
||||||
|
|
||||||
|
if (failures > 3) {
|
||||||
|
// After 3 failures, slow down requests with exponential backoff
|
||||||
|
const backoffTime = Math.min(Math.pow(2, failures - 3) * 1000, 30000); // Max 30 seconds
|
||||||
|
logEmailOp('FETCH_UNREAD', `API failure #${failures}, backing off for ${backoffTime}ms`);
|
||||||
|
|
||||||
|
// Schedule next attempt with backoff
|
||||||
|
if ((window as any).__failureBackoffTimer) {
|
||||||
|
clearTimeout((window as any).__failureBackoffTimer);
|
||||||
|
}
|
||||||
|
|
||||||
|
(window as any).__failureBackoffTimer = setTimeout(() => {
|
||||||
|
fetchUnreadCounts();
|
||||||
|
}, backoffTime);
|
||||||
|
|
||||||
|
throw new Error(`Failed to fetch unread counts: ${response.status}`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Reset failure counter on success
|
||||||
|
(window as any).__unreadCountFailures = 0;
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
const timeAfterCall = performance.now();
|
||||||
|
logEmailOp('FETCH_UNREAD', `Received unread counts in ${(timeAfterCall - timeBeforeCall).toFixed(2)}ms`, data);
|
||||||
|
|
||||||
|
if (data && typeof data === 'object') {
|
||||||
|
dispatch({ type: 'SET_UNREAD_COUNTS', payload: data });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching unread counts:', error);
|
||||||
|
} finally {
|
||||||
|
dispatch({ type: 'SET_LOADING_UNREAD_COUNTS', payload: false });
|
||||||
|
}
|
||||||
}, [dispatch, session?.user, state.isLoadingUnreadCounts, logEmailOp]);
|
}, [dispatch, session?.user, state.isLoadingUnreadCounts, logEmailOp]);
|
||||||
|
|
||||||
// Calculate and update unread counts
|
// Calculate and update unread counts
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user