import { NextResponse } from 'next/server'; import { ImapFlow } from 'imapflow'; import { getServerSession } from 'next-auth'; import { authOptions } from "@/app/api/auth/options"; import { prisma } from '@/lib/prisma'; import { simpleParser } from 'mailparser'; // Simple in-memory cache for email content const emailContentCache = new Map(); const CACHE_TTL = 30 * 60 * 1000; // 30 minutes in milliseconds export async function GET(request: Request, props: { params: Promise<{ id: string }> }) { const params = await props.params; try { console.log(`[DEBUG] GET request for single email ID: ${params.id}`); const session = await getServerSession(authOptions); if (!session || !session.user?.id) { console.log('[DEBUG] Not authenticated'); return NextResponse.json( { error: "Not authenticated" }, { status: 401 } ); } const { id } = params; if (!id) { console.log('[DEBUG] Missing email ID'); return NextResponse.json( { error: "Missing email ID" }, { status: 400 } ); } // Check cache first const cacheKey = `${session.user.id}:${id}`; const cachedContent = emailContentCache.get(cacheKey); const now = Date.now(); if (cachedContent && now - cachedContent.timestamp < CACHE_TTL) { console.log(`[DEBUG] Using cached content for email ${id}`); return NextResponse.json({ id, content: cachedContent.content, contentFetched: true, fromCache: true }); } // Get mail credentials const credentials = await prisma.mailCredentials.findFirst({ where: { userId: session.user.id, }, }); if (!credentials) { console.log('[DEBUG] No mail credentials found'); return NextResponse.json( { error: "No mail credentials found" }, { status: 404 } ); } const { searchParams } = new URL(request.url); const folder = searchParams.get("folder") || "INBOX"; console.log(`[DEBUG] Fetching email ${id} from folder ${folder}`); // Create IMAP client let imapClient: any = null; try { imapClient = new ImapFlow({ host: credentials.host, port: credentials.port, secure: true, auth: { user: credentials.email, pass: credentials.password || undefined, }, logger: false, }); await imapClient.connect(); console.log(`[DEBUG] Connected to IMAP server to fetch email ${id}`); // Select mailbox const mailboxData = await imapClient.mailboxOpen(folder); console.log(`[DEBUG] Opened mailbox ${folder}, total messages: ${mailboxData.exists}`); // Fetch the complete email with its source const message = await imapClient.fetchOne(Number(id), { source: true, envelope: true }); if (!message) { console.log(`[DEBUG] Email ${id} not found`); return NextResponse.json( { error: "Email not found" }, { status: 404 } ); } const { source, envelope } = message; console.log(`[DEBUG] Successfully fetched email source, length: ${source.length}`); // Parse the full email content const parsedEmail = await simpleParser(source.toString()); console.log(`[DEBUG] Parsed email source: has HTML: ${!!parsedEmail.html}, has text: ${!!parsedEmail.text}`); // Prioritize HTML content with fallbacks const content = parsedEmail.html || parsedEmail.textAsHtml || parsedEmail.text || ''; // Cache the content emailContentCache.set(cacheKey, { content, timestamp: now }); // Return the content return NextResponse.json({ id, subject: envelope.subject, content, contentFetched: true }); } catch (error: any) { console.error("[DEBUG] Error fetching email content:", error); return NextResponse.json( { error: "Failed to fetch email content", message: error.message, details: error.stack }, { status: 500 } ); } finally { // Close the mailbox and connection if (imapClient) { try { await imapClient.mailboxClose(); await imapClient.logout(); console.log(`[DEBUG] Closed IMAP connection for email ${id}`); } catch (e) { console.error("[DEBUG] Error closing IMAP connection:", e); } } } } catch (error: any) { console.error("[DEBUG] Unhandled error in GET:", error); return NextResponse.json( { error: "Internal server error", message: error.message, details: error.stack }, { status: 500 } ); } } // Handle marking emails as read export async function POST(request: Request, props: { params: Promise<{ id: string }> }) { const params = await props.params; try { console.log(`[DEBUG] POST request for email ID: ${params.id}`); const session = await getServerSession(authOptions); if (!session || !session.user?.id) { return NextResponse.json( { error: "Not authenticated" }, { status: 401 } ); } const { id } = params; if (!id) { return NextResponse.json( { error: "Missing email ID" }, { status: 400 } ); } const body = await request.json(); const { action } = body; if (action !== 'mark-read' && action !== 'mark-unread') { return NextResponse.json( { error: "Invalid action. Supported actions: mark-read, mark-unread" }, { status: 400 } ); } console.log(`[DEBUG] Processing action ${action} for email ${id}`); // Get mail credentials const credentials = await prisma.mailCredentials.findFirst({ where: { userId: session.user.id, }, }); if (!credentials) { return NextResponse.json( { error: "No mail credentials found" }, { status: 404 } ); } const { searchParams } = new URL(request.url); const folder = searchParams.get("folder") || "INBOX"; // Create IMAP client let imapClient: any = null; try { imapClient = new ImapFlow({ host: credentials.host, port: credentials.port, secure: true, auth: { user: credentials.email, pass: credentials.password || undefined, }, logger: false, }); await imapClient.connect(); console.log(`[DEBUG] Connected to IMAP server for ${action}`); // Select mailbox await imapClient.mailboxOpen(folder); console.log(`[DEBUG] Opened mailbox ${folder} for ${action}`); // Set flag based on action if (action === 'mark-read') { await imapClient.messageFlagsAdd(Number(id), ['\\Seen']); console.log(`[DEBUG] Marked email ${id} as read`); } else { await imapClient.messageFlagsRemove(Number(id), ['\\Seen']); console.log(`[DEBUG] Marked email ${id} as unread`); } return NextResponse.json({ success: true, action }); } catch (error: any) { console.error(`[DEBUG] Error ${action}:`, error); return NextResponse.json( { error: `Failed to ${action}`, message: error.message }, { status: 500 } ); } finally { // Close the mailbox and connection if (imapClient) { try { await imapClient.mailboxClose(); await imapClient.logout(); console.log(`[DEBUG] Closed IMAP connection after ${action}`); } catch (e) { console.error("[DEBUG] Error closing IMAP connection:", e); } } } } catch (error: any) { console.error("[DEBUG] Unhandled error in POST:", error); return NextResponse.json( { error: "Internal server error", message: error.message }, { status: 500 } ); } }