Skip to content

Commit 20d4da3

Browse files
authored
use queue for fetching ranges (#2388)
* fix: use estimated time instead of remaining time * fix: add sse related header * fix: handle errors & increase timeout, decrease body size * fix: use env var for body fetch && remove type any * use queue with concurrent connections * add: try/catch block * add: code refactoring + bug fixes * fix: check the number of connections allowed * fix: ignore email coming from the user doing the mining * add: remove `contact@` from role and add `webmaster@` `questions@` * fix: reduce log frequency by logging signatures pushed to extraction * fix: update mining source on every signin * fix: restore old logic * add: toggle to enable/disable signature extraction * fix: log error do not throw * fix: update tagging unit test * fix: clean, minor refactoring * fix: update param name * add: static method to get single connection * add event listener for pool errors * remove console.log * fix: deepsource issues * fix: formatting
1 parent ee79d0a commit 20d4da3

File tree

11 files changed

+397
-544
lines changed

11 files changed

+397
-544
lines changed

backend/package-lock.json

Lines changed: 35 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

backend/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,9 @@
5656
"mailparser": "^3.7.4",
5757
"nanoid": "^3.3.11",
5858
"nodemon": "^3.1.10",
59-
"pg": "^8.16.3",
6059
"p-limit": "^7.1.0",
60+
"p-queue": "^8.1.0",
61+
"pg": "^8.16.3",
6162
"pg-format": "^1.0.4",
6263
"planer": "^1.2.0",
6364
"qs": "^6.14.0",

backend/src/controllers/mining.controller.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -200,18 +200,21 @@ export default function initializeMiningController(
200200
const user = res.locals.user as User;
201201

202202
const {
203+
extractSignatures,
203204
miningSource: { email },
204205
boxes: folders
205206
}: {
206207
miningSource: {
207208
email: string;
208209
};
209210
boxes: string[];
211+
extractSignatures: boolean;
210212
} = req.body;
211213

212214
const errors = [
213215
validateType('email', email, 'string'),
214-
validateType('boxes', folders, 'string[]')
216+
validateType('boxes', folders, 'string[]'),
217+
validateType('extractSignatures', extractSignatures, 'boolean')
215218
].filter(Boolean);
216219

217220
if (errors.length) {
@@ -232,7 +235,7 @@ export default function initializeMiningController(
232235
);
233236

234237
if (!miningSourceCredentials) {
235-
return res.status(400).json({
238+
return res.status(401).json({
236239
message: "This mining source isn't registered for this user"
237240
});
238241
}
@@ -263,7 +266,7 @@ export default function initializeMiningController(
263266
userId: user.id,
264267
email: miningSourceCredentials.email,
265268
batchSize: ENV.FETCHING_BATCH_SIZE_TO_SEND,
266-
fetchEmailBody: ENV.IMAP_FETCH_BODY
269+
fetchEmailBody: extractSignatures && ENV.IMAP_FETCH_BODY
267270
};
268271
miningTask = await tasksManager.createTask(imapEmailsFetcherOptions);
269272
} catch (err) {

backend/src/db/pg/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
11
import { Pool } from 'pg';
22
import ENV from '../../config';
3+
import logger from '../../utils/logger';
34

45
const pool = new Pool({
56
connectionString: ENV.PG_CONNECTION_STRING,
67
max: 10
78
});
89

10+
pool.on('error', (error, client) => {
11+
logger.error('Error raised by pg-pool: ', error);
12+
client.release();
13+
});
14+
915
export default pool;

backend/src/services/imap/ImapConnectionProvider.ts

Lines changed: 63 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,60 @@ class ImapConnectionProvider {
3333
this.poolIsInitialized = false;
3434
}
3535

36+
/**
37+
* Creates a single IMAP connection to test credentials.
38+
* @param email - Email to connect with
39+
* @param options - Optional: host, password, port, tls, or OAuth token
40+
* @returns Promise<Connection> - A connected ImapFlow instance
41+
*/
42+
static async getSingleConnection(
43+
email: string,
44+
options?: {
45+
host?: string;
46+
password?: string;
47+
tls?: boolean;
48+
port?: number;
49+
oauthToken?: string;
50+
}
51+
): Promise<Connection> {
52+
assert(
53+
options?.password || options?.oauthToken,
54+
'Either password or OAuth token and host must be provided'
55+
);
56+
const imapConfig: Partial<ImapFlowOptions> = {
57+
auth: options?.oauthToken
58+
? { user: email, accessToken: options.oauthToken }
59+
: { user: email },
60+
logger: false,
61+
socketTimeout: 3600000, // Timeout after one hour
62+
connectionTimeout: ENV.IMAP_CONNECTION_TIMEOUT,
63+
greetingTimeout: ENV.IMAP_AUTH_TIMEOUT,
64+
secure: true
65+
};
66+
67+
if (!options?.host || !options?.port) {
68+
const { host, port, tls } = await getOAuthImapConfigByEmail(email);
69+
imapConfig.host = host;
70+
imapConfig.port = port;
71+
imapConfig.secure = tls;
72+
}
73+
74+
const connection = new Connection(imapConfig as ImapFlowOptions);
75+
76+
// Optional logging
77+
connection.on('error', (err) => {
78+
logger.error('ImapFlow connection error:', err);
79+
});
80+
81+
try {
82+
await connection.connect();
83+
return connection;
84+
} catch (err) {
85+
await connection.logout();
86+
throw err;
87+
}
88+
}
89+
3690
/**
3791
* Builds the configuration for connecting to Google using OAuth.
3892
* @param accessToken - OAuth access token
@@ -102,7 +156,6 @@ class ImapConnectionProvider {
102156

103157
connection.on('error', (err) => {
104158
logger.error('ImapFlow connection error:', err);
105-
throw err;
106159
});
107160

108161
try {
@@ -177,8 +230,15 @@ class ImapConnectionProvider {
177230
}
178231
},
179232
destroy: async (connection) => {
180-
await connection.logout();
181-
logger.debug('[ImapConnectionProvider]: Imap connection destroyed');
233+
try {
234+
await connection.logout();
235+
logger.debug('[ImapConnectionProvider]: Imap connection destroyed');
236+
} catch (err) {
237+
logger.error(
238+
'[ImapConnectionProvider]: Error destroying connection',
239+
err
240+
);
241+
}
182242
}
183243
};
184244

0 commit comments

Comments
 (0)