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[];
}
// Connection pool to reuse IMAP clients
// Connection pool management
const connectionPool: Record<string, { client: ImapFlow; lastUsed: number }> = {};
const CONNECTION_TIMEOUT = 5 * 60 * 1000; // 5 minutes
// Clean up idle connections periodically
setInterval(() => {
const now = Date.now();
Object.entries(connectionPool).forEach(([key, { client, lastUsed }]) => {
if (now - lastUsed > CONNECTION_TIMEOUT) {
console.log(`Closing idle IMAP connection for ${key}`);
client.logout().catch(err => {
console.error(`Error closing connection for ${key}:`, err);
});
if (client && client.usable) {
client.logout().catch(err => {
console.error(`Error closing connection for ${key}:`, err);
});
}
delete connectionPool[key];
}
});
}, 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(
userId: string,
accountId?: string
): Promise<ImapFlow> {
console.log(`Getting IMAP connection for user ${userId}${accountId ? ` account ${accountId}` : ''}`);
export async function getImapConnection(userId: string, accountId?: string): Promise<ImapFlow> {
const key = `${userId}:${accountId || 'default'}`;
// First try to get credentials from Redis cache
let credentials = accountId
? await getCachedEmailCredentials(userId, accountId)
: await getCachedEmailCredentials(userId, 'default');
// Check if we have a valid connection in the pool
const existingConnection = connectionPool[key];
if (existingConnection && existingConnection.client.usable) {
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) {
console.log(`Credentials not found in cache for ${userId}${accountId ? ` account ${accountId}` : ''}, attempting database lookup`);
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);
throw new Error('No email credentials found');
}
// 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
const client = new ImapFlow({
host: credentials.host,
@ -125,30 +76,18 @@ export async function getImapConnection(
secure: true,
auth: {
user: credentials.email,
pass: credentials.password,
pass: credentials.password
},
logger: false,
emitLogs: false,
tls: {
rejectUnauthorized: false
}
logger: false
});
try {
await client.connect();
console.log(`Successfully connected to IMAP server for ${connectionKey}`);
// Store in connection pool
connectionPool[connectionKey] = {
client,
lastUsed: Date.now()
};
connectionPool[key] = { client, lastUsed: Date.now() };
return client;
} catch (error: unknown) {
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
console.error(`IMAP connection error for ${connectionKey}:`, errorMessage);
throw new Error(`Failed to connect to IMAP server: ${errorMessage}`);
} catch (error) {
console.error('Error connecting to IMAP:', error);
throw error;
}
}
@ -281,7 +220,7 @@ export async function getEmails(
// Get IMAP connection
client = await getImapConnection(userId, accountId);
if (!client) {
if (!client || !client.usable) {
throw new Error('Failed to establish IMAP connection');
}
@ -381,7 +320,7 @@ export async function getEmails(
console.error('Error fetching emails:', error);
throw error;
} finally {
if (client) {
if (client && client.usable) {
try {
await client.mailboxClose();
} catch (error) {
@ -472,9 +411,10 @@ export async function getEmailContent(
contentType: att.contentType,
size: att.size || 0
})),
html: rawHtml,
text: parsedEmail.text || undefined,
content: rawHtml || parsedEmail.text || '',
content: {
text: parsedEmail.text || '',
html: rawHtml || ''
},
folder,
contentFetched: true,
size: size || 0