courrier redis login
This commit is contained in:
parent
6c94b7f8f8
commit
4899bd093b
95
lib/redis.ts
95
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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -100,11 +100,14 @@ setInterval(() => {
|
||||
* Get IMAP connection for a user, reusing existing connections when possible
|
||||
*/
|
||||
export async function getImapConnection(userId: string): Promise<ImapFlow> {
|
||||
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<ImapFlow> {
|
||||
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<ImapFlow> {
|
||||
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<ImapFlow> {
|
||||
}
|
||||
}
|
||||
|
||||
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<ImapFlow> {
|
||||
|
||||
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<ImapFlow> {
|
||||
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}`);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user