import { NextResponse } from 'next/server'; import Imap from 'imap'; import { simpleParser } from 'mailparser'; // Debug logging for environment variables console.log('Environment Variables:', { IMAP_USER: process.env.IMAP_USER, IMAP_HOST: process.env.IMAP_HOST, IMAP_PORT: process.env.IMAP_PORT, IMAP_PASSWORD: process.env.IMAP_PASSWORD ? '***' : undefined, NODE_ENV: process.env.NODE_ENV }); // Helper function to get stored credentials function getStoredCredentials() { if (typeof window === 'undefined') { // Server-side: use environment variables return { user: process.env.IMAP_USER, password: process.env.IMAP_PASSWORD, host: process.env.IMAP_HOST, port: process.env.IMAP_PORT }; } // Client-side: use localStorage const stored = localStorage.getItem('imapCredentials'); return stored ? JSON.parse(stored) : null; } // IMAP configuration const getImapConfig = () => { const credentials = getStoredCredentials(); if (!credentials?.user || !credentials?.password) { throw new Error('Email credentials not found. Please log in first.'); } return { user: credentials.user, password: credentials.password, host: credentials.host || 'mail.infomaniak.com', port: parseInt(credentials.port || '993', 10), tls: true, tlsOptions: { rejectUnauthorized: false, servername: credentials.host || 'mail.infomaniak.com' }, authTimeout: 10000, connTimeout: 10000, debug: console.log }; }; // Debug logging for IMAP configuration console.log('IMAP Configuration:', { user: getImapConfig().user, host: getImapConfig().host, port: getImapConfig().port, tls: getImapConfig().tls, hasPassword: !!getImapConfig().password, authTimeout: getImapConfig().authTimeout, connTimeout: getImapConfig().connTimeout }); interface ImapMessage { header: { from?: string[]; to?: string[]; subject?: string[]; date?: string[]; [key: string]: string[] | undefined; }; body: string; attributes: { flags: string[]; }; } interface ImapError extends Error { type?: string; textCode?: string; source?: string; } interface Email { id: string; from: string; subject: string; date: string; body: string; read: boolean; starred: boolean; } interface EmailHeaders { from: string; subject: string; date: string; } function parseEmailHeaders(buffer: string): EmailHeaders { const headers: EmailHeaders = { from: '', subject: '', date: '' }; const lines = buffer.split('\r\n'); for (const line of lines) { if (line.toLowerCase().startsWith('from:')) { headers.from = line.substring(5).trim(); } else if (line.toLowerCase().startsWith('subject:')) { headers.subject = line.substring(8).trim(); } else if (line.toLowerCase().startsWith('date:')) { headers.date = line.substring(5).trim(); } } return headers; } // Helper function to create a promise-based IMAP connection function createImapConnection(imapConfig: Imap.Config) { return new Promise((resolve, reject) => { console.log('Creating new IMAP connection with config:', { user: imapConfig.user, host: imapConfig.host, port: imapConfig.port, tls: imapConfig.tls }); const imap = new Imap(imapConfig); imap.once('ready', () => { console.log('IMAP connection established successfully'); resolve(imap); }); imap.once('error', (err: ImapError) => { console.error('IMAP connection error:', err); console.error('Error details:', { type: err.type, textCode: err.textCode, source: err.source }); reject(err); }); imap.once('end', () => { console.log('IMAP connection ended'); }); try { console.log('Attempting to connect to IMAP server...'); imap.connect(); } catch (err) { console.error('Error during IMAP connection:', err); reject(err); } }); } // Helper function to promisify the message fetching function fetchMessages(imap: Imap, box: string): Promise { return new Promise((resolve, reject) => { imap.openBox(box, false, (err, mailbox) => { if (err) { reject(err); return; } // Search for all messages imap.search(['ALL'], (err, results) => { if (err) { reject(err); return; } // No messages found if (!results || !results.length) { resolve([]); return; } const fetch = imap.fetch(results, { bodies: ['HEADER.FIELDS (FROM TO SUBJECT DATE)', 'TEXT'], struct: true }); const messages: ImapMessage[] = []; fetch.on('message', (msg) => { const message: Partial = {}; msg.on('body', (stream, info) => { let buffer = ''; stream.on('data', (chunk) => { buffer += chunk.toString('utf8'); }); stream.once('end', () => { if (info.which === 'TEXT') { message.body = buffer; } else { message.header = Imap.parseHeader(buffer); } }); }); msg.once('attributes', (attrs) => { message.attributes = attrs; }); msg.once('end', () => { messages.push(message as ImapMessage); }); }); fetch.once('error', (err) => { reject(err); }); fetch.once('end', () => { resolve(messages); }); }); }); }); } export async function GET() { console.log('Starting email fetch process...'); try { const credentials = getStoredCredentials(); if (!credentials) { return NextResponse.json( { error: 'No credentials found', details: 'Please login first' }, { status: 401 } ); } const imapConfig = { user: credentials.email, password: credentials.password, host: credentials.host, port: credentials.port, tls: true, debug: console.log }; console.log('IMAP Configuration:', { user: imapConfig.user, host: imapConfig.host, port: imapConfig.port, tls: imapConfig.tls, hasPassword: !!imapConfig.password }); const imap = new Imap(imapConfig); return new Promise((resolve, reject) => { imap.once('ready', () => { console.log('IMAP connection ready'); imap.openBox('INBOX', false, (err, box) => { if (err) { console.error('Error opening inbox:', err); imap.end(); reject(new Error('Failed to open inbox')); return; } const fetch = imap.seq.fetch('1:10', { bodies: ['HEADER', 'TEXT'], struct: true }); const messages: Email[] = []; fetch.on('message', (msg) => { const email: Email = { id: '', from: '', subject: '', date: '', body: '', read: false, starred: false }; msg.on('body', (stream) => { let buffer = ''; stream.on('data', (chunk) => { buffer += chunk.toString('utf8'); }); stream.on('end', () => { const headers = parseEmailHeaders(buffer); email.from = headers.from; email.subject = headers.subject; email.date = headers.date; }); }); msg.once('attributes', (attrs) => { email.id = attrs.uid.toString(); email.read = !attrs.flags.includes('\\Unseen'); email.starred = attrs.flags.includes('\\Flagged'); }); msg.once('end', () => { messages.push(email); }); }); fetch.once('error', (err) => { console.error('Fetch error:', err); imap.end(); reject(new Error('Failed to fetch messages')); }); fetch.once('end', () => { imap.end(); resolve(NextResponse.json(messages)); }); }); }); imap.once('error', (err: ImapError) => { console.error('IMAP connection error:', err); console.error('Error details:', { type: err.type, textCode: err.textCode, source: err.source }); imap.end(); reject(new Error(err.message)); }); imap.connect(); }); } catch (error) { console.error('Error in GET handler:', error); if (error instanceof Error) { if (error.message.includes('Invalid login or password')) { return NextResponse.json( { error: 'Invalid login or password', details: error.message }, { status: 401 } ); } return NextResponse.json( { error: 'Failed to fetch emails', details: error.message }, { status: 500 } ); } return NextResponse.json( { error: 'Unknown error occurred' }, { status: 500 } ); } } // Add endpoint to get mailboxes export async function POST(request: Request) { try { const imap = await createImapConnection(getImapConfig()) as Imap; const mailboxes = await new Promise((resolve, reject) => { imap.getBoxes((err, boxes) => { if (err) reject(err); resolve(boxes); }); }); imap.end(); return NextResponse.json({ mailboxes }); } catch (error) { console.error('Error fetching mailboxes:', error); return NextResponse.json({ error: 'Failed to fetch mailboxes' }, { status: 500 }); } }