diff --git a/app/api/mail/route.ts b/app/api/mail/route.ts index f6fdf50..b16ae3a 100644 --- a/app/api/mail/route.ts +++ b/app/api/mail/route.ts @@ -107,164 +107,111 @@ export async function GET() { password: '***' }); - const imapConfig: ImapConfig = { + const imap = new Imap({ user: credentials.email, password: credentials.password, host: credentials.host, port: credentials.port, tls: true, - authTimeout: 10000, - connTimeout: 10000, - debug: (info: string) => console.log('IMAP Debug:', info) - }; + tlsOptions: { rejectUnauthorized: false }, + authTimeout: 30000, + connTimeout: 30000 + }); - console.log('Connecting to IMAP server...'); - const imap = new Imap(imapConfig); + return new Promise((resolve) => { + const emails: Email[] = []; - const connectPromise = new Promise((resolve, reject) => { imap.once('ready', () => { - console.log('IMAP connection ready'); - resolve(true); + imap.openBox('INBOX', false, (err, box) => { + if (err) { + imap.end(); + resolve(NextResponse.json({ error: 'Failed to open inbox' }, { status: 500 })); + return; + } + + const total = box.messages.total; + const start = Math.max(1, total - 19); // Get last 20 emails + + if (total === 0) { + imap.end(); + resolve(NextResponse.json({ emails: [], mailUrl: null })); + return; + } + + const f = imap.seq.fetch(`${start}:${total}`, { + bodies: ['HEADER.FIELDS (FROM TO SUBJECT DATE)', 'TEXT'], + struct: true + }); + + f.on('message', (msg) => { + const email: any = { + id: '', + from: '', + subject: '', + date: new Date(), + read: true, + starred: false, + body: '', + to: '' + }; + + msg.on('body', (stream, info) => { + let buffer = ''; + stream.on('data', (chunk) => { + buffer += chunk.toString('utf8'); + }); + stream.on('end', () => { + if (info.which === 'HEADER.FIELDS (FROM TO SUBJECT DATE)') { + const headers = Imap.parseHeader(buffer); + email.from = headers.from?.[0] || ''; + email.to = headers.to?.[0] || ''; + email.subject = headers.subject?.[0] || '(No subject)'; + email.date = new Date(headers.date?.[0] || Date.now()); + } else { + email.body = buffer; + } + }); + }); + + msg.once('attributes', (attrs) => { + email.id = attrs.uid; + email.read = attrs.flags?.includes('\\Seen') || false; + email.starred = attrs.flags?.includes('\\Flagged') || false; + }); + + msg.once('end', () => { + emails.push(email); + }); + }); + + f.once('error', (err) => { + console.error('Fetch error:', err); + imap.end(); + resolve(NextResponse.json({ error: 'Failed to fetch emails' }, { status: 500 })); + }); + + f.once('end', () => { + imap.end(); + resolve(NextResponse.json({ + emails: emails.sort((a, b) => b.date.getTime() - a.date.getTime()), + mailUrl: null + })); + }); + }); }); - imap.once('error', (err: Error) => { - console.error('IMAP connection error:', err); - reject(err); + + imap.once('error', (err) => { + console.error('IMAP error:', err); + resolve(NextResponse.json({ error: 'IMAP connection error' }, { status: 500 })); }); + imap.connect(); }); - - await connectPromise; - - console.log('Opening INBOX...'); - const openBoxPromise = new Promise((resolve, reject) => { - imap.openBox('INBOX', false, (err: Error | null, box: ImapBox) => { - if (err) { - console.error('Error opening INBOX:', err); - reject(err); - } else { - console.log('INBOX opened successfully'); - resolve(box); - } - }); - }); - - const box = await openBoxPromise; - console.log('Total messages in INBOX:', box.messages.total); - - const fetchPromise = (imap: Imap, seqno: number): Promise => { - return new Promise((resolve, reject) => { - const f = imap.fetch(seqno, { - bodies: ['HEADER', 'TEXT'], - struct: true, - markSeen: false - }); - - f.on('message', (msg: ImapMessage) => { - resolve(msg); - }); - - f.once('error', (err: Error) => { - console.error('Fetch error:', err); - reject(err); - }); - }); - }; - - const processMessage = (msg: ImapMessage): Promise => { - return new Promise((resolve, reject) => { - let headerContent = ''; - let bodyContent = ''; - let headersParsed = false; - let bodyParsed = false; - - // Process headers - msg.body['HEADER']?.on('data', (chunk: Buffer) => { - headerContent += chunk.toString('utf8'); - }); - - // Process body - msg.body['TEXT']?.on('data', (chunk: Buffer) => { - bodyContent += chunk.toString('utf8'); - }); - - const tryResolve = () => { - if (headersParsed && bodyParsed) { - try { - const headers = parseEmailHeaders(headerContent); - const contentType = headerContent.match(/Content-Type:\s*([^;\r\n]+)/i)?.[1] || 'text/plain'; - const decodedBody = decodeEmailBody(bodyContent, contentType); - - resolve({ - uid: msg.attributes.uid, - flags: msg.attributes.flags, - size: msg.attributes.size, - body: decodedBody, - ...headers - }); - } catch (error) { - console.error('Error processing message:', error); - reject(error); - } - } - }; - - msg.body['HEADER']?.on('end', () => { - headersParsed = true; - tryResolve(); - }); - - msg.body['TEXT']?.on('end', () => { - bodyParsed = true; - tryResolve(); - }); - - msg.body['HEADER']?.on('error', reject); - msg.body['TEXT']?.on('error', reject); - }); - }; - - const messages: any[] = []; - // Fetch the most recent 20 emails - const start = Math.max(1, box.messages.total - 19); // last 20 emails - const end = box.messages.total; - for (let seqno = start; seqno <= end; seqno++) { - const msg = await fetchPromise(imap, seqno); - const processedMsg = await processMessage(msg); - messages.push(processedMsg); - } - imap.end(); - - console.log('Raw messages:', messages); - - const emails: Email[] = messages.map((msg) => { - return { - id: msg.uid.toString(), - from: msg.from, - subject: msg.subject || '(No subject)', - date: new Date(msg.date), - read: !msg.flags?.includes('\\Unseen'), - starred: msg.flags?.includes('\\Flagged') || false, - body: msg.body || '', - to: msg.to - }; - }); - - return NextResponse.json({ - emails, - mailUrl: process.env.NEXTCLOUD_URL ? `${process.env.NEXTCLOUD_URL}/apps/mail/` : null - }, { - headers: { - 'Content-Type': 'application/json' - } - }); } catch (error) { - console.error('Error fetching emails:', error); - const status = error instanceof Error && error.message.includes('Invalid login') - ? 401 - : 500; + console.error('Error in mail API:', error); return NextResponse.json( - { error: error instanceof Error ? error.message : 'Failed to fetch emails' }, - { status } + { error: error instanceof Error ? error.message : 'Unknown error' }, + { status: 500 } ); } } diff --git a/components/email.tsx b/components/email.tsx index bfdc01f..6e56e5b 100644 --- a/components/email.tsx +++ b/components/email.tsx @@ -38,23 +38,21 @@ export function Email() { if (!isRefresh) setLoading(true); try { - const response = await fetch('/api/emails'); - const data: EmailResponse = await response.json(); + const response = await fetch('/api/mail'); if (!response.ok) { - // Handle session expiration if (response.status === 401) { - signIn(); // Redirect to login + signIn(); return; } - // Handle specific error messages - if (response.status === 404) { - setError("L'application Mail n'est pas disponible sur Nextcloud. Veuillez contacter votre administrateur."); - return; - } - - throw new Error(data.error || 'Failed to fetch emails'); + const errorData = await response.json(); + throw new Error(errorData.error || 'Failed to fetch emails'); + } + + const data = await response.json(); + if (!Array.isArray(data.emails)) { + throw new Error('Invalid response format'); } setEmails(data.emails || []); @@ -62,7 +60,7 @@ export function Email() { setError(null); } catch (err) { console.error('Error fetching emails:', err); - setError(err instanceof Error ? err.message : 'Erreur lors de la récupération des emails'); + setError(err instanceof Error ? err.message : 'Error fetching emails'); } finally { setLoading(false); setRefreshing(false);