Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions backend/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import initializeSentry from './middleware/sentry';
import initializeAuthRoutes from './routes/auth.routes';
import initializeContactsRoutes from './routes/contacts.routes';
import initializeEnrichmentRoutes from './routes/enrichment.routes';
import initializeImapRoutes from './routes/imap.routes';
import initializeMiningRoutes from './routes/mining.routes';
import initializeSmtpSendersRoutes from './routes/smtp-senders.routes';
import initializeStreamRouter from './routes/stream.routes';
Expand Down Expand Up @@ -65,7 +64,6 @@ export default function initializeApp(
);

app.use('/api/auth', initializeAuthRoutes());
app.use('/api/imap', initializeImapRoutes(authResolver, miningSourceService));
app.use('/api/imap', initializeStreamRouter(miningEngine, authResolver));
app.use(
'/api/imap',
Expand Down
141 changes: 0 additions & 141 deletions backend/src/controllers/imap.controller.ts

This file was deleted.

18 changes: 0 additions & 18 deletions backend/src/routes/imap.routes.ts

This file was deleted.

58 changes: 0 additions & 58 deletions backend/src/services/imap/ImapBoxesFetcher.ts

This file was deleted.

9 changes: 3 additions & 6 deletions frontend/src/stores/leadminer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -226,13 +226,10 @@ export const useLeadminerStore = defineStore('leadminer', () => {
selectedBoxes.value = [];
extractSignatures.value = true;

const { data } = await $api<{
const { data } = await supabase.functions.invoke<{
data: { message: string; folders: BoxNode[] };
}>('/imap/boxes', {
method: 'POST',
body: {
...activeMiningSource.value,
},
}>('imap/boxes', {
body: { email: activeMiningSource.value.email },
});

const { folders } = data || {};
Expand Down
78 changes: 78 additions & 0 deletions micro-services/emails-fetcher/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import EmailsFetcher, {
import GoogleContactsFetcher from './services/google_contacts/GoogleContactsFetcher';
import EmailFetcherFactory from './factory/EmailFetcherFactory';
import PSTFetcherFactory from './factory/PSTFetcherFactory';
import ImapBoxesFetcher from './services/imap/ImapBoxesFetcher';
import ImapConnectionProvider from './services/imap/ImapConnectionProvider';
import { generateErrorObjectFromImapError } from './utils/imap';
import logger from './utils/logger';
Expand Down Expand Up @@ -566,4 +567,81 @@ apiRoutes.delete(
}
);

apiRoutes.post(
'/imap/boxes',
async (req: Request, res: Response, next: NextFunction) => {
const { userId, email } = req.body;

const errors = [
validateType('userId', userId, 'string'),
validateType('email', email, 'string')
].filter(Boolean);

if (errors.length) {
return res
.status(400)
.json({ message: `Invalid input: ${errors.join(', ')}` });
}

const sanitizedEmail = sanitizeImapInput(email);
let connection: Connection | undefined;

try {
const sources = await miningSourceService.getSourcesForUser(
userId,
sanitizedEmail
);
const miningSourceCredentials = sources?.pop()?.credentials;

if (!miningSourceCredentials) {
return res.status(401).json({
message: "This mining source isn't registered for this user"
});
}

const credentials =
'accessToken' in miningSourceCredentials
? {
oauthToken: miningSourceCredentials.accessToken
}
: {
host: miningSourceCredentials.host,
password: miningSourceCredentials.password,
tls: miningSourceCredentials.tls,
port: miningSourceCredentials.port
};

connection = await ImapConnectionProvider.getSingleConnection(
sanitizedEmail,
credentials
);

const boxesFetcher = new ImapBoxesFetcher(connection, logger);
const folders = await boxesFetcher.getTree(sanitizedEmail);

return res.status(200).json({
data: {
message: 'IMAP folders fetched successfully',
folders
}
});
} catch (err) {
logger.error('Failed to fetch IMAP boxes', err);
const newError = generateErrorObjectFromImapError(err);

res.status(500);
return next(new Error(newError.message));
} finally {
if (connection) {
try {
await connection.logout();
} catch (logoutError) {
logger.error('Error closing IMAP connection', logoutError);
connection.close();
}
}
}
}
);

export default apiRoutes;
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { ImapFlow as Connection } from 'imapflow';
import { Logger } from 'winston';
import {
buildFinalTree,
createFlatTreeFromImap
} from '../../utils/helpers/imapTreeHelpers';

export default class ImapBoxesFetcher {
constructor(
private readonly imapConnection: Connection,
private readonly logger: Logger
) {}

/**
* Retrieves the IMAP tree of the email account.
* @returns IMAP tree.
*/
async getTree(userEmail: string) {
const tree = await this.imapConnection.list({
statusQuery: { messages: true }
});

return buildFinalTree(createFlatTreeFromImap(tree), userEmail);
}
}
Loading
Loading