diff --git a/lib/services/email-service.ts b/lib/services/email-service.ts index 539f0d3f..67ddc674 100644 --- a/lib/services/email-service.ts +++ b/lib/services/email-service.ts @@ -32,6 +32,14 @@ interface EmailCredentialsExtended extends EmailCredentials { tokenExpiry?: number; } +// Define the extended MailCredentials type that includes OAuth fields +interface MailCredentialsWithOAuth extends MailCredentials { + use_oauth?: boolean; + access_token?: string | null; + refresh_token?: string | null; + token_expiry?: Date | null; +} + // Types specific to this service export interface EmailListResult { emails: EmailMessage[]; @@ -251,14 +259,54 @@ 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, accountId); - if (!credentials) { + // Fetch directly from database with all fields + const dbCredentials = await prisma.mailCredentials.findFirst({ + where: { + AND: [ + { userId }, + accountId ? { id: accountId } : {} + ] + } + }) as MailCredentialsWithOAuth | null; + + if (!dbCredentials) { console.error(`No credentials found for user ${userId}${accountId ? ` account ${accountId}` : ''}`); totalConnectionErrors++; throw new Error('Email account credentials not found'); } + // Map database fields to our credential format + credentials = { + email: dbCredentials.email, + password: dbCredentials.password, + host: dbCredentials.host, + port: dbCredentials.port, + secure: dbCredentials.secure, + smtp_host: dbCredentials.smtp_host || undefined, + smtp_port: dbCredentials.smtp_port || undefined, + smtp_secure: dbCredentials.smtp_secure ?? false, + display_name: dbCredentials.display_name || undefined, + color: dbCredentials.color || undefined, + + // Map OAuth fields + useOAuth: dbCredentials.use_oauth || false, + accessToken: dbCredentials.access_token || undefined, + refreshToken: dbCredentials.refresh_token || undefined, + tokenExpiry: dbCredentials.token_expiry ? dbCredentials.token_expiry.getTime() : undefined + }; + + // Log credentials (safely) + console.log('Loaded credentials from database:', { + email: credentials.email, + host: credentials.host, + port: credentials.port, + useOAuth: credentials.useOAuth, + hasAccessToken: !!credentials.accessToken, + hasRefreshToken: !!credentials.refreshToken, + tokenExpiry: credentials.tokenExpiry ? new Date(credentials.tokenExpiry).toISOString() : undefined + }); + // Cache the credentials for future use await cacheEmailCredentials(userId, accountId, credentials); } @@ -268,12 +316,19 @@ export async function getImapConnection( // If using OAuth, ensure we have a fresh token if (extendedCreds.useOAuth) { + console.log(`Account is configured to use OAuth: ${extendedCreds.useOAuth}`); + + if (!extendedCreds.accessToken) { + console.error(`OAuth is enabled but no access token for account ${extendedCreds.email}`); + } + try { console.log(`Ensuring fresh token for OAuth account ${extendedCreds.email}`); const { accessToken, success } = await ensureFreshToken(userId, extendedCreds.email); if (success) { extendedCreds.accessToken = accessToken; + console.log(`Successfully refreshed token for ${extendedCreds.email}`); } else { console.error(`Failed to refresh token for ${extendedCreds.email}`); } @@ -489,6 +544,9 @@ export async function saveUserEmailCredentials( throw new Error('No credentials provided'); } + // Cast to extended type to access OAuth properties + const extendedCreds = credentials as EmailCredentialsExtended; + // Extract only the fields that exist in the database schema const dbCredentials = { email: credentials.email, @@ -500,10 +558,23 @@ export async function saveUserEmailCredentials( smtp_port: credentials.smtp_port || null, smtp_secure: credentials.smtp_secure ?? false, display_name: credentials.display_name || null, - color: credentials.color || null + color: credentials.color || null, + // Add OAuth fields if present + use_oauth: extendedCreds.useOAuth ?? false, + access_token: extendedCreds.accessToken || null, + refresh_token: extendedCreds.refreshToken || null, + token_expiry: extendedCreds.tokenExpiry ? new Date(extendedCreds.tokenExpiry) : null }; try { + console.log('Saving credentials with OAuth data:', { + ...dbCredentials, + password: dbCredentials.password ? '***' : null, + access_token: dbCredentials.access_token ? '***' : null, + refresh_token: dbCredentials.refresh_token ? '***' : null, + use_oauth: dbCredentials.use_oauth + }); + // Save to database using the unique constraint on [userId, email] await prisma.mailCredentials.upsert({ where: { @@ -525,7 +596,7 @@ export async function saveUserEmailCredentials( }); // Cache the full credentials object in Redis - await cacheEmailCredentials(userId, accountId, credentials); + await cacheEmailCredentials(userId, accountId, extendedCreds); console.log('Successfully saved and cached credentials for user:', userId); } catch (error) { console.error('Error saving credentials:', error);