IMAP edge cases (#17065)

Fixes IMAP edge case. We had a maximum call stack reached crash if the
array was very large for some folders. Plus some minor perf improvement.


https://twenty-v7.sentry.io/issues/6997180505/events/652c69da1be944aab1c87ac2e6a06c30/
This commit is contained in:
neo773
2026-01-10 19:56:07 +05:30
committed by GitHub
parent 9e22c74b2d
commit 87fb25c703
6 changed files with 21 additions and 33 deletions
@@ -123,16 +123,6 @@ export class ImapClientProvider {
`Connected to IMAP server for ${connectedAccount.handle}`,
);
try {
const mailboxes = await client.list();
this.logger.log(
`Available mailboxes for ${connectedAccount.handle}: ${mailboxes.map((m) => m.path).join(', ')}`,
);
} catch (error) {
this.logger.warn(`Failed to list mailboxes: ${error.message}`);
}
return client;
} catch (error) {
if (timeoutId) {
@@ -140,23 +140,15 @@ export class ImapFindSentFolderService {
folderPath: string,
): Promise<number> {
try {
const lock = await client.getMailboxLock(folderPath);
const status = await client.status(folderPath, {
messages: true,
});
try {
const status = await client.status(folderPath, {
messages: true,
});
const messageCount = status?.messages;
const messageCount = status?.messages;
this.logger.debug(`Folder "${folderPath}" has ${messageCount} messages`);
this.logger.debug(
`Folder "${folderPath}" has ${messageCount} messages`,
);
return isNumber(messageCount) ? messageCount : 0;
} finally {
lock.release();
}
return isNumber(messageCount) ? messageCount : 0;
} catch (error) {
this.logger.warn(
`Error checking folder "${folderPath}": ${error.message}`,
@@ -110,7 +110,7 @@ export class ImapGetMessageListService {
);
const nextCursor = createSyncCursor(
messageUids.map((uid) => ({ uid })),
messageUids,
previousCursor,
mailboxState,
);
@@ -2,17 +2,20 @@ import { type MailboxState } from './extract-mailbox-state.util';
import { type ImapSyncCursor } from './parse-sync-cursor.util';
export const createSyncCursor = (
messages: { uid: number }[],
messageUids: number[],
previousCursor: ImapSyncCursor | null,
mailboxState: MailboxState,
): ImapSyncCursor => {
const { uidValidity, highestModSeq } = mailboxState;
const lastSeenUid = previousCursor?.highestUid ?? 0;
const highestUid =
messages.length > 0
? Math.max(...messages.map((message) => message.uid))
: lastSeenUid;
let highestUid = lastSeenUid;
for (let i = 0; i < messageUids.length; i++) {
if (messageUids[i] > highestUid) {
highestUid = messageUids[i];
}
}
return {
highestUid,
@@ -3,9 +3,10 @@ export type ParsedMessageId = {
uid: number;
};
const MESSAGE_ID_REGEX = /^(.+):(\d+)$/;
export function parseMessageId(messageId: string): ParsedMessageId | null {
const regex = /^(.+):(\d+)$/;
const match = regex.exec(messageId);
const match = MESSAGE_ID_REGEX.exec(messageId);
if (!match) {
return null;
@@ -1,6 +1,8 @@
const NULL_CHAR_REGEX = /\0/g;
/**
* Removes null characters (\0) from a string to prevent unexpected errors
*/
export const sanitizeString = (str: string) => {
return str.replace(/\0/g, '');
return str.replace(NULL_CHAR_REGEX, '');
};