Skip to content

Commit 5e29d93

Browse files
authored
background passive mining (#2616)
* fix passive mining dialog ui * create sources page * create passive-mining edge function * Update 20260130124036_passive_mining_in_mining_sources.sql * Update 20260130124036_passive_mining_in_mining_sources.sql * Update 20260130124036_passive_mining_in_mining_sources.sql * Update 20260130124036_passive_mining_in_mining_sources.sql * rm getMiningSources from backend * Update mining.controller.ts * Update index.ts * Update index.ts * Update EmailFetcherFactory.ts * t * fill user data if sent backend request * add passive_mining to task details * add passive_mining to task details * fix mining & imap error propagation * Update index.ts * Update leadminer.ts * Update auth.ts * Update sources.vue * Create boxes.ts * Update index.ts * add /sources to redirectoptions * fixes * Update TasksManager.ts * rername `taskManagerFile` to `tasksManagerFile` * fixes * i18n * Update stream.routes.ts * Update app.ts * i18n * Update index.ts * fixes * Update mining.controller.ts * Update mining.controller.ts * use getUser instead of a service user mock * Update auth.ts * Update auth.ts
1 parent c4db149 commit 5e29d93

File tree

23 files changed

+493
-76
lines changed

23 files changed

+493
-76
lines changed

backend/src/app.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ import initializeImapRoutes from './routes/imap.routes';
2020
import initializeMiningRoutes from './routes/mining.routes';
2121
import initializeStreamRouter from './routes/stream.routes';
2222
import AuthResolver from './services/auth/AuthResolver';
23-
import TasksManagerFile from './services/tasks-manager/TaskManagerFile';
2423
import TasksManager from './services/tasks-manager/TasksManager';
24+
import TasksManagerFile from './services/tasks-manager/TasksManagerFile';
2525
import TasksManagerPST from './services/tasks-manager/TasksManagerPST';
2626
import Billing from './utils/billing-plugin';
2727

backend/src/controllers/mining.controller.ts

Lines changed: 23 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ import {
99
} from '../db/interfaces/MiningSources';
1010
import RedisQueuedEmailsCache from '../services/cache/redis/RedisQueuedEmailsCache';
1111
import { ContactFormat } from '../services/extractors/engines/FileImport';
12-
import TaskManagerFile from '../services/tasks-manager/TaskManagerFile';
1312
import TasksManager from '../services/tasks-manager/TasksManager';
13+
import TasksManagerFile from '../services/tasks-manager/TasksManagerFile';
1414
import TasksManagerPST from '../services/tasks-manager/TasksManagerPST';
1515
import { Task } from '../services/tasks-manager/types';
1616
import { ImapAuthError } from '../utils/errors';
@@ -160,7 +160,7 @@ async function publishPreviouslyUnverifiedEmailsToCleaning(
160160

161161
export default function initializeMiningController(
162162
tasksManager: TasksManager,
163-
tasksManagerFile: TaskManagerFile,
163+
tasksManagerFile: TasksManagerFile,
164164
tasksManagerPST: TasksManagerPST,
165165
miningSources: MiningSources,
166166
contactsDB: Contacts
@@ -293,31 +293,8 @@ export default function initializeMiningController(
293293
}
294294
},
295295

296-
async getMiningSources(_req: Request, res: Response, next: NextFunction) {
297-
const user = res.locals.user as User;
298-
299-
try {
300-
const sourcesData = await miningSources.getByUser(user.id);
301-
302-
const sources = sourcesData.map((s) => ({
303-
email: s.email,
304-
type: s.type,
305-
passive_mining: s.passive_mining
306-
}));
307-
308-
return res.status(200).send({
309-
message: 'Mining sources retrieved successfully',
310-
sources
311-
});
312-
} catch (error) {
313-
res.status(500);
314-
return next(error);
315-
}
316-
},
317-
318296
async startMining(req: Request, res: Response, next: NextFunction) {
319297
const user = res.locals.user as User;
320-
321298
const {
322299
extractSignatures,
323300
miningSource: { email },
@@ -330,6 +307,8 @@ export default function initializeMiningController(
330307
extractSignatures: boolean;
331308
} = req.body;
332309

310+
user.email = email; // used when user is not provided (edge function req)
311+
333312
const errors = [
334313
validateType('email', email, 'string'),
335314
validateType('boxes', folders, 'string[]'),
@@ -384,16 +363,30 @@ export default function initializeMiningController(
384363
} catch (err) {
385364
if (
386365
err instanceof Error &&
387-
err.message.toLowerCase().startsWith('invalid credentials')
366+
'textCode' in err &&
367+
err.textCode === 'CANNOT'
388368
) {
389-
return res.status(401).json({ message: err.message });
369+
return res.sendStatus(409);
390370
}
371+
391372
if (
392373
err instanceof Error &&
393-
'textCode' in err &&
394-
err.textCode === 'CANNOT'
374+
err.message.includes('Request failed with status code 401')
395375
) {
396-
return res.sendStatus(409);
376+
res
377+
.status(401)
378+
.send('Failed to start fetching: Invalid credentials 401');
379+
}
380+
381+
if (
382+
err instanceof Error &&
383+
err.message.includes('Request failed with status code 503')
384+
) {
385+
res
386+
.status(503)
387+
.send(
388+
'Failed to start fetching: Connection not available, please try again later 503'
389+
);
397390
}
398391

399392
const newError = generateErrorObjectFromImapError(err);

backend/src/controllers/stream.controller.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Request, Response } from 'express';
2-
import TasksManagerFile from '../services/tasks-manager/TaskManagerFile';
32
import TasksManager from '../services/tasks-manager/TasksManager';
3+
import TasksManagerFile from '../services/tasks-manager/TasksManagerFile';
44
import TasksManagerPST from '../services/tasks-manager/TasksManagerPST';
55
import logger from '../utils/logger';
66

backend/src/middleware/auth.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,37 @@
11
import { NextFunction, Request, Response } from 'express';
2+
import ENV from '../config';
23
import AuthResolver from '../services/auth/AuthResolver';
4+
import supabase from '../utils/supabase';
35

46
export default function initializeAuthMiddleware(authResolver: AuthResolver) {
57
const verifyJWT = async (req: Request, res: Response, next: NextFunction) => {
68
try {
9+
const authHeader = req.headers.authorization;
10+
const token = authHeader?.split(' ')[1];
11+
// Check for service role authentication
12+
if (
13+
ENV.SUPABASE_SECRET_PROJECT_TOKEN &&
14+
token === ENV.SUPABASE_SECRET_PROJECT_TOKEN
15+
) {
16+
// Extract userId from route or query params
17+
const userId = req.params.userId ?? req.query.userId;
18+
19+
if (userId) {
20+
// Create a service user object with the extracted userId
21+
const { user } = (await supabase.auth.admin.getUserById(userId)).data;
22+
23+
if (!user) {
24+
return res.status(404).json({
25+
message: 'User not found'
26+
});
27+
}
28+
29+
res.locals.user = user;
30+
return next();
31+
}
32+
}
33+
34+
// Standard JWT validation
735
const accessToken = authResolver.getAccessToken(req);
836

937
if (!accessToken) {

backend/src/routes/mining.routes.ts

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import { Router } from 'express';
22
import initializeMiningController from '../controllers/mining.controller';
3+
import { Contacts } from '../db/interfaces/Contacts';
34
import { MiningSources } from '../db/interfaces/MiningSources';
45
import initializeAuthMiddleware from '../middleware/auth';
56
import AuthResolver from '../services/auth/AuthResolver';
6-
import TasksManagerFile from '../services/tasks-manager/TaskManagerFile';
77
import TasksManager from '../services/tasks-manager/TasksManager';
8+
import TasksManagerFile from '../services/tasks-manager/TasksManagerFile';
89
import TasksManagerPST from '../services/tasks-manager/TasksManagerPST';
9-
import { Contacts } from '../db/interfaces/Contacts';
1010

1111
export default function initializeMiningRoutes(
1212
tasksManager: TasksManager,
@@ -26,8 +26,7 @@ export default function initializeMiningRoutes(
2626
getMiningTask,
2727
createProviderMiningSource,
2828
createProviderMiningSourceCallback,
29-
createImapMiningSource,
30-
getMiningSources
29+
createImapMiningSource
3130
} = initializeMiningController(
3231
tasksManager,
3332
tasksManagerFile,
@@ -38,8 +37,6 @@ export default function initializeMiningRoutes(
3837

3938
const authMiddleware = initializeAuthMiddleware(authResolver);
4039

41-
router.get('/mine/sources', authMiddleware, getMiningSources);
42-
4340
router.post('/mine/sources/imap', authMiddleware, createImapMiningSource);
4441

4542
router.post(

backend/src/routes/stream.routes.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ import { Router } from 'express';
22
import initializeStreamController from '../controllers/stream.controller';
33
import initializeAuthMiddleware from '../middleware/auth';
44
import AuthResolver from '../services/auth/AuthResolver';
5-
import TasksManagerFile from '../services/tasks-manager/TaskManagerFile';
65
import TasksManager from '../services/tasks-manager/TasksManager';
6+
import TasksManagerFile from '../services/tasks-manager/TasksManagerFile';
77
import TasksManagerPST from '../services/tasks-manager/TasksManagerPST';
88

99
export default function initializeStreamRouter(

backend/src/server.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ import SupabaseAuthResolver from './services/auth/SupabaseAuthResolver';
1111
import EmailFetcherClient from './services/email-fetching';
1212
import PSTFetcherClient from './services/email-fetching/pst';
1313
import SSEBroadcasterFactory from './services/factory/SSEBroadcasterFactory';
14-
import TasksManagerFile from './services/tasks-manager/TaskManagerFile';
1514
import TasksManager from './services/tasks-manager/TasksManager';
15+
import TasksManagerFile from './services/tasks-manager/TasksManagerFile';
1616
import TasksManagerPST from './services/tasks-manager/TasksManagerPST';
1717
import { flickrBase58IdGenerator } from './services/tasks-manager/utils';
1818
import logger from './utils/logger';

backend/src/services/tasks-manager/TasksManager.ts

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { AxiosError } from 'axios';
12
import { Request, Response } from 'express';
23
import { Redis } from 'ioredis';
34
import {
@@ -10,11 +11,10 @@ import {
1011
TaskProgressType
1112
} from './types';
1213
// eslint-disable-next-line max-classes-per-file
13-
import { TaskCategory, TaskStatus, TaskType } from '../../db/types';
14-
1514
import ENV from '../../config';
1615
import { mailMiningComplete, refineContacts } from '../../db/mail';
1716
import SupabaseTasks from '../../db/supabase/tasks';
17+
import { TaskCategory, TaskStatus, TaskType } from '../../db/types';
1818
import logger from '../../utils/logger';
1919
import EmailFetcherClient from '../email-fetching';
2020
import SSEBroadcasterFactory from '../factory/SSEBroadcasterFactory';
@@ -145,12 +145,10 @@ export default class TasksManager {
145145
* @throws {Error} If a task with the same mining ID already exists.
146146
* @throws {Error} If there is an error when creating the task.
147147
*/
148-
async createTask({
149-
email,
150-
boxes,
151-
fetchEmailBody,
152-
userId
153-
}: ImapEmailsFetcherOptions) {
148+
async createTask(
149+
{ email, boxes, fetchEmailBody, userId }: ImapEmailsFetcherOptions,
150+
passive_mining = false
151+
): Promise<RedactedTask> {
154152
let miningTaskId: string | null = null;
155153
try {
156154
const { miningId, stream } = await this.generateTaskInformation();
@@ -183,7 +181,8 @@ export default class TasksManager {
183181
},
184182
progress: {
185183
signatures: 0
186-
}
184+
},
185+
passive_mining
187186
}
188187
},
189188
fetch: {
@@ -200,7 +199,8 @@ export default class TasksManager {
200199
totalMessages: 0,
201200
folders: boxes,
202201
fetched: 0
203-
}
202+
},
203+
passive_mining
204204
}
205205
},
206206
extract: {
@@ -217,7 +217,8 @@ export default class TasksManager {
217217
},
218218
progress: {
219219
extracted: 0
220-
}
220+
},
221+
passive_mining
221222
}
222223
},
223224
clean: {
@@ -234,7 +235,8 @@ export default class TasksManager {
234235
progress: {
235236
verifiedContacts: 0,
236237
createdContacts: 0
237-
}
238+
},
239+
passive_mining
238240
}
239241
}
240242
},
@@ -294,6 +296,9 @@ export default class TasksManager {
294296
logger.error(`Failed to start fetching task with id: ${miningId}`, {
295297
error
296298
});
299+
if (error instanceof AxiosError) {
300+
throw new Error(`Failed to start fetching: ${error.message}`);
301+
}
297302
throw new Error('Failed to start fetching');
298303
}
299304

backend/src/services/tasks-manager/TaskManagerFile.ts renamed to backend/src/services/tasks-manager/TasksManagerFile.ts

File renamed without changes.

backend/src/services/tasks-manager/types.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ export interface TaskFetch extends Task {
6666
fetched: number;
6767
folders: string[];
6868
};
69+
passive_mining?: boolean;
6970
};
7071
}
7172

@@ -78,6 +79,7 @@ export interface TaskExtract extends Task {
7879
progress: {
7980
extracted: number;
8081
};
82+
passive_mining?: boolean;
8183
};
8284
}
8385

@@ -91,6 +93,7 @@ export interface TaskClean extends Task {
9193
verifiedContacts: number;
9294
createdContacts: number;
9395
};
96+
passive_mining?: boolean;
9497
};
9598
}
9699

@@ -108,6 +111,7 @@ export interface TaskEnrich extends Task {
108111
data: Array<Partial<Contact>>;
109112
raw_data: Array<unknown>;
110113
}[];
114+
passive_mining?: boolean;
111115
};
112116
}
113117

@@ -130,6 +134,7 @@ export interface MiningTask {
130134
progress: TaskProgress;
131135
progressHandlerSSE: RealtimeSSE;
132136
startedAt: number;
137+
passive_mining?: boolean;
133138
}
134139

135140
/**

0 commit comments

Comments
 (0)