courrier multi account restore compose

This commit is contained in:
alma 2025-04-28 15:12:38 +02:00
parent e2805dca07
commit 266805950e

View File

@ -266,114 +266,84 @@ interface FetchOptions {
*/ */
export async function getEmails( export async function getEmails(
userId: string, userId: string,
folder: string = 'INBOX', folder: string,
page: number = 1, page: number = 1,
perPage: number = 20, perPage: number = 20,
searchQuery: string = '',
accountId?: string accountId?: string
): Promise<EmailListResult> { ): Promise<EmailMessage[]> {
console.log(`Fetching emails for user ${userId}${accountId ? ` account ${accountId}` : ''} in folder ${folder}`);
// Extract the base folder name if it's an account-specific folder
const baseFolder = accountId && folder.includes(`-${accountId}`)
? folder.split(`-${accountId}`)[0]
: folder;
console.log(`Using base folder name: ${baseFolder} for IMAP operations`);
// Try to get from cache first
const cached = await getCachedEmailList(userId, accountId || 'default', folder, page, perPage);
if (cached) {
console.log(`Using cached email list for ${userId}${accountId ? ` account ${accountId}` : ''} in folder ${folder}`);
return cached;
}
// Get IMAP connection for the specific account
const client = await getImapConnection(userId, accountId);
if (!client) {
throw new Error('Failed to get IMAP connection');
}
try { try {
// Open the mailbox using the base folder name // Extract the base folder name by removing the accountId suffix if present
await client.mailboxOpen(baseFolder); const baseFolder = accountId && folder.includes(`-${accountId}`)
? folder.split(`-${accountId}`)[0]
: folder;
// Get total count of messages console.log(`Fetching emails for folder: ${baseFolder} (original: ${folder})`);
const status = await client.status(baseFolder, { messages: true });
const totalEmails = status.messages || 0;
const totalPages = Math.ceil(totalEmails / perPage);
// Calculate the range of messages to fetch // Get IMAP connection for the account
const start = (page - 1) * perPage + 1; const imap = await getImapConnection(userId, accountId);
const end = Math.min(start + perPage - 1, totalEmails); if (!imap) {
throw new Error('Failed to establish IMAP connection');
}
// Open the mailbox
await imap.mailboxOpen(baseFolder);
// Calculate message range for pagination
const totalMessages = await imap.status(baseFolder, { messages: true });
const total = totalMessages.messages || 0;
const start = Math.max(1, total - (page * perPage) + 1);
const end = Math.max(1, total - ((page - 1) * perPage));
// Fetch messages
const messages = await imap.fetch(`${start}:${end}`, {
envelope: true,
flags: true,
bodyStructure: true,
uid: true
});
const emails: EmailMessage[] = []; const emails: EmailMessage[] = [];
if (totalEmails > 0) { for await (const message of messages) {
// Fetch messages in the specified range const email: EmailMessage = {
const messages = await client.fetch(`${start}:${end}`, { id: message.uid.toString(),
envelope: true, from: (message.envelope?.from || []).map(addr => ({
flags: true, name: addr.name || '',
bodyStructure: true, address: addr.address || ''
size: true, })),
bodyParts: ['HEADER'] to: (message.envelope?.to || []).map(addr => ({
}); name: addr.name || '',
address: addr.address || ''
for await (const message of messages) { })),
const email: EmailMessage = { subject: message.envelope?.subject || '',
id: message.uid.toString(), date: message.envelope?.date || new Date(),
from: (message.envelope?.from || []).map(addr => ({ flags: {
name: addr.name || '', seen: message.flags.has('\\Seen'),
address: addr.address || '' answered: message.flags.has('\\Answered'),
})), flagged: message.flags.has('\\Flagged'),
to: (message.envelope?.to || []).map(addr => ({ draft: message.flags.has('\\Draft'),
name: addr.name || '', deleted: message.flags.has('\\Deleted')
address: addr.address || '' },
})), size: message.size || 0,
subject: message.envelope?.subject || '', hasAttachments: message.bodyStructure?.childNodes?.some(node => node.disposition === 'attachment') || false,
date: message.internalDate || new Date(), folder: folder,
flags: { contentFetched: false,
seen: message.flags.has('\\Seen'), accountId: accountId || '',
answered: message.flags.has('\\Answered'), content: '',
flagged: message.flags.has('\\Flagged'), messageId: message.envelope?.messageId || undefined
draft: message.flags.has('\\Draft'), };
deleted: message.flags.has('\\Deleted') emails.push(email);
},
size: message.size || 0,
hasAttachments: message.bodyStructure?.childNodes?.some(node => node.disposition === 'attachment') || false,
folder: folder, // Use the original folder name with account ID
contentFetched: false,
accountId: accountId,
content: '',
messageId: message.envelope?.messageId || undefined
};
emails.push(email);
}
} }
const result: EmailListResult = { // Cache the result
emails, if (accountId) {
totalEmails, await cacheEmailList(userId, accountId, folder, page, perPage, emails);
page, }
perPage,
totalPages,
folder,
mailboxes: await getMailboxes(client, accountId) // Pass accountId to getMailboxes
};
// Cache the result with the account-specific folder return emails;
await cacheEmailList(userId, accountId || 'default', folder, page, perPage, result);
return result;
} catch (error) { } catch (error) {
console.error(`Error fetching emails for folder ${baseFolder}:`, error); console.error(`Error fetching emails for folder ${folder}:`, error);
throw error; throw error;
} finally {
try {
await client.mailboxClose();
} catch (error) {
console.error('Error closing mailbox:', error);
}
} }
} }
@ -594,7 +564,7 @@ export async function getMailboxes(client: ImapFlow, accountId?: string): Promis
try { try {
const mailboxes = await client.list(); const mailboxes = await client.list();
// Map special folders to standard names, but keep account-specific paths // Map special folders to standard names
const specialFolders = new Map<string, string>([ const specialFolders = new Map<string, string>([
['Sent Messages', 'Sent'], ['Sent Messages', 'Sent'],
['Sent Items', 'Sent'], ['Sent Items', 'Sent'],
@ -604,31 +574,31 @@ export async function getMailboxes(client: ImapFlow, accountId?: string): Promis
['Spam', 'Junk'] ['Spam', 'Junk']
]); ]);
// Process mailboxes and map special folders while preserving account-specific paths // Process mailboxes and map special folders
const processedMailboxes = mailboxes.map(mailbox => { const processedMailboxes = mailboxes.map(mailbox => {
const path = mailbox.path; const path = mailbox.path;
// Check if this is a special folder // Check if this is a special folder
for (const [special, standard] of specialFolders) { for (const [special, standard] of specialFolders) {
if (path.includes(special)) { if (path.includes(special)) {
// Include accountId in the folder name to make it unique per account return standard;
return accountId ? `${standard}-${accountId}` : standard;
} }
} }
// Include accountId in the folder name to make it unique per account return path;
return accountId ? `${path}-${accountId}` : path;
}); });
// Ensure we have all standard folders with account-specific names // Ensure we have all standard folders
const standardFolders = accountId const standardFolders = ['INBOX', 'Sent', 'Drafts', 'Trash', 'Junk'];
? ['INBOX', 'Sent', 'Drafts', 'Trash', 'Junk'].map(f => `${f}-${accountId}`)
: ['INBOX', 'Sent', 'Drafts', 'Trash', 'Junk'];
// Combine standard folders with custom folders
const uniqueFolders = new Set([...standardFolders, ...processedMailboxes]); const uniqueFolders = new Set([...standardFolders, ...processedMailboxes]);
return Array.from(uniqueFolders); // If accountId is provided, append it to each folder name
return accountId
? Array.from(uniqueFolders).map(f => `${f}-${accountId}`)
: Array.from(uniqueFolders);
} catch (error) { } catch (error) {
console.error('Error fetching mailboxes:', error); console.error('Error fetching mailboxes:', error);
// Return default folders on error, with account-specific names if accountId is provided // Return default folders on error
const defaultFolders = ['INBOX', 'Sent', 'Drafts', 'Trash', 'Junk']; const defaultFolders = ['INBOX', 'Sent', 'Drafts', 'Trash', 'Junk'];
return accountId return accountId
? defaultFolders.map(f => `${f}-${accountId}`) ? defaultFolders.map(f => `${f}-${accountId}`)