RocketChat/Rocket.Chat

View on GitHub
apps/meteor/server/features/EmailInbox/EmailInbox.ts

Summary

Maintainability
A
2 hrs
Test Coverage
import type { IEmailInbox } from '@rocket.chat/core-typings';
import { EmailInbox, EmailMessageHistory } from '@rocket.chat/models';
import { Meteor } from 'meteor/meteor';
import nodemailer from 'nodemailer';
import type Mail from 'nodemailer/lib/mailer';

import { settings } from '../../../app/settings/server';
import { IMAPInterceptor } from '../../email/IMAPInterceptor';
import { onEmailReceived } from './EmailInbox_Incoming';
import { logger } from './logger';

export type Inbox = {
    imap: IMAPInterceptor;
    smtp: Mail;
    config: IEmailInbox;
};

export const inboxes = new Map<string, Inbox>();

export async function configureEmailInboxes(): Promise<void> {
    const emailInboxesCursor = EmailInbox.findActive();

    logger.info('Clearing old email inbox registrations');
    for (const { imap } of inboxes.values()) {
        imap.stop();
    }

    inboxes.clear();

    for await (const emailInboxRecord of emailInboxesCursor) {
        try {
            logger.info(`Setting up email interceptor for ${emailInboxRecord.email}`);

            const imap = new IMAPInterceptor(
                {
                    password: emailInboxRecord.imap.password,
                    user: emailInboxRecord.imap.username,
                    host: emailInboxRecord.imap.server,
                    port: emailInboxRecord.imap.port,
                    ...(emailInboxRecord.imap.secure
                        ? {
                                tls: emailInboxRecord.imap.secure,
                                tlsOptions: {
                                    rejectUnauthorized: false,
                                },
                          }
                        : {}),
                },
                {
                    deleteAfterRead: false,
                    filter: [['UNSEEN'], ['SINCE', emailInboxRecord._createdAt]],
                    rejectBeforeTS: emailInboxRecord._createdAt,
                    markSeen: true,
                    maxRetries: emailInboxRecord.imap.maxRetries,
                },
                emailInboxRecord._id,
            );

            imap.on('email', async (email) => {
                if (!email.messageId) {
                    return;
                }

                try {
                    await EmailMessageHistory.create({ _id: email.messageId, email: emailInboxRecord.email });
                    void onEmailReceived(email, emailInboxRecord.email, emailInboxRecord.department);
                } catch (e: any) {
                    // In case the email message history has been received by other instance..
                    logger.error(e);
                }
            });

            await imap.start();

            const smtp = nodemailer.createTransport({
                host: emailInboxRecord.smtp.server,
                port: emailInboxRecord.smtp.port,
                secure: emailInboxRecord.smtp.secure,
                auth: {
                    user: emailInboxRecord.smtp.username,
                    pass: emailInboxRecord.smtp.password,
                },
            });

            inboxes.set(emailInboxRecord.email, { imap, smtp, config: emailInboxRecord });
        } catch (err) {
            logger.error({ msg: `Error setting up email interceptor for ${emailInboxRecord.email}`, err });
        }
    }

    logger.info(`Configured a total of ${inboxes.size} inboxes`);
}

Meteor.startup(() => {
    settings.watchOnce('Livechat_Routing_Method', (_) => {
        void configureEmailInboxes();
    });
});