courrier multi account restore compose

This commit is contained in:
alma 2025-04-28 16:36:09 +02:00
parent 5152f9ded0
commit 7fc8e83330

View File

@ -30,94 +30,45 @@ export interface EmailListResult {
mailboxes: string[]; mailboxes: string[];
} }
// Connection pool to reuse IMAP clients // Connection pool management
const connectionPool: Record<string, { client: ImapFlow; lastUsed: number }> = {}; const connectionPool: Record<string, { client: ImapFlow; lastUsed: number }> = {};
const CONNECTION_TIMEOUT = 5 * 60 * 1000; // 5 minutes const CONNECTION_TIMEOUT = 5 * 60 * 1000; // 5 minutes
// Clean up idle connections periodically // Clean up idle connections periodically
setInterval(() => { setInterval(() => {
const now = Date.now(); const now = Date.now();
Object.entries(connectionPool).forEach(([key, { client, lastUsed }]) => { Object.entries(connectionPool).forEach(([key, { client, lastUsed }]) => {
if (now - lastUsed > CONNECTION_TIMEOUT) { if (now - lastUsed > CONNECTION_TIMEOUT) {
console.log(`Closing idle IMAP connection for ${key}`); console.log(`Closing idle IMAP connection for ${key}`);
client.logout().catch(err => { if (client && client.usable) {
console.error(`Error closing connection for ${key}:`, err); client.logout().catch(err => {
}); console.error(`Error closing connection for ${key}:`, err);
});
}
delete connectionPool[key]; delete connectionPool[key];
} }
}); });
}, 60 * 1000); // Check every minute }, 60 * 1000); // Check every minute
/** /**
* Get IMAP connection for a user, reusing existing connections when possible * Get IMAP connection for a user
*/ */
export async function getImapConnection( export async function getImapConnection(userId: string, accountId?: string): Promise<ImapFlow> {
userId: string, const key = `${userId}:${accountId || 'default'}`;
accountId?: string
): Promise<ImapFlow> {
console.log(`Getting IMAP connection for user ${userId}${accountId ? ` account ${accountId}` : ''}`);
// First try to get credentials from Redis cache // Check if we have a valid connection in the pool
let credentials = accountId const existingConnection = connectionPool[key];
? await getCachedEmailCredentials(userId, accountId) if (existingConnection && existingConnection.client.usable) {
: await getCachedEmailCredentials(userId, 'default'); existingConnection.lastUsed = Date.now();
return existingConnection.client;
}
// If not in cache, get from database and cache them // Get credentials from cache or database
const credentials = await getCachedEmailCredentials(userId, accountId || 'default');
if (!credentials) { if (!credentials) {
console.log(`Credentials not found in cache for ${userId}${accountId ? ` account ${accountId}` : ''}, attempting database lookup`); throw new Error('No email credentials found');
credentials = await getUserEmailCredentials(userId, accountId);
if (!credentials) {
throw new Error('No email credentials found');
}
// Cache credentials for future use
await cacheEmailCredentials(userId, accountId || 'default', credentials);
} }
// Validate credentials
if (!credentials.password) {
console.error(`Missing password in credentials for user ${userId}${accountId ? ` account ${accountId}` : ''}`);
throw new Error('No password configured');
}
if (!credentials.email || !credentials.host) {
console.error(`Incomplete credentials for user ${userId}${accountId ? ` account ${accountId}` : ''}`);
throw new Error('Invalid email credentials configuration');
}
// Use accountId in connection key to ensure different accounts get different connections
const connectionKey = `${userId}:${accountId || 'default'}`;
const existingConnection = connectionPool[connectionKey];
// Try to get session data from Redis
const sessionData = await getCachedImapSession(userId);
// Return existing connection if available and connected
if (existingConnection) {
try {
if (existingConnection.client.usable) {
existingConnection.lastUsed = Date.now();
console.log(`Reusing existing IMAP connection for ${connectionKey}`);
// Update session data in Redis
if (sessionData) {
await cacheImapSession(userId, {
...sessionData,
lastActive: Date.now()
});
}
return existingConnection.client;
}
} catch (error) {
console.warn(`Existing connection for ${connectionKey} is not usable, creating new connection`);
// Will create a new connection below
}
}
console.log(`Creating new IMAP connection for ${connectionKey}`);
// Create new connection // Create new connection
const client = new ImapFlow({ const client = new ImapFlow({
host: credentials.host, host: credentials.host,
@ -125,30 +76,18 @@ export async function getImapConnection(
secure: true, secure: true,
auth: { auth: {
user: credentials.email, user: credentials.email,
pass: credentials.password, pass: credentials.password
}, },
logger: false, logger: false
emitLogs: false,
tls: {
rejectUnauthorized: false
}
}); });
try { try {
await client.connect(); await client.connect();
console.log(`Successfully connected to IMAP server for ${connectionKey}`); connectionPool[key] = { client, lastUsed: Date.now() };
// Store in connection pool
connectionPool[connectionKey] = {
client,
lastUsed: Date.now()
};
return client; return client;
} catch (error: unknown) { } catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Unknown error'; console.error('Error connecting to IMAP:', error);
console.error(`IMAP connection error for ${connectionKey}:`, errorMessage); throw error;
throw new Error(`Failed to connect to IMAP server: ${errorMessage}`);
} }
} }
@ -281,7 +220,7 @@ export async function getEmails(
// Get IMAP connection // Get IMAP connection
client = await getImapConnection(userId, accountId); client = await getImapConnection(userId, accountId);
if (!client) { if (!client || !client.usable) {
throw new Error('Failed to establish IMAP connection'); throw new Error('Failed to establish IMAP connection');
} }
@ -381,7 +320,7 @@ export async function getEmails(
console.error('Error fetching emails:', error); console.error('Error fetching emails:', error);
throw error; throw error;
} finally { } finally {
if (client) { if (client && client.usable) {
try { try {
await client.mailboxClose(); await client.mailboxClose();
} catch (error) { } catch (error) {
@ -472,9 +411,10 @@ export async function getEmailContent(
contentType: att.contentType, contentType: att.contentType,
size: att.size || 0 size: att.size || 0
})), })),
html: rawHtml, content: {
text: parsedEmail.text || undefined, text: parsedEmail.text || '',
content: rawHtml || parsedEmail.text || '', html: rawHtml || ''
},
folder, folder,
contentFetched: true, contentFetched: true,
size: size || 0 size: size || 0