courrier multi account restore compose
This commit is contained in:
parent
a9012dec69
commit
346b766b7f
@ -41,7 +41,7 @@ export async function GET(request: Request) {
|
||||
const folderAccountId = folder.includes(':') ? folder.split(':')[0] : accountId;
|
||||
|
||||
// Use the most specific account ID available
|
||||
const effectiveAccountId = folderAccountId || accountId || 'default';
|
||||
let effectiveAccountId = folderAccountId || accountId || 'default';
|
||||
|
||||
// Normalize folder name by removing account prefix if present
|
||||
const normalizedFolder = folder.includes(':') ? folder.split(':')[1] : folder;
|
||||
|
||||
@ -190,8 +190,14 @@ export default function EmailSidebar({
|
||||
variant="ghost"
|
||||
className={`w-full justify-start text-xs py-1 h-7 ${isSelected ? 'bg-gray-100' : ''}`}
|
||||
onClick={() => {
|
||||
console.log(`Folder button clicked: ${prefixedFolder}`);
|
||||
onFolderChange(prefixedFolder, accountId);
|
||||
console.log(`Folder button clicked: folder=${prefixedFolder}, accountId=${accountId}, normalized=${baseFolderName}`);
|
||||
|
||||
// Always ensure the folder name includes the account ID prefix
|
||||
const fullyPrefixedFolder = folder.includes(':') ? folder : `${accountId}:${folder}`;
|
||||
|
||||
// Make sure we pass the EXACT accountId parameter here, not the folder's extracted account ID
|
||||
console.log(`Calling onFolderChange with folder=${fullyPrefixedFolder}, accountId=${accountId}`);
|
||||
onFolderChange(fullyPrefixedFolder, accountId);
|
||||
}}
|
||||
>
|
||||
<div className="flex items-center w-full">
|
||||
|
||||
@ -65,10 +65,28 @@ export async function getImapConnection(
|
||||
): Promise<ImapFlow> {
|
||||
console.log(`Getting IMAP connection for user ${userId}${accountId ? ` account ${accountId}` : ''}`);
|
||||
|
||||
// Special handling for 'default' accountId - find the first available account
|
||||
if (!accountId || accountId === 'default') {
|
||||
console.log(`No specific account provided or 'default' requested, trying to find first account for user ${userId}`);
|
||||
|
||||
// Query to find all accounts for this user
|
||||
const accounts = await prisma.mailCredentials.findMany({
|
||||
where: { userId },
|
||||
orderBy: { createdAt: 'asc' },
|
||||
take: 1
|
||||
});
|
||||
|
||||
if (accounts && accounts.length > 0) {
|
||||
const firstAccount = accounts[0];
|
||||
console.log(`Using first available account: ${firstAccount.id} (${firstAccount.email})`);
|
||||
accountId = firstAccount.id;
|
||||
} else {
|
||||
throw new Error('No email accounts configured for this user');
|
||||
}
|
||||
}
|
||||
|
||||
// First try to get credentials from Redis cache
|
||||
let credentials = accountId
|
||||
? await getCachedEmailCredentials(userId, accountId)
|
||||
: await getCachedEmailCredentials(userId, 'default');
|
||||
let credentials = await getCachedEmailCredentials(userId, accountId);
|
||||
|
||||
// If not in cache, get from database and cache them
|
||||
if (!credentials) {
|
||||
@ -79,7 +97,7 @@ export async function getImapConnection(
|
||||
}
|
||||
|
||||
// Cache credentials for future use
|
||||
await cacheEmailCredentials(userId, accountId || 'default', credentials);
|
||||
await cacheEmailCredentials(userId, accountId, credentials);
|
||||
}
|
||||
|
||||
// Validate credentials
|
||||
@ -94,7 +112,7 @@ export async function getImapConnection(
|
||||
}
|
||||
|
||||
// Use accountId in connection key to ensure different accounts get different connections
|
||||
const connectionKey = `${userId}:${accountId || 'default'}`;
|
||||
const connectionKey = `${userId}:${accountId}`;
|
||||
const existingConnection = connectionPool[connectionKey];
|
||||
|
||||
// Try to get session data from Redis
|
||||
@ -279,127 +297,134 @@ export async function getEmails(
|
||||
perPage: number = 20,
|
||||
accountId?: string
|
||||
): Promise<EmailListResult> {
|
||||
let client: ImapFlow | undefined;
|
||||
// Normalize folder name and handle account ID
|
||||
console.log(`[getEmails] Processing request for folder: ${folder}, normalized to ${folder}, account: ${accountId || 'default'}`);
|
||||
|
||||
try {
|
||||
// Extract account ID from folder name if present and none was explicitly provided
|
||||
const folderAccountId = folder.includes(':') ? folder.split(':')[0] : accountId;
|
||||
// The getImapConnection function already handles 'default' accountId by finding the first available account
|
||||
const client = await getImapConnection(userId, accountId);
|
||||
|
||||
// Use the most specific account ID available
|
||||
const effectiveAccountId = folderAccountId || accountId || 'default';
|
||||
// At this point, accountId has been resolved to an actual account ID by getImapConnection
|
||||
// Store the resolved accountId in a variable that is guaranteed to be a string
|
||||
const resolvedAccountId = accountId || 'default';
|
||||
|
||||
// Normalize folder name by removing account prefix if present
|
||||
const normalizedFolder = folder.includes(':') ? folder.split(':')[1] : folder;
|
||||
|
||||
console.log(`[getEmails] Processing request for folder: ${folder}, normalized to ${normalizedFolder}, account: ${effectiveAccountId}`);
|
||||
// Attempt to select the mailbox
|
||||
try {
|
||||
const mailboxInfo = await client.mailboxOpen(folder);
|
||||
console.log(`Opened mailbox ${folder} with ${mailboxInfo.exists} messages`);
|
||||
|
||||
// Get list of all mailboxes for UI
|
||||
const mailboxes = await getMailboxes(client, resolvedAccountId);
|
||||
|
||||
// Calculate pagination
|
||||
const totalEmails = mailboxInfo.exists || 0;
|
||||
const totalPages = Math.ceil(totalEmails / perPage);
|
||||
|
||||
// Check if mailbox is empty
|
||||
if (totalEmails === 0) {
|
||||
// Cache the empty result
|
||||
const emptyResult = {
|
||||
emails: [],
|
||||
totalEmails: 0,
|
||||
page,
|
||||
perPage,
|
||||
totalPages: 0,
|
||||
folder,
|
||||
mailboxes
|
||||
};
|
||||
|
||||
await cacheEmailList(
|
||||
userId,
|
||||
resolvedAccountId, // Use the guaranteed string account ID
|
||||
folder,
|
||||
page,
|
||||
perPage,
|
||||
emptyResult
|
||||
);
|
||||
|
||||
return emptyResult;
|
||||
}
|
||||
|
||||
// Get IMAP connection using the effective account ID
|
||||
client = await getImapConnection(userId, effectiveAccountId);
|
||||
if (!client) {
|
||||
throw new Error('Failed to establish IMAP connection');
|
||||
}
|
||||
// Calculate message range for pagination
|
||||
const start = Math.max(1, totalEmails - (page * perPage) + 1);
|
||||
const end = Math.max(1, totalEmails - ((page - 1) * perPage));
|
||||
console.log(`Fetching messages ${start}:${end} from ${folder} for account ${resolvedAccountId}`);
|
||||
|
||||
// Open mailbox with the normalized folder name
|
||||
await client.mailboxOpen(normalizedFolder);
|
||||
const mailbox = client.mailbox;
|
||||
if (!mailbox || typeof mailbox === 'boolean') {
|
||||
throw new Error(`Failed to open mailbox: ${normalizedFolder}`);
|
||||
}
|
||||
// Fetch messages
|
||||
const messages = await client.fetch(`${start}:${end}`, {
|
||||
envelope: true,
|
||||
flags: true,
|
||||
bodyStructure: true
|
||||
});
|
||||
|
||||
// Get total messages
|
||||
const total = mailbox.exists || 0;
|
||||
console.log(`Total messages in ${normalizedFolder} for account ${effectiveAccountId}: ${total}`);
|
||||
const emails: EmailMessage[] = [];
|
||||
for await (const message of messages) {
|
||||
const email: EmailMessage = {
|
||||
id: message.uid.toString(),
|
||||
from: message.envelope.from?.map(addr => ({
|
||||
name: addr.name || '',
|
||||
address: addr.address || ''
|
||||
})) || [],
|
||||
to: message.envelope.to?.map(addr => ({
|
||||
name: addr.name || '',
|
||||
address: addr.address || ''
|
||||
})) || [],
|
||||
subject: message.envelope.subject || '',
|
||||
date: message.envelope.date || new Date(),
|
||||
flags: {
|
||||
seen: message.flags.has('\\Seen'),
|
||||
flagged: message.flags.has('\\Flagged'),
|
||||
answered: message.flags.has('\\Answered'),
|
||||
draft: message.flags.has('\\Draft'),
|
||||
deleted: message.flags.has('\\Deleted')
|
||||
},
|
||||
size: message.size || 0,
|
||||
hasAttachments: message.bodyStructure?.childNodes?.some(node => node.disposition === 'attachment') || false,
|
||||
folder: folder,
|
||||
contentFetched: false,
|
||||
accountId: resolvedAccountId,
|
||||
content: {
|
||||
text: '',
|
||||
html: ''
|
||||
}
|
||||
};
|
||||
emails.push(email);
|
||||
}
|
||||
|
||||
// If no messages, return empty result
|
||||
if (total === 0) {
|
||||
return {
|
||||
emails: [],
|
||||
totalEmails: 0,
|
||||
// Cache the result with the effective account ID
|
||||
await cacheEmailList(
|
||||
userId,
|
||||
resolvedAccountId, // Use the guaranteed string account ID
|
||||
folder,
|
||||
page,
|
||||
perPage,
|
||||
totalPages: 0,
|
||||
folder: normalizedFolder,
|
||||
mailboxes: []
|
||||
};
|
||||
}
|
||||
|
||||
// Calculate message range for pagination
|
||||
const start = Math.max(1, total - (page * perPage) + 1);
|
||||
const end = Math.max(1, total - ((page - 1) * perPage));
|
||||
console.log(`Fetching messages ${start}:${end} from ${normalizedFolder} for account ${effectiveAccountId}`);
|
||||
|
||||
// Fetch messages
|
||||
const messages = await client.fetch(`${start}:${end}`, {
|
||||
envelope: true,
|
||||
flags: true,
|
||||
bodyStructure: true
|
||||
});
|
||||
|
||||
const emails: EmailMessage[] = [];
|
||||
for await (const message of messages) {
|
||||
const email: EmailMessage = {
|
||||
id: message.uid.toString(),
|
||||
from: message.envelope.from?.map(addr => ({
|
||||
name: addr.name || '',
|
||||
address: addr.address || ''
|
||||
})) || [],
|
||||
to: message.envelope.to?.map(addr => ({
|
||||
name: addr.name || '',
|
||||
address: addr.address || ''
|
||||
})) || [],
|
||||
subject: message.envelope.subject || '',
|
||||
date: message.envelope.date || new Date(),
|
||||
flags: {
|
||||
seen: message.flags.has('\\Seen'),
|
||||
flagged: message.flags.has('\\Flagged'),
|
||||
answered: message.flags.has('\\Answered'),
|
||||
draft: message.flags.has('\\Draft'),
|
||||
deleted: message.flags.has('\\Deleted')
|
||||
},
|
||||
size: message.size || 0,
|
||||
hasAttachments: message.bodyStructure?.childNodes?.some(node => node.disposition === 'attachment') || false,
|
||||
folder: normalizedFolder,
|
||||
contentFetched: false,
|
||||
accountId: effectiveAccountId,
|
||||
content: {
|
||||
text: '',
|
||||
html: ''
|
||||
{
|
||||
emails,
|
||||
totalEmails: totalEmails,
|
||||
page,
|
||||
perPage,
|
||||
totalPages: Math.ceil(totalEmails / perPage),
|
||||
folder: folder,
|
||||
mailboxes: mailboxes
|
||||
}
|
||||
);
|
||||
|
||||
return {
|
||||
emails,
|
||||
totalEmails: totalEmails,
|
||||
page,
|
||||
perPage,
|
||||
totalPages: Math.ceil(totalEmails / perPage),
|
||||
folder: folder,
|
||||
mailboxes: mailboxes
|
||||
};
|
||||
emails.push(email);
|
||||
} catch (error) {
|
||||
console.error('Error fetching emails:', error);
|
||||
throw error;
|
||||
}
|
||||
|
||||
// Cache the result with the effective account ID
|
||||
await cacheEmailList(userId, effectiveAccountId, normalizedFolder, page, perPage, {
|
||||
emails,
|
||||
totalEmails: total,
|
||||
page,
|
||||
perPage,
|
||||
totalPages: Math.ceil(total / perPage),
|
||||
folder: normalizedFolder,
|
||||
mailboxes: []
|
||||
});
|
||||
|
||||
return {
|
||||
emails,
|
||||
totalEmails: total,
|
||||
page,
|
||||
perPage,
|
||||
totalPages: Math.ceil(total / perPage),
|
||||
folder: normalizedFolder,
|
||||
mailboxes: []
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Error fetching emails:', error);
|
||||
throw error;
|
||||
} finally {
|
||||
if (client) {
|
||||
try {
|
||||
await client.mailboxClose();
|
||||
} catch (error) {
|
||||
console.error('Error closing mailbox:', error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user