courrier multi account restore compose
This commit is contained in:
parent
b78590727b
commit
542a535ce1
@ -1,15 +1,11 @@
|
|||||||
import { NextResponse } from 'next/server';
|
import { NextResponse } from 'next/server';
|
||||||
import { getServerSession } from 'next-auth';
|
import { getServerSession } from 'next-auth';
|
||||||
import { authOptions } from '@/lib/auth';
|
import { authOptions } from '@/lib/auth';
|
||||||
import { getUserEmailCredentials, getMailboxes } from '@/lib/services/email-service';
|
import { 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 { getRedisClient } from '@/lib/redis';
|
import { getRedisClient } from '@/lib/redis';
|
||||||
import { getImapConnection } from '@/lib/services/email-service';
|
import { getImapConnection } from '@/lib/services/email-service';
|
||||||
import { MailCredentials } from '@prisma/client';
|
import { MailCredentials } from '@prisma/client';
|
||||||
import { redis } from '@/lib/redis';
|
import { prisma } from '@/lib/prisma';
|
||||||
|
|
||||||
// Keep track of last prefetch time for each user
|
// Keep track of last prefetch time for each user
|
||||||
const lastPrefetchMap = new Map<string, number>();
|
const lastPrefetchMap = new Map<string, number>();
|
||||||
@ -18,63 +14,9 @@ const PREFETCH_COOLDOWN_MS = 30000; // 30 seconds cooldown between prefetches
|
|||||||
// Cache TTL for folders in Redis (5 minutes)
|
// Cache TTL for folders in Redis (5 minutes)
|
||||||
const FOLDERS_CACHE_TTL = 3600; // 1 hour
|
const FOLDERS_CACHE_TTL = 3600; // 1 hour
|
||||||
|
|
||||||
// Cache to store account folders to avoid repeated calls to the IMAP server
|
|
||||||
// const accountFoldersCache = new Map<string, string[]>();
|
|
||||||
|
|
||||||
// Redis key for folders cache
|
// Redis key for folders cache
|
||||||
const FOLDERS_CACHE_KEY = (userId: string, accountId: string) => `email:folders:${userId}:${accountId}`;
|
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<string[]> {
|
|
||||||
// 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
|
* 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
|
* 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
|
// Get all accounts for the user
|
||||||
const accounts: MailCredentials[] = user.mailCredentials || [];
|
const accounts = (user.mailCredentials || []) as MailCredentials[];
|
||||||
if (accounts.length === 0) {
|
if (accounts.length === 0) {
|
||||||
return NextResponse.json({ error: 'No email accounts found' }, { status: 404 });
|
return NextResponse.json({ error: 'No email accounts found' }, { status: 404 });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch folders for each account
|
// Fetch folders for each account
|
||||||
const accountsWithFolders = await Promise.all(
|
const accountsWithFolders = await Promise.all(
|
||||||
accounts.map(async (account) => {
|
accounts.map(async (account: MailCredentials) => {
|
||||||
const cacheKey = FOLDERS_CACHE_KEY(user.id, account.id);
|
const cacheKey = FOLDERS_CACHE_KEY(user.id, account.id);
|
||||||
// Try to get folders from Redis cache first
|
// Try to get folders from Redis cache first
|
||||||
const cachedFolders = await redis.get(cacheKey);
|
const cachedFolders = await redis.get(cacheKey);
|
||||||
@ -122,7 +64,7 @@ export async function GET() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If not in cache, fetch from IMAP
|
// If not in cache, fetch from IMAP
|
||||||
const client = await getImapConnection(account);
|
const client = await getImapConnection(user.id, account.id);
|
||||||
if (!client) {
|
if (!client) {
|
||||||
return {
|
return {
|
||||||
...account,
|
...account,
|
||||||
|
|||||||
@ -66,7 +66,7 @@ export async function getImapConnection(
|
|||||||
// If not in cache, get from database and cache them
|
// If not in cache, get from database and cache them
|
||||||
if (!credentials) {
|
if (!credentials) {
|
||||||
console.log(`Credentials not found in cache for ${userId}${accountId ? ` account ${accountId}` : ''}, attempting database lookup`);
|
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) {
|
if (!credentials) {
|
||||||
throw new Error('No email credentials found');
|
throw new Error('No email credentials found');
|
||||||
}
|
}
|
||||||
@ -154,24 +154,37 @@ export async function getImapConnection(
|
|||||||
/**
|
/**
|
||||||
* Get user's email credentials from database
|
* Get user's email credentials from database
|
||||||
*/
|
*/
|
||||||
export async function getUserEmailCredentials(userId: string): Promise<EmailCredentials | null> {
|
export async function getUserEmailCredentials(userId: string, accountId?: string): Promise<EmailCredentials | null> {
|
||||||
const credentials = await prisma.mailCredentials.findFirst({
|
const credentials = await prisma.mailCredentials.findFirst({
|
||||||
where: { userId }
|
where: accountId ? { userId, id: accountId } : { userId }
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!credentials) return null;
|
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 {
|
return {
|
||||||
email: credentials.email,
|
email: mailCredentials.email,
|
||||||
password: credentials.password,
|
password: mailCredentials.password,
|
||||||
host: credentials.host,
|
host: mailCredentials.host,
|
||||||
port: credentials.port,
|
port: mailCredentials.port,
|
||||||
secure: credentials.secure,
|
secure: mailCredentials.secure,
|
||||||
smtp_host: credentials.smtp_host,
|
smtp_host: mailCredentials.smtp_host || undefined,
|
||||||
smtp_port: credentials.smtp_port,
|
smtp_port: mailCredentials.smtp_port || undefined,
|
||||||
smtp_secure: credentials.smtp_secure,
|
smtp_secure: mailCredentials.smtp_secure || false,
|
||||||
display_name: credentials.display_name,
|
display_name: mailCredentials.display_name || undefined,
|
||||||
color: credentials.color
|
color: mailCredentials.color || undefined
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -235,7 +248,7 @@ export async function getEmails(
|
|||||||
// Try to get from cache first
|
// Try to get from cache first
|
||||||
if (!searchQuery) {
|
if (!searchQuery) {
|
||||||
const cacheKey = accountId ? `${userId}:${accountId}:${folder}` : `${userId}:${folder}`;
|
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) {
|
if (cachedResult) {
|
||||||
console.log(`Using cached email list for ${cacheKey}:${page}:${perPage}`);
|
console.log(`Using cached email list for ${cacheKey}:${page}:${perPage}`);
|
||||||
return cachedResult;
|
return cachedResult;
|
||||||
@ -331,7 +344,7 @@ export async function getEmails(
|
|||||||
|
|
||||||
// Cache even empty results
|
// Cache even empty results
|
||||||
if (!searchQuery) {
|
if (!searchQuery) {
|
||||||
await cacheEmailList(userId, folder, page, perPage, result);
|
await cacheEmailList(userId, accountId || 'default', folder, page, perPage, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 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
|
// Always cache the result if it's not a search query, even for pagination
|
||||||
if (!searchQuery) {
|
if (!searchQuery) {
|
||||||
console.log(`Caching email list for ${userId}:${folder}:${page}:${perPage}`);
|
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;
|
return result;
|
||||||
@ -501,7 +514,7 @@ export async function getEmailContent(
|
|||||||
folder: string = 'INBOX'
|
folder: string = 'INBOX'
|
||||||
): Promise<EmailMessage> {
|
): Promise<EmailMessage> {
|
||||||
// Try to get from cache first
|
// Try to get from cache first
|
||||||
const cachedEmail = await getCachedEmailContent(userId, emailId);
|
const cachedEmail = await getCachedEmailContent(userId, folder, emailId);
|
||||||
if (cachedEmail) {
|
if (cachedEmail) {
|
||||||
console.log(`Using cached email content for ${userId}:${emailId}`);
|
console.log(`Using cached email content for ${userId}:${emailId}`);
|
||||||
return cachedEmail;
|
return cachedEmail;
|
||||||
@ -582,7 +595,7 @@ export async function getEmailContent(
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Cache the email content
|
// Cache the email content
|
||||||
await cacheEmailContent(userId, emailId, email);
|
await cacheEmailContent(userId, folder, emailId, email);
|
||||||
|
|
||||||
return email;
|
return email;
|
||||||
} finally {
|
} finally {
|
||||||
@ -615,10 +628,10 @@ export async function markEmailReadStatus(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Invalidate content cache since the flags changed
|
// 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
|
// Also invalidate folder cache because unread counts may have changed
|
||||||
await invalidateFolderCache(userId, folder);
|
await invalidateFolderCache(userId, folder, folder);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user