courrier formatting
This commit is contained in:
parent
af18853d30
commit
67ab46879c
@ -157,6 +157,7 @@ interface ImapSessionData {
|
||||
lastActive: number;
|
||||
mailboxes?: string[];
|
||||
lastVisit?: number;
|
||||
defaultAccountId?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -36,8 +36,16 @@ const connectionPool: Record<string, {
|
||||
lastUsed: number;
|
||||
isConnecting: boolean;
|
||||
connectionPromise?: Promise<ImapFlow>;
|
||||
connectionAttempts?: number;
|
||||
}> = {};
|
||||
|
||||
// Track overall connection metrics
|
||||
let totalConnectionRequests = 0;
|
||||
let totalNewConnections = 0;
|
||||
let totalReuseConnections = 0;
|
||||
let totalConnectionErrors = 0;
|
||||
let lastMetricsReset = Date.now();
|
||||
|
||||
const CONNECTION_TIMEOUT = 15 * 60 * 1000; // Increased to 15 minutes for long-lived connections
|
||||
const MAX_POOL_SIZE = 20; // Maximum number of connections to keep in the pool
|
||||
const CONNECTION_CHECK_INTERVAL = 60 * 1000; // Check every minute
|
||||
@ -47,6 +55,16 @@ setInterval(() => {
|
||||
const now = Date.now();
|
||||
const connectionKeys = Object.keys(connectionPool);
|
||||
|
||||
// If we've been collecting metrics for more than an hour, log and reset
|
||||
if (now - lastMetricsReset > 60 * 60 * 1000) {
|
||||
console.log(`[IMAP METRICS] Total requests: ${totalConnectionRequests}, New connections: ${totalNewConnections}, Reused: ${totalReuseConnections}, Errors: ${totalConnectionErrors}, Success rate: ${((totalReuseConnections + totalNewConnections) / totalConnectionRequests * 100).toFixed(2)}%`);
|
||||
totalConnectionRequests = 0;
|
||||
totalNewConnections = 0;
|
||||
totalReuseConnections = 0;
|
||||
totalConnectionErrors = 0;
|
||||
lastMetricsReset = now;
|
||||
}
|
||||
|
||||
// If we're over the pool size limit, sort by last used and remove oldest
|
||||
if (connectionKeys.length > MAX_POOL_SIZE) {
|
||||
const sortedConnections = connectionKeys
|
||||
@ -94,6 +112,9 @@ setInterval(() => {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Log connection pool status
|
||||
console.log(`[IMAP POOL] Current size: ${connectionKeys.length}, Max: ${MAX_POOL_SIZE}`);
|
||||
}, CONNECTION_CHECK_INTERVAL);
|
||||
|
||||
/**
|
||||
@ -104,6 +125,9 @@ export async function getImapConnection(
|
||||
userId: string,
|
||||
accountId?: string
|
||||
): Promise<ImapFlow> {
|
||||
const startTime = Date.now();
|
||||
totalConnectionRequests++;
|
||||
|
||||
console.log(`Getting IMAP connection for user ${userId}${accountId ? ` account ${accountId}` : ''}`);
|
||||
|
||||
// Special handling for 'default' accountId - find the first available account
|
||||
@ -142,6 +166,7 @@ export async function getImapConnection(
|
||||
});
|
||||
}
|
||||
} else {
|
||||
totalConnectionErrors++;
|
||||
throw new Error('No email accounts configured for this user');
|
||||
}
|
||||
}
|
||||
@ -160,6 +185,8 @@ export async function getImapConnection(
|
||||
try {
|
||||
const client = await connection.connectionPromise;
|
||||
connection.lastUsed = Date.now();
|
||||
totalReuseConnections++;
|
||||
console.log(`[IMAP] Reused pending connection for ${connectionKey} in ${Date.now() - startTime}ms`);
|
||||
return client;
|
||||
} catch (error) {
|
||||
console.error(`Error waiting for connection for ${connectionKey}:`, error);
|
||||
@ -177,6 +204,8 @@ export async function getImapConnection(
|
||||
// Update session data in Redis
|
||||
await updateSessionData(userId, accountId);
|
||||
|
||||
totalReuseConnections++;
|
||||
console.log(`[IMAP] Successfully reused connection for ${connectionKey} in ${Date.now() - startTime}ms`);
|
||||
return connection.client;
|
||||
} else {
|
||||
console.log(`Existing connection for ${connectionKey} not usable, recreating`);
|
||||
@ -198,60 +227,54 @@ export async function getImapConnection(
|
||||
if (!credentials) {
|
||||
console.log(`Credentials not found in cache for ${userId}${accountId ? ` account ${accountId}` : ''}, attempting database lookup`);
|
||||
credentials = await getUserEmailCredentials(userId, accountId);
|
||||
|
||||
if (!credentials) {
|
||||
throw new Error('No email credentials found');
|
||||
console.error(`No credentials found for user ${userId}${accountId ? ` account ${accountId}` : ''}`);
|
||||
totalConnectionErrors++;
|
||||
throw new Error('Email account credentials not found');
|
||||
}
|
||||
|
||||
// Cache credentials for future use
|
||||
// Cache the credentials for future use
|
||||
await cacheEmailCredentials(userId, accountId, credentials);
|
||||
}
|
||||
|
||||
// Validate credentials
|
||||
if (!credentials.password) {
|
||||
console.error(`Missing password in credentials for user ${userId}${accountId ? ` account ${accountId}` : ''}`);
|
||||
throw new Error('No password configured');
|
||||
}
|
||||
|
||||
if (!credentials.email || !credentials.host) {
|
||||
console.error(`Incomplete credentials for user ${userId}${accountId ? ` account ${accountId}` : ''}`);
|
||||
throw new Error('Invalid email credentials configuration');
|
||||
}
|
||||
|
||||
// Create connection record with connecting state
|
||||
// Initialize connection tracking
|
||||
connectionPool[connectionKey] = {
|
||||
client: null as any, // Will be set once connected
|
||||
client: null as any,
|
||||
lastUsed: Date.now(),
|
||||
isConnecting: true
|
||||
isConnecting: true,
|
||||
connectionAttempts: (connectionPool[connectionKey]?.connectionAttempts || 0) + 1
|
||||
};
|
||||
|
||||
// Create the connection promise
|
||||
const connectionPromise = createImapConnection(credentials, connectionKey);
|
||||
// Create connection promise
|
||||
const connectionPromise = createImapConnection(credentials, connectionKey)
|
||||
.then(client => {
|
||||
// Update connection pool entry
|
||||
connectionPool[connectionKey].client = client;
|
||||
connectionPool[connectionKey].isConnecting = false;
|
||||
connectionPool[connectionKey].lastUsed = Date.now();
|
||||
|
||||
// Update session data
|
||||
updateSessionData(userId, accountId).catch(err => {
|
||||
console.error(`Failed to update session data: ${err.message}`);
|
||||
});
|
||||
|
||||
totalNewConnections++;
|
||||
console.log(`[IMAP] Created new connection for ${connectionKey} in ${Date.now() - startTime}ms (attempt #${connectionPool[connectionKey].connectionAttempts})`);
|
||||
return client;
|
||||
})
|
||||
.catch(error => {
|
||||
// Handle connection error
|
||||
console.error(`Failed to create IMAP connection for ${connectionKey}:`, error);
|
||||
delete connectionPool[connectionKey];
|
||||
totalConnectionErrors++;
|
||||
throw error;
|
||||
});
|
||||
|
||||
// Save the promise to allow other requests to wait for this connection
|
||||
connectionPool[connectionKey].connectionPromise = connectionPromise;
|
||||
|
||||
try {
|
||||
const client = await connectionPromise;
|
||||
console.log(`Successfully connected to IMAP server for ${connectionKey}`);
|
||||
|
||||
// Update connection record
|
||||
connectionPool[connectionKey] = {
|
||||
client,
|
||||
lastUsed: Date.now(),
|
||||
isConnecting: false
|
||||
};
|
||||
|
||||
// Update session data in Redis
|
||||
await updateSessionData(userId, accountId);
|
||||
|
||||
return client;
|
||||
} catch (error) {
|
||||
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
||||
console.error(`IMAP connection error for ${connectionKey}:`, errorMessage);
|
||||
|
||||
// Clean up failed connection
|
||||
delete connectionPool[connectionKey];
|
||||
|
||||
throw new Error(`Failed to connect to IMAP server: ${errorMessage}`);
|
||||
}
|
||||
return connectionPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -272,10 +295,7 @@ async function createImapConnection(credentials: EmailCredentials, connectionKey
|
||||
rejectUnauthorized: false
|
||||
},
|
||||
// Connection timeout settings
|
||||
disableAutoIdle: false, // Keep idle to auto-refresh connection
|
||||
idleTimeout: 60000, // 1 minute
|
||||
idleRefreshTimeout: 30000, // 30 seconds
|
||||
idleRefreshIntervalMs: 30 * 1000, // 30 seconds
|
||||
disableAutoIdle: false // Keep idle to auto-refresh connection
|
||||
});
|
||||
|
||||
await client.connect();
|
||||
@ -302,12 +322,12 @@ async function updateSessionData(userId: string, accountId?: string): Promise<vo
|
||||
await cacheImapSession(userId, {
|
||||
...sessionData,
|
||||
lastActive: Date.now(),
|
||||
...(accountId && { lastAccountId: accountId })
|
||||
...(accountId && { defaultAccountId: accountId })
|
||||
});
|
||||
} else {
|
||||
await cacheImapSession(userId, {
|
||||
lastActive: Date.now(),
|
||||
...(accountId && { lastAccountId: accountId })
|
||||
...(accountId && { defaultAccountId: accountId })
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user