diff --git a/app/api/mail/route.ts b/app/api/mail/route.ts index 074ceb3..b123976 100644 --- a/app/api/mail/route.ts +++ b/app/api/mail/route.ts @@ -101,20 +101,28 @@ export async function GET() { ); } - const imap = new Imap({ - user: credentials.email, - password: credentials.password, - host: credentials.host, - port: credentials.port, - tls: true, - tlsOptions: { rejectUnauthorized: false }, - authTimeout: 30000, - connTimeout: 30000 - }); + const availableMailboxes: string[] = []; + const emailsByFolder: { [key: string]: any[] } = {}; return new Promise((resolve) => { - // Create a map to store emails by folder - const emailsByFolder: { [key: string]: any[] } = {}; + const imap = new Imap({ + user: credentials.email, + password: credentials.password, + host: credentials.host, + port: credentials.port, + tls: true, + tlsOptions: { rejectUnauthorized: false }, + authTimeout: 30000, + connTimeout: 30000 + }); + + imap.once('error', (err: Error) => { + console.error('IMAP error:', err); + resolve(NextResponse.json({ + emails: [], + error: 'IMAP connection error' + })); + }); imap.once('ready', () => { imap.getBoxes((err, boxes) => { @@ -125,81 +133,77 @@ export async function GET() { return; } - const availableMailboxes = Object.keys(boxes); + // Process mailboxes + Object.keys(boxes).forEach((box) => { + availableMailboxes.push(box); + }); + console.log('Available mailboxes:', availableMailboxes); - // Process only the mailboxes we want to use - const foldersToCheck = ['INBOX', 'Sent', 'Trash', 'Spam', 'Drafts', 'Archives', 'Archive']; - let foldersProcessed = 0; + // Process each mailbox + const foldersToProcess = availableMailboxes; + let processedFolders = 0; - const processFolder = (folderName: string) => { - console.log(`Processing folder: ${folderName}`); + function finishProcessing() { + // Combine all emails from all folders + const allEmails = Object.entries(emailsByFolder).flatMap(([folder, emails]) => emails); + console.log('Emails by folder:', Object.fromEntries( + Object.entries(emailsByFolder).map(([folder, emails]) => [folder, emails.length]) + )); + console.log('All folders processed, total emails:', allEmails.length); + + const response = { + emails: allEmails, + folders: availableMailboxes, + mailUrl: process.env.NEXTCLOUD_URL ? `${process.env.NEXTCLOUD_URL}/apps/mail/` : null + }; + imap.end(); + resolve(NextResponse.json(response)); + } + + foldersToProcess.forEach((folderName) => { // Initialize array for this folder emailsByFolder[folderName] = []; imap.openBox(folderName, false, (err, box) => { if (err) { - console.error(`Error opening ${folderName}:`, err); - foldersProcessed++; - if (foldersProcessed === foldersToCheck.length) { + console.error(`Error opening box ${folderName}:`, err); + processedFolders++; + if (processedFolders === foldersToProcess.length) { finishProcessing(); } return; } - console.log(`Successfully opened ${folderName}, total messages: ${box.messages.total}`); - - if (box.messages.total === 0) { - foldersProcessed++; - if (foldersProcessed === foldersToCheck.length) { - finishProcessing(); - } - return; - } - - // Search for messages in this folder - const searchCriteria = ['ALL']; - - imap.search(searchCriteria, (err, results) => { + // Search for all emails in this folder + imap.search(['ALL'], (err, results) => { if (err) { - console.error(`Search error in ${folderName}:`, err); - foldersProcessed++; - if (foldersProcessed === foldersToCheck.length) { + console.error(`Error searching in ${folderName}:`, err); + processedFolders++; + if (processedFolders === foldersToProcess.length) { finishProcessing(); } return; } - const messageNumbers = results - .sort((a, b) => b - a) - .slice(0, 20); - - if (messageNumbers.length === 0) { - foldersProcessed++; - if (foldersProcessed === foldersToCheck.length) { + if (!results || results.length === 0) { + processedFolders++; + if (processedFolders === foldersToProcess.length) { finishProcessing(); } return; } - const f = imap.fetch(messageNumbers, { - bodies: ['HEADER.FIELDS (FROM TO SUBJECT DATE)', 'TEXT'], + // Fetch emails + const fetch = imap.fetch(results, { + bodies: ['HEADER', 'TEXT'], struct: true }); - f.on('message', (msg) => { - const email: any = { - id: '', - from: '', - subject: '', - date: new Date(), - read: true, - starred: false, - body: '', - to: '', - folder: folderName - }; + fetch.on('message', (msg) => { + let header = ''; + let text = ''; msg.on('body', (stream, info) => { let buffer = ''; @@ -207,75 +211,46 @@ export async function GET() { 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 = headers.date?.[0] || new Date().toISOString(); - } else { - email.body = buffer; + if (info.which === 'HEADER') { + header = buffer; + } else if (info.which === 'TEXT') { + text = 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', () => { - // Add email to its folder's array + msg.on('end', () => { + const parsedHeader = Imap.parseHeader(header); + const email = { + id: msg.attributes.uid, + from: parsedHeader.from[0], + to: parsedHeader.to[0], + subject: parsedHeader.subject[0], + date: parsedHeader.date[0], + body: text, + folder: folderName, + flags: msg.attributes.flags + }; emailsByFolder[folderName].push(email); }); }); - f.once('error', (err) => { - console.error(`Fetch error in ${folderName}:`, err); + fetch.on('error', (err) => { + console.error(`Error fetching emails from ${folderName}:`, err); }); - f.once('end', () => { - console.log(`Finished processing ${folderName}, emails found: ${emailsByFolder[folderName].length}`); - foldersProcessed++; - if (foldersProcessed === foldersToCheck.length) { + fetch.on('end', () => { + processedFolders++; + if (processedFolders === foldersToProcess.length) { finishProcessing(); } }); }); }); - }; - - // Process each folder sequentially - foldersToCheck.forEach(folder => processFolder(folder)); + }); }); }); - function finishProcessing() { - // Combine all emails from all folders - const allEmails = Object.entries(emailsByFolder).flatMap(([folder, emails]) => emails); - - console.log('Emails by folder:', Object.fromEntries( - Object.entries(emailsByFolder).map(([folder, emails]) => [folder, emails.length]) - )); - console.log('All folders processed, total emails:', allEmails.length); - - const response = { - emails: allEmails, - mailUrl: process.env.NEXTCLOUD_URL ? `${process.env.NEXTCLOUD_URL}/apps/mail/` : null - }; - imap.end(); - resolve(NextResponse.json(response)); - } - - imap.once('error', (err) => { - console.error('IMAP error:', err); - resolve(NextResponse.json({ - emails: [], - error: 'IMAP connection error' - })); - }); - imap.connect(); }); } catch (error) { diff --git a/app/mail/page.tsx b/app/mail/page.tsx index 7b295b4..52ea7f2 100644 --- a/app/mail/page.tsx +++ b/app/mail/page.tsx @@ -429,7 +429,7 @@ function cleanEmailContent(content: string): string { } // Define the exact folder names from IMAP -type MailFolder = 'INBOX' | 'Sent' | 'Trash' | 'Spam' | 'Drafts' | 'Archives' | 'Archive'; +type MailFolder = string; type MailView = MailFolder | 'starred'; // Map the exact IMAP folders to our sidebar items @@ -445,42 +445,6 @@ const sidebarNavItems = [ label: 'Starred', icon: Star, folder: null // Special case for starred items - }, - { - view: 'Sent' as MailView, - label: 'Sent', - icon: Send, - folder: 'Sent' - }, - { - view: 'Drafts' as MailView, - label: 'Drafts', - icon: Edit, - folder: 'Drafts' - }, - { - view: 'Spam' as MailView, - label: 'Spam', - icon: AlertOctagon, - folder: 'Spam' - }, - { - view: 'Trash' as MailView, - label: 'Trash', - icon: Trash, - folder: 'Trash' - }, - { - view: 'Archives' as MailView, - label: 'Archives', - icon: Archive, - folder: 'Archives' - }, - { - view: 'Archive' as MailView, - label: 'Archive', - icon: Archive, - folder: 'Archive' } ]; @@ -523,6 +487,7 @@ export default function MailPage() { const [attachments, setAttachments] = useState([]); const [folders, setFolders] = useState([]); const [unreadCount, setUnreadCount] = useState(0); + const [availableFolders, setAvailableFolders] = useState([]); // Debug logging for email distribution useEffect(() => { @@ -583,6 +548,11 @@ export default function MailPage() { const data = await response.json(); console.log('Raw email data:', data); + // Get available folders from the API response + if (data.folders) { + setAvailableFolders(data.folders); + } + // Process emails keeping exact folder names const processedEmails = data.emails.map((email: any) => ({ id: Number(email.id), @@ -955,14 +925,6 @@ export default function MailPage() { }); }, [currentView, emails]); - // Filter emails based on current view - const filteredEmails = useMemo(() => { - if (currentView === 'starred') { - return emails.filter(email => email.starred); - } - return emails.filter(email => email.folder === currentView); - }, [emails, currentView]); - // Add a function to move to trash const moveToTrash = async (emailId: number) => { // Update the email in state @@ -1064,7 +1026,7 @@ export default function MailPage() {
- {filteredEmails.length} emails + {emails.length} emails
@@ -1075,7 +1037,7 @@ export default function MailPage() {
- ) : filteredEmails.length === 0 ? ( + ) : emails.length === 0 ? (

@@ -1096,7 +1058,7 @@ export default function MailPage() {

) : (
- {filteredEmails.map((email) => ( + {emails.map((email) => (