import { NextResponse } from 'next/server'; import { getServerSession } from 'next-auth'; import { authOptions } from '@/app/api/auth/[...nextauth]/route'; import { getImapConnection } from '@/lib/services/email-service'; import { prisma } from '@/lib/prisma'; import { getRedisClient } from '@/lib/redis'; // Cache TTL for unread counts (30 seconds) const UNREAD_COUNTS_CACHE_TTL = 30; // Key for unread counts cache const UNREAD_COUNTS_CACHE_KEY = (userId: string) => `email:unread:${userId}`; /** * API route for fetching unread counts for email folders * Optimized with proper caching and connection reuse */ export async function GET(request: Request) { try { // Authenticate user const session = await getServerSession(authOptions); if (!session || !session.user?.id) { return NextResponse.json( { error: "Not authenticated" }, { status: 401 } ); } const userId = session.user.id; const redis = getRedisClient(); // First try to get from cache const cachedCounts = await redis.get(UNREAD_COUNTS_CACHE_KEY(userId)); if (cachedCounts) { // Use cached results if available console.log(`[UNREAD_API] Using cached unread counts for user ${userId}`); return NextResponse.json(JSON.parse(cachedCounts)); } console.log(`[UNREAD_API] Cache miss for user ${userId}, fetching unread counts`); // Get all accounts from the database directly const accounts = await prisma.mailCredentials.findMany({ where: { userId }, select: { id: true, email: true } }); console.log(`[UNREAD_API] Found ${accounts.length} accounts for user ${userId}`); if (accounts.length === 0) { return NextResponse.json({ default: {} }); } // Mapping to hold the unread counts const unreadCounts: Record> = {}; // For each account, get the unread counts for standard folders for (const account of accounts) { const accountId = account.id; try { // Get IMAP connection for this account console.log(`[UNREAD_API] Processing account ${accountId} (${account.email})`); const client = await getImapConnection(userId, accountId); unreadCounts[accountId] = {}; // Standard folders to check const standardFolders = ['INBOX', 'Sent', 'Drafts', 'Trash', 'Junk', 'Spam', 'Archive', 'Sent Items', 'Archives', 'Notes', 'Éléments supprimés']; // Get mailboxes for this account to check if folders exist const mailboxes = await client.list(); const availableFolders = mailboxes.map(mb => mb.path); // Check each standard folder if it exists for (const folder of standardFolders) { // Skip if folder doesn't exist in this account if (!availableFolders.includes(folder) && !availableFolders.some(f => f.toLowerCase() === folder.toLowerCase())) { continue; } try { // Check folder status without opening it (more efficient) const status = await client.status(folder, { unseen: true }); if (status && typeof status.unseen === 'number') { // Store the unread count unreadCounts[accountId][folder] = status.unseen; // Also store with prefixed version for consistency unreadCounts[accountId][`${accountId}:${folder}`] = status.unseen; console.log(`[UNREAD_API] Account ${accountId}, folder ${folder}: ${status.unseen} unread`); } } catch (folderError) { console.error(`[UNREAD_API] Error getting unread count for ${accountId}:${folder}:`, folderError); // Continue to next folder even if this one fails } } // Don't close the connection - let the connection pool handle it // This avoids opening and closing connections repeatedly } catch (accountError) { console.error(`[UNREAD_API] Error processing account ${accountId}:`, accountError); } } // Save to cache for 30 seconds to avoid hammering the IMAP server await redis.set( UNREAD_COUNTS_CACHE_KEY(userId), JSON.stringify(unreadCounts), 'EX', UNREAD_COUNTS_CACHE_TTL ); return NextResponse.json(unreadCounts); } catch (error: any) { console.error("[UNREAD_API] Error fetching unread counts:", error); return NextResponse.json( { error: "Failed to fetch unread counts", message: error.message }, { status: 500 } ); } } /** * Helper to get all account IDs for a user */ async function getUserAccountIds(userId: string): Promise { try { // Get credentials for all accounts from the email service // This is a simplified version - you should replace this with your actual logic // to retrieve the user's accounts // First try the default account const defaultClient = await getImapConnection(userId, 'default'); const accounts = ['default']; try { // Try to get other accounts if they exist // This is just a placeholder - implement your actual account retrieval logic // Close the default connection await defaultClient.logout(); } catch (error) { console.error('[UNREAD_API] Error getting additional accounts:', error); } return accounts; } catch (error) { console.error('[UNREAD_API] Error getting account IDs:', error); return ['default']; // Return at least the default account } }