@snehal96/unimail
Version:
Unified email fetching & document extraction layer for modern web apps
71 lines (70 loc) • 2.51 kB
JavaScript
import { ImapFlow } from 'imapflow';
import { EmailParserService } from '../services/EmailParserService.js';
export class ImapAdapter {
constructor(config) {
this.retries = 0;
this.maxRetries = 3;
this.config = config;
this.client = new ImapFlow(config);
this.emailParserService = new EmailParserService();
}
log(msg, extra) {
console.log(`[IMAP] ${msg}`, extra || '');
}
async connectWithRetry() {
while (this.retries < this.maxRetries) {
try {
await this.client.connect();
this.log('Connected successfully');
return;
}
catch (err) {
this.retries++;
this.log(`Connection failed (attempt ${this.retries})`, err);
await new Promise(res => setTimeout(res, 1000 * this.retries)); // exponential backoff
}
}
throw new Error(`Failed to connect after ${this.maxRetries} attempts`);
}
async fetchEmails({ since, limit = 10, mailbox = 'INBOX' }) {
const output = [];
await this.connectWithRetry();
try {
await this.client.mailboxOpen(mailbox);
let counter = 0;
let normalized;
for await (const msg of this.client.fetch({ since }, { source: true, uid: true })) {
normalized = await this.emailParserService.parseEmail(msg.source, msg.uid.toString(), 'imap');
normalized.attachments = normalized.attachments.filter(att => {
if (att.contentId && normalized.bodyHtml?.includes(`cid:${att.contentId.replace(/[<>]/g, '')}`)) {
// This is likely an inline image referenced in the HTML
return false;
}
return true;
});
output.push(normalized);
counter++;
if (counter >= limit)
break;
}
this.log(`Fetched ${output.length} email(s)`);
return output;
}
catch (err) {
this.log('Error during fetchEmails', err);
throw err;
}
finally {
await this.close();
}
}
async close() {
try {
await this.client.logout();
this.log('Disconnected cleanly');
}
catch (err) {
this.log('Error during logout', err);
}
}
}