diff --git a/app/api/courrier/route.ts b/app/api/courrier/route.ts index e197c4f5..bd0f93f5 100644 --- a/app/api/courrier/route.ts +++ b/app/api/courrier/route.ts @@ -100,18 +100,154 @@ function getCacheKey(userId: string, folder: string, page: number, limit: number return `${userId}:${folder}:${page}:${limit}`; } -export async function GET( - request: Request, - { params }: { params: { id: string } } -) { - // Get the ID from the URL - const id = await Promise.resolve(params.id); - - // Create the new URL for redirect - const url = new URL(`/api/courrier/${id}`, request.url); - - // Redirect to the new endpoint - return NextResponse.redirect(url); +export async function GET(request: Request) { + try { + // 1. Authentication + const session = await getServerSession(authOptions); + if (!session?.user?.id) { + return NextResponse.json( + { error: 'Unauthorized' }, + { status: 401 } + ); + } + + // 2. Parse request parameters + const url = new URL(request.url); + const folder = url.searchParams.get('folder') || 'INBOX'; + const page = parseInt(url.searchParams.get('page') || '1'); + const limit = parseInt(url.searchParams.get('limit') || '20'); + const preview = url.searchParams.get('preview') === 'true'; + const forceRefresh = url.searchParams.get('refresh') === 'true'; + + // 3. Check cache first (unless refresh requested) + const cacheKey = getCacheKey(session.user.id, folder, page, limit); + if (!forceRefresh) { + const cachedData = emailCache.get(cacheKey); + if (cachedData) { + return NextResponse.json(cachedData); + } + } + + // 4. Get credentials + const credentials = await prisma.mailCredentials.findUnique({ + where: { + userId: session.user.id + } + }); + + if (!credentials) { + return NextResponse.json( + { error: 'No mail credentials found. Please configure your email account.' }, + { status: 401 } + ); + } + + // 5. Get IMAP client from pool (or create new) + const client = await getImapClient(session.user.id, credentials); + + try { + // 6. Get mailboxes (with caching) + let availableFolders: string[]; + const foldersCacheKey = `folders:${session.user.id}`; + const cachedFolders = emailCache.get(foldersCacheKey); + + if (cachedFolders) { + availableFolders = cachedFolders; + } else { + const mailboxes = await client.list(); + availableFolders = mailboxes.map(box => box.path); + emailCache.set(foldersCacheKey, availableFolders); + } + + // 7. Open mailbox + const mailbox = await client.mailboxOpen(folder); + + const result: Email[] = []; + + // 8. Fetch emails (if any exist) + // Define start and end variables HERE + let start = 1; + let end = 0; + + if (mailbox.exists > 0) { + // Calculate range with boundaries + start = Math.min((page - 1) * limit + 1, mailbox.exists); + end = Math.min(start + limit - 1, mailbox.exists); + + // Use sequence numbers in descending order for newest first + const range = `${mailbox.exists - end + 1}:${mailbox.exists - start + 1}`; + + // Fetch messages with optimized options + const options: any = { + envelope: true, + flags: true, + bodyStructure: true + }; + + // Only fetch preview if requested + if (preview) { + options.bodyParts = ['TEXT', 'HTML']; + } + + const messages = await client.fetch(range, options); + + // Process messages + for await (const message of messages) { + // Extract preview content correctly + let previewContent = null; + if (preview && message.bodyParts) { + // Try HTML first, then TEXT + const htmlPart = message.bodyParts.get('HTML'); + const textPart = message.bodyParts.get('TEXT'); + previewContent = htmlPart?.toString() || textPart?.toString() || null; + } + + const email: Email = { + id: message.uid.toString(), + from: message.envelope.from?.[0]?.address || '', + fromName: message.envelope.from?.[0]?.name || + message.envelope.from?.[0]?.address?.split('@')[0] || '', + to: message.envelope.to?.map((addr: any) => addr.address).join(', ') || '', + subject: message.envelope.subject || '(No subject)', + date: message.envelope.date?.toISOString() || new Date().toISOString(), + read: message.flags.has('\\Seen'), + starred: message.flags.has('\\Flagged'), + folder: mailbox.path, + hasAttachments: message.bodyStructure?.type === 'multipart', + flags: Array.from(message.flags), + preview: previewContent + }; + + result.push(email); + } + } + + // 9. Prepare response data + const responseData = { + emails: result, + folders: availableFolders, + total: mailbox.exists, + hasMore: end < mailbox.exists, + page, + limit + }; + + // 10. Cache the results + emailCache.set(cacheKey, responseData); + + return NextResponse.json(responseData); + } catch (error) { + // Connection error - remove from pool + connectionPool.delete(session.user.id); + throw error; + } + } catch (error) { + console.error('Error in courrier route:', error); + return NextResponse.json( + { error: 'An unexpected error occurred' }, + { status: 500 } + ); + } } // Helper method to release connection when app shutting down @@ -120,4 +256,4 @@ export async function cleanup() { await connection.client.logout().catch(console.error); connectionPool.delete(userId); } -} \ No newline at end of file +} \ No newline at end of file