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 }); // Store for credentials (in memory, you might want to use a proper storage solution) let storedCredentials: { user: string; password: string; host: string; port: string; } | null = null; // Helper function to get stored credentials function getStoredCredentials() { // Server-side: use stored credentials if (typeof window === 'undefined') { return storedCredentials; } // 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(config: any): Promise { return new Promise((resolve, reject) => { const imap = new Imap({ user: config.user, password: config.password, host: config.host, port: config.port, tls: true, tlsOptions: { rejectUnauthorized: false, servername: config.host }, authTimeout: 10000, connTimeout: 10000, debug: console.log, autotls: 'always', keepalive: true }); 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('[connection] Ended')); imap.once('close', () => console.log('[connection] Closed')); try { console.log('Attempting to connect to IMAP server...'); console.log('Using credentials:', { user: config.user, passwordLength: config.password.length, host: config.host, port: config.port }); 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() { try { // Get stored credentials const credentials = getStoredCredentials(); if (!credentials) { return NextResponse.json( { error: 'No IMAP credentials found. Please configure your email account.' }, { status: 401 } ); } console.log('Starting email fetch process...'); console.log('IMAP Configuration:', { user: credentials.user, host: credentials.host, port: credentials.port, tls: true, hasPassword: !!credentials.password }); // Create IMAP connection const imap = await createImapConnection({ user: credentials.user, password: credentials.password, host: credentials.host, port: parseInt(credentials.port), tls: true }); 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 } ); } } export async function POST(request: Request) { try { const body = await request.json(); const { email, password, host, port } = body; if (!email || !password || !host || !port) { return NextResponse.json( { error: 'Missing required fields' }, { status: 400 } ); } // Store credentials storedCredentials = { user: email, password, host, port }; // Test the connection const imap = await createImapConnection(storedCredentials); imap.end(); return NextResponse.json({ success: true }); } catch (error) { console.error('Error in POST 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 connect to email server', details: error.message }, { status: 500 } ); } return NextResponse.json( { error: 'Unknown error occurred' }, { status: 500 } ); } }