From 4899bd093b7d59217f38db5c9ee428968db773f1 Mon Sep 17 00:00:00 2001 From: alma Date: Sun, 27 Apr 2025 13:55:33 +0200 Subject: [PATCH] courrier redis login --- lib/redis.ts | 95 ++++++++++++++++++++++++++--------- lib/services/email-service.ts | 19 +++++++ 2 files changed, 91 insertions(+), 23 deletions(-) diff --git a/lib/redis.ts b/lib/redis.ts index 9bd659d2..02a5882b 100644 --- a/lib/redis.ts +++ b/lib/redis.ts @@ -114,20 +114,44 @@ export async function cacheEmailCredentials( const redis = getRedisClient(); const key = KEYS.CREDENTIALS(userId); - // Create a copy without the password to store - const secureCredentials: EmailCredentials = { - email: credentials.email, - host: credentials.host, - port: credentials.port, - secure: credentials.secure ?? true - }; - - // Encrypt password separately if it exists - if (credentials.password) { - secureCredentials.encryptedPassword = encryptData(credentials.password); + // Validate credentials before caching + if (!credentials.email || !credentials.host || !credentials.password) { + console.error(`Cannot cache incomplete credentials for user ${userId}`); + return; } - await redis.set(key, JSON.stringify(secureCredentials), 'EX', TTL.CREDENTIALS); + try { + console.log(`Caching credentials for user ${userId}`); + + // Create a copy without the password to store + const secureCredentials: EmailCredentials = { + email: credentials.email, + host: credentials.host, + port: credentials.port, + secure: credentials.secure ?? true + }; + + // Encrypt password + if (credentials.password) { + try { + const encrypted = encryptData(credentials.password); + console.log(`Successfully encrypted password for user ${userId}`); + secureCredentials.encryptedPassword = encrypted; + } catch (encryptError) { + console.error(`Failed to encrypt password for user ${userId}:`, encryptError); + // Don't proceed with caching if encryption fails + return; + } + } else { + console.warn(`No password provided for user ${userId}, skipping credential caching`); + return; + } + + await redis.set(key, JSON.stringify(secureCredentials), 'EX', TTL.CREDENTIALS); + console.log(`Credentials cached for user ${userId}`); + } catch (error) { + console.error(`Error caching credentials for user ${userId}:`, error); + } } /** @@ -139,18 +163,43 @@ export async function getCachedEmailCredentials( const redis = getRedisClient(); const key = KEYS.CREDENTIALS(userId); - const cachedData = await redis.get(key); - if (!cachedData) return null; - - const credentials = JSON.parse(cachedData) as EmailCredentials; - - // Decrypt password if it was encrypted - if (credentials.encryptedPassword) { - credentials.password = decryptData(credentials.encryptedPassword); - delete credentials.encryptedPassword; + try { + const cachedData = await redis.get(key); + if (!cachedData) { + console.log(`No cached credentials found for user ${userId}`); + return null; + } + + console.log(`Found cached credentials for user ${userId}, attempting to decrypt`); + const credentials = JSON.parse(cachedData) as EmailCredentials; + + // Check if we have encrypted password + if (!credentials.encryptedPassword) { + console.warn(`Cached credentials for user ${userId} missing encrypted password`); + return null; + } + + try { + // Decrypt password with error handling + credentials.password = decryptData(credentials.encryptedPassword); + delete credentials.encryptedPassword; + + // Validate the credentials to ensure they're complete + if (!credentials.password || !credentials.email || !credentials.host) { + console.warn(`Incomplete credentials in cache for user ${userId}, missing required fields`); + return null; + } + + console.log(`Successfully retrieved and decrypted credentials for ${userId}`); + return credentials; + } catch (decryptError) { + console.error(`Failed to decrypt password for user ${userId}:`, decryptError); + return null; + } + } catch (error) { + console.error(`Error retrieving credentials from Redis for user ${userId}:`, error); + return null; } - - return credentials; } /** diff --git a/lib/services/email-service.ts b/lib/services/email-service.ts index f6c66cc5..318b01d2 100644 --- a/lib/services/email-service.ts +++ b/lib/services/email-service.ts @@ -100,11 +100,14 @@ setInterval(() => { * Get IMAP connection for a user, reusing existing connections when possible */ export async function getImapConnection(userId: string): Promise { + console.log(`Getting IMAP connection for user ${userId}`); + // First try to get credentials from Redis cache let credentials = await getCachedEmailCredentials(userId); // If not in cache, get from database and cache them if (!credentials) { + console.log(`Credentials not found in cache for ${userId}, attempting database lookup`); credentials = await getUserEmailCredentials(userId); if (!credentials) { throw new Error('No email credentials found'); @@ -114,6 +117,17 @@ export async function getImapConnection(userId: string): Promise { await cacheEmailCredentials(userId, credentials); } + // Validate credentials + if (!credentials.password) { + console.error(`Missing password in credentials for user ${userId}`); + throw new Error('No password configured'); + } + + if (!credentials.email || !credentials.host) { + console.error(`Incomplete credentials for user ${userId}`); + throw new Error('Invalid email credentials configuration'); + } + const connectionKey = `${userId}:${credentials.email}`; const existingConnection = connectionPool[connectionKey]; @@ -125,6 +139,7 @@ export async function getImapConnection(userId: string): Promise { try { if (existingConnection.client.usable) { existingConnection.lastUsed = Date.now(); + console.log(`Reusing existing IMAP connection for ${connectionKey}`); // Update session data in Redis if (sessionData) { @@ -142,6 +157,8 @@ export async function getImapConnection(userId: string): Promise { } } + console.log(`Creating new IMAP connection for ${connectionKey}`); + // Create new connection const client = new ImapFlow({ host: credentials.host, @@ -160,6 +177,7 @@ export async function getImapConnection(userId: string): Promise { try { await client.connect(); + console.log(`Successfully connected to IMAP server for ${connectionKey}`); // Store in connection pool connectionPool[connectionKey] = { @@ -170,6 +188,7 @@ export async function getImapConnection(userId: string): Promise { 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}`); } }