diff --git a/lib/services/email-service.ts b/lib/services/email-service.ts index a0e54298..85221d55 100644 --- a/lib/services/email-service.ts +++ b/lib/services/email-service.ts @@ -260,7 +260,7 @@ export async function getImapConnection( if (!credentials) { console.log(`Credentials not found in cache for ${userId}${accountId ? ` account ${accountId}` : ''}, attempting database lookup`); - // Fetch directly from database with all fields + // Fetch directly from database const dbCredentials = await prisma.mailCredentials.findFirst({ where: { AND: [ @@ -268,7 +268,7 @@ export async function getImapConnection( accountId ? { id: accountId } : {} ] } - }) as MailCredentialsWithOAuth | null; + }); if (!dbCredentials) { console.error(`No credentials found for user ${userId}${accountId ? ` account ${accountId}` : ''}`); @@ -276,49 +276,27 @@ export async function getImapConnection( throw new Error('Email account credentials not found'); } - // Map database fields to our credential format - if (dbCredentials) { - 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 - using camelCase field names from the schema - useOAuth: dbCredentials.useOAuth || false, - accessToken: dbCredentials.accessToken || undefined, - refreshToken: dbCredentials.refreshToken || undefined, - tokenExpiry: dbCredentials.tokenExpiry ? dbCredentials.tokenExpiry.getTime() : undefined - } as EmailCredentialsExtended; // Cast to extended type - - // 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); - } + // Create our credentials object from database data + 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 + }; } // Cast to extended type const extendedCreds = credentials as EmailCredentialsExtended; - // If using OAuth, ensure we have a fresh token + // If this account is marked as using OAuth in the cache if (extendedCreds.useOAuth) { - console.log(`Account is configured to use OAuth: ${extendedCreds.useOAuth}`); + console.log(`Account is configured to use OAuth`); if (!extendedCreds.accessToken) { console.error(`OAuth is enabled but no access token for account ${extendedCreds.email}`); @@ -328,7 +306,7 @@ export async function getImapConnection( console.log(`Ensuring fresh token for OAuth account ${extendedCreds.email}`); const { accessToken, success } = await ensureFreshToken(userId, extendedCreds.email); - if (success) { + if (success && accessToken) { extendedCreds.accessToken = accessToken; console.log(`Successfully refreshed token for ${extendedCreds.email}`); } else { @@ -548,11 +526,20 @@ export async function saveUserEmailCredentials( // Cast to extended type to access OAuth properties const extendedCreds = credentials as EmailCredentialsExtended; + + // Store OAuth information in a separate object for caching + const oauthData = { + useOAuth: extendedCreds.useOAuth, + accessToken: extendedCreds.accessToken, + refreshToken: extendedCreds.refreshToken, + tokenExpiry: extendedCreds.tokenExpiry + }; // Extract only the fields that exist in the database schema + // Based on the schema from 'npx prisma db pull', OAuth fields don't exist const dbCredentials = { email: credentials.email, - password: credentials.password ?? '', + password: credentials.password ?? '', // Required field in the DB schema host: credentials.host, port: credentials.port, secure: credentials.secure ?? true, @@ -560,21 +547,19 @@ 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, - // Add OAuth fields if present - using the camelCase names from the schema - useOAuth: extendedCreds.useOAuth ?? false, - accessToken: extendedCreds.accessToken || null, - refreshToken: extendedCreds.refreshToken || null, - tokenExpiry: extendedCreds.tokenExpiry ? new Date(extendedCreds.tokenExpiry) : null + color: credentials.color || null }; try { - console.log('Saving credentials with OAuth data:', { + console.log('Saving credentials to database:', { ...dbCredentials, password: dbCredentials.password ? '***' : null, - accessToken: dbCredentials.accessToken ? '***' : null, - refreshToken: dbCredentials.refreshToken ? '***' : null, - useOAuth: dbCredentials.useOAuth + }); + + console.log('OAuth data will be saved to Redis cache only:', { + hasOAuth: !!oauthData.useOAuth, + hasAccessToken: !!oauthData.accessToken, + hasRefreshToken: !!oauthData.refreshToken }); // Save to database using the unique constraint on [userId, email] @@ -597,9 +582,15 @@ export async function saveUserEmailCredentials( } }); - // Cache the full credentials object in Redis - await cacheEmailCredentials(userId, accountId, extendedCreds); - console.log('Successfully saved and cached credentials for user:', userId); + // Create a combined credentials object for caching + const fullCreds = { + ...dbCredentials, + ...oauthData + } as EmailCredentialsExtended; // Cast to the expected type + + // Cache the full credentials including OAuth tokens + await cacheEmailCredentials(userId, accountId, fullCreds); + console.log('Successfully saved credentials to database and cached full data with OAuth tokens'); } catch (error) { console.error('Error saving credentials:', error); throw error; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 46517d51..953c946e 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -66,10 +66,10 @@ model MailCredentials { secure Boolean @default(true) // OAuth Settings - useOAuth Boolean @default(false) - refreshToken String? - accessToken String? - tokenExpiry DateTime? + use_oauth Boolean @default(false) + refresh_token String? + access_token String? + token_expiry DateTime? // SMTP Settings smtp_host String?