mail page connected with folders 6

This commit is contained in:
alma 2025-04-16 17:00:07 +02:00
parent 7cf49d8cf3
commit fd2a4f1483
2 changed files with 91 additions and 112 deletions

View File

@ -91,7 +91,7 @@ function getStoredCredentials(): StoredCredentials | null {
} }
} }
export async function GET() { export async function GET(request: Request) {
try { try {
const credentials = getStoredCredentials(); const credentials = getStoredCredentials();
if (!credentials) { if (!credentials) {
@ -101,8 +101,10 @@ export async function GET() {
); );
} }
const availableMailboxes: string[] = []; // Get the current folder from the URL
const emailsByFolder: { [key: string]: any[] } = {}; const url = new URL(request.url);
const folder = url.searchParams.get('folder') || 'INBOX';
const limit = 50; // Limit number of emails per folder
return new Promise((resolve) => { return new Promise((resolve) => {
const imap = new Imap({ const imap = new Imap({
@ -116,7 +118,6 @@ export async function GET() {
connTimeout: 30000 connTimeout: 30000
}); });
// Add a timeout to prevent hanging
const timeout = setTimeout(() => { const timeout = setTimeout(() => {
console.error('IMAP connection timeout'); console.error('IMAP connection timeout');
imap.end(); imap.end();
@ -124,7 +125,7 @@ export async function GET() {
emails: [], emails: [],
error: 'Connection timeout' error: 'Connection timeout'
})); }));
}, 60000); }, 30000);
imap.once('error', (err: Error) => { imap.once('error', (err: Error) => {
console.error('IMAP error:', err); console.error('IMAP error:', err);
@ -145,133 +146,111 @@ export async function GET() {
return; return;
} }
// Process mailboxes const availableMailboxes = Object.keys(boxes);
Object.keys(boxes).forEach((box) => {
availableMailboxes.push(box);
});
console.log('Available mailboxes:', availableMailboxes); console.log('Available mailboxes:', availableMailboxes);
// Process each mailbox // Only process the requested folder
const foldersToProcess = availableMailboxes; imap.openBox(folder, false, (err, box) => {
let processedFolders = 0; if (err) {
let activeFetches = 0; console.error(`Error opening box ${folder}:`, err);
function checkCompletion() {
processedFolders++;
if (processedFolders === foldersToProcess.length && activeFetches === 0) {
clearTimeout(timeout); clearTimeout(timeout);
finishProcessing(); imap.end();
resolve(NextResponse.json({ emails: [], error: `Failed to open folder ${folder}` }));
return;
} }
}
function finishProcessing() { // Search for emails in this folder, limited to the most recent ones
// Combine all emails from all folders imap.search(['ALL'], (err, results) => {
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) { if (err) {
console.error(`Error opening box ${folderName}:`, err); console.error(`Error searching in ${folder}:`, err);
checkCompletion(); clearTimeout(timeout);
imap.end();
resolve(NextResponse.json({ emails: [], error: `Failed to search in ${folder}` }));
return; return;
} }
// Search for all emails in this folder if (!results || results.length === 0) {
imap.search(['ALL'], (err, results) => { clearTimeout(timeout);
if (err) { imap.end();
console.error(`Error searching in ${folderName}:`, err); resolve(NextResponse.json({
checkCompletion(); emails: [],
return; folders: availableMailboxes,
} mailUrl: process.env.NEXTCLOUD_URL ? `${process.env.NEXTCLOUD_URL}/apps/mail/` : null
}));
return;
}
if (!results || results.length === 0) { // Take only the most recent emails up to the limit
checkCompletion(); const recentResults = results.slice(-limit);
return; const emails: any[] = [];
}
activeFetches++; const fetch = imap.fetch(recentResults, {
// Fetch emails bodies: ['HEADER', 'TEXT'],
const fetch = imap.fetch(results, { struct: true
bodies: ['HEADER', 'TEXT'], });
struct: true
fetch.on('message', (msg) => {
let header = '';
let text = '';
let messageId: number | null = null;
let messageFlags: string[] = [];
msg.once('attributes', (attrs) => {
messageId = attrs.uid;
messageFlags = attrs.flags || [];
}); });
fetch.on('message', (msg) => { msg.on('body', (stream, info) => {
let header = ''; let buffer = '';
let text = ''; stream.on('data', (chunk) => {
let messageId: number | null = null; buffer += chunk.toString('utf8');
let messageFlags: string[] = [];
// Handle attributes first
msg.once('attributes', (attrs) => {
messageId = attrs.uid;
messageFlags = attrs.flags || [];
}); });
stream.on('end', () => {
msg.on('body', (stream, info) => { if (info.which === 'HEADER') {
let buffer = ''; header = buffer;
stream.on('data', (chunk) => { } else if (info.which === 'TEXT') {
buffer += chunk.toString('utf8'); text = buffer;
});
stream.on('end', () => {
if (info.which === 'HEADER') {
header = buffer;
} else if (info.which === 'TEXT') {
text = buffer;
}
});
});
msg.on('end', () => {
if (!messageId) {
console.error('No message ID found for email');
return;
} }
const parsedHeader = Imap.parseHeader(header);
const email = {
id: messageId,
from: parsedHeader.from?.[0] || '',
to: parsedHeader.to?.[0] || '',
subject: parsedHeader.subject?.[0] || '(No subject)',
date: parsedHeader.date?.[0] || new Date().toISOString(),
body: text,
folder: folderName,
flags: messageFlags
};
emailsByFolder[folderName].push(email);
}); });
}); });
fetch.on('error', (err) => { msg.on('end', () => {
console.error(`Error fetching emails from ${folderName}:`, err); if (!messageId) {
activeFetches--; console.error('No message ID found for email');
checkCompletion(); return;
}); }
fetch.on('end', () => { const parsedHeader = Imap.parseHeader(header);
activeFetches--; const email = {
checkCompletion(); id: messageId,
from: parsedHeader.from?.[0] || '',
to: parsedHeader.to?.[0] || '',
subject: parsedHeader.subject?.[0] || '(No subject)',
date: parsedHeader.date?.[0] || new Date().toISOString(),
body: text,
folder: folder,
flags: messageFlags
};
emails.push(email);
}); });
}); });
fetch.on('error', (err) => {
console.error(`Error fetching emails from ${folder}:`, err);
clearTimeout(timeout);
imap.end();
resolve(NextResponse.json({ emails: [], error: `Failed to fetch emails from ${folder}` }));
});
fetch.on('end', () => {
clearTimeout(timeout);
imap.end();
resolve(NextResponse.json({
emails: emails,
folders: availableMailboxes,
mailUrl: process.env.NEXTCLOUD_URL ? `${process.env.NEXTCLOUD_URL}/apps/mail/` : null
}));
});
}); });
}); });
}); });

View File

@ -540,7 +540,7 @@ export default function MailPage() {
setLoading(true); setLoading(true);
setError(null); setError(null);
const response = await fetch('/api/mail'); const response = await fetch(`/api/mail?folder=${currentView}`);
if (!response.ok) { if (!response.ok) {
throw new Error('Failed to load emails'); throw new Error('Failed to load emails');
} }