From 542a535ce11ad3be06a81b63c04f30adea355df0 Mon Sep 17 00:00:00 2001 From: alma Date: Mon, 28 Apr 2025 13:05:05 +0200 Subject: [PATCH] courrier multi account restore compose --- app/api/courrier/session/route.ts | 68 +++---------------------------- lib/services/email-service.ts | 53 +++++++++++++++--------- 2 files changed, 38 insertions(+), 83 deletions(-) diff --git a/app/api/courrier/session/route.ts b/app/api/courrier/session/route.ts index a7d1686c..97cec398 100644 --- a/app/api/courrier/session/route.ts +++ b/app/api/courrier/session/route.ts @@ -1,15 +1,11 @@ import { NextResponse } from 'next/server'; import { getServerSession } from 'next-auth'; import { authOptions } from '@/lib/auth'; -import { getUserEmailCredentials, getMailboxes } from '@/lib/services/email-service'; -import { prefetchUserEmailData } from '@/lib/services/prefetch-service'; -import { getCachedEmailCredentials, getRedisStatus, warmupRedisCache, getCachedImapSession, cacheImapSession } from '@/lib/redis'; -import { prisma } from '@/lib/prisma'; -import { ImapFlow } from 'imapflow'; +import { getMailboxes } from '@/lib/services/email-service'; import { getRedisClient } from '@/lib/redis'; import { getImapConnection } from '@/lib/services/email-service'; import { MailCredentials } from '@prisma/client'; -import { redis } from '@/lib/redis'; +import { prisma } from '@/lib/prisma'; // Keep track of last prefetch time for each user const lastPrefetchMap = new Map(); @@ -18,63 +14,9 @@ const PREFETCH_COOLDOWN_MS = 30000; // 30 seconds cooldown between prefetches // Cache TTL for folders in Redis (5 minutes) const FOLDERS_CACHE_TTL = 3600; // 1 hour -// Cache to store account folders to avoid repeated calls to the IMAP server -// const accountFoldersCache = new Map(); - // Redis key for folders cache const FOLDERS_CACHE_KEY = (userId: string, accountId: string) => `email:folders:${userId}:${accountId}`; -/** - * Get folders for a specific account - */ -async function getAccountFolders(accountId: string, account: any): Promise { - // Check cache first - const cacheKey = `folders:${accountId}`; - const cachedData = accountFoldersCache.get(cacheKey); - const now = Date.now(); - - if (cachedData && (now - cachedData.timestamp < FOLDERS_CACHE_TTL)) { - return cachedData.folders; - } - - try { - // Connect to IMAP server for this account - const client = new ImapFlow({ - host: account.host, - port: account.port, - secure: true, - auth: { - user: account.email, - pass: account.password, - }, - logger: false, - tls: { - rejectUnauthorized: false - } - }); - - await client.connect(); - - // Get folders for this account - const folders = await getMailboxes(client); - - // Close connection - await client.logout(); - - // Cache the result - accountFoldersCache.set(cacheKey, { - folders, - timestamp: now - }); - - return folders; - } catch (error) { - console.error(`Error fetching folders for account ${account.email}:`, error); - // Return fallback folders on error - return ['INBOX', 'Sent', 'Drafts', 'Trash']; - } -} - /** * This endpoint is called when the app initializes to check if the user has email credentials * and to start prefetching email data in the background if they do @@ -103,14 +45,14 @@ export async function GET() { } // Get all accounts for the user - const accounts: MailCredentials[] = user.mailCredentials || []; + const accounts = (user.mailCredentials || []) as MailCredentials[]; if (accounts.length === 0) { return NextResponse.json({ error: 'No email accounts found' }, { status: 404 }); } // Fetch folders for each account const accountsWithFolders = await Promise.all( - accounts.map(async (account) => { + accounts.map(async (account: MailCredentials) => { const cacheKey = FOLDERS_CACHE_KEY(user.id, account.id); // Try to get folders from Redis cache first const cachedFolders = await redis.get(cacheKey); @@ -122,7 +64,7 @@ export async function GET() { } // If not in cache, fetch from IMAP - const client = await getImapConnection(account); + const client = await getImapConnection(user.id, account.id); if (!client) { return { ...account, diff --git a/lib/services/email-service.ts b/lib/services/email-service.ts index 34394e1c..06772588 100644 --- a/lib/services/email-service.ts +++ b/lib/services/email-service.ts @@ -66,7 +66,7 @@ export async function getImapConnection( // If not in cache, get from database and cache them if (!credentials) { console.log(`Credentials not found in cache for ${userId}${accountId ? ` account ${accountId}` : ''}, attempting database lookup`); - credentials = await getUserEmailCredentials(userId); + credentials = await getUserEmailCredentials(userId, accountId); if (!credentials) { throw new Error('No email credentials found'); } @@ -154,24 +154,37 @@ export async function getImapConnection( /** * Get user's email credentials from database */ -export async function getUserEmailCredentials(userId: string): Promise { +export async function getUserEmailCredentials(userId: string, accountId?: string): Promise { const credentials = await prisma.mailCredentials.findFirst({ - where: { userId } + where: accountId ? { userId, id: accountId } : { userId } }); if (!credentials) return null; + const mailCredentials = credentials as unknown as { + email: string; + password: string; + host: string; + port: number; + secure: boolean; + smtp_host: string | null; + smtp_port: number | null; + smtp_secure: boolean | null; + display_name: string | null; + color: string | null; + }; + return { - email: credentials.email, - password: credentials.password, - host: credentials.host, - port: credentials.port, - secure: credentials.secure, - smtp_host: credentials.smtp_host, - smtp_port: credentials.smtp_port, - smtp_secure: credentials.smtp_secure, - display_name: credentials.display_name, - color: credentials.color + email: mailCredentials.email, + password: mailCredentials.password, + host: mailCredentials.host, + port: mailCredentials.port, + secure: mailCredentials.secure, + smtp_host: mailCredentials.smtp_host || undefined, + smtp_port: mailCredentials.smtp_port || undefined, + smtp_secure: mailCredentials.smtp_secure || false, + display_name: mailCredentials.display_name || undefined, + color: mailCredentials.color || undefined }; } @@ -235,7 +248,7 @@ export async function getEmails( // Try to get from cache first if (!searchQuery) { const cacheKey = accountId ? `${userId}:${accountId}:${folder}` : `${userId}:${folder}`; - const cachedResult = await getCachedEmailList(userId, folder, page, perPage); + const cachedResult = await getCachedEmailList(userId, accountId || 'default', folder, page, perPage); if (cachedResult) { console.log(`Using cached email list for ${cacheKey}:${page}:${perPage}`); return cachedResult; @@ -331,7 +344,7 @@ export async function getEmails( // Cache even empty results if (!searchQuery) { - await cacheEmailList(userId, folder, page, perPage, result); + await cacheEmailList(userId, accountId || 'default', folder, page, perPage, result); } return result; @@ -476,7 +489,7 @@ export async function getEmails( // Always cache the result if it's not a search query, even for pagination if (!searchQuery) { console.log(`Caching email list for ${userId}:${folder}:${page}:${perPage}`); - await cacheEmailList(userId, folder, page, perPage, result); + await cacheEmailList(userId, accountId || 'default', folder, page, perPage, result); } return result; @@ -501,7 +514,7 @@ export async function getEmailContent( folder: string = 'INBOX' ): Promise { // Try to get from cache first - const cachedEmail = await getCachedEmailContent(userId, emailId); + const cachedEmail = await getCachedEmailContent(userId, folder, emailId); if (cachedEmail) { console.log(`Using cached email content for ${userId}:${emailId}`); return cachedEmail; @@ -582,7 +595,7 @@ export async function getEmailContent( }; // Cache the email content - await cacheEmailContent(userId, emailId, email); + await cacheEmailContent(userId, folder, emailId, email); return email; } finally { @@ -615,10 +628,10 @@ export async function markEmailReadStatus( } // Invalidate content cache since the flags changed - await invalidateEmailContentCache(userId, emailId); + await invalidateEmailContentCache(userId, folder, emailId); // Also invalidate folder cache because unread counts may have changed - await invalidateFolderCache(userId, folder); + await invalidateFolderCache(userId, folder, folder); return true; } catch (error) {