EverydayTech Platform - Developer Reference
Complete Source Code Documentation - All Applications
Loading...
Searching...
No Matches
emailPollWorker.js
Go to the documentation of this file.
1// Email Poll Worker: Polls tenant email settings and pushes inbound emails to automation
2const Redis = require("ioredis");
3const { ImapFlow } = require("imapflow");
4const { simpleParser } = require("mailparser");
5
6const redisConfig = require('./config/redis');
7const redis = new Redis(redisConfig);
8const db = require('./services/db');
9
10/**
11 *
12 */
13async function getTenantEmailSettings() {
14 // Query DB for all tenants with email automation enabled
15 const res = await db.query("SELECT tenant_id, email_automation FROM tenants WHERE email_automation IS NOT NULL");
16 return res.rows;
17}
18
19/**
20 *
21 * @param tenantId
22 * @param settings
23 */
24async function pollTenantMailbox(tenantId, settings) {
25 // Validate settings before attempting connection
26 if (!settings || typeof settings !== 'object') {
27 console.log(`[EmailPoll] Tenant ${tenantId}: No email settings configured, skipping`);
28 return;
29 }
30
31 const { host, port, username, password } = settings;
32
33 // Skip if essential settings are missing
34 if (!host || !port || !username || !password) {
35 console.log(`[EmailPoll] Tenant ${tenantId}: Incomplete email config (missing host/port/username/password), skipping`);
36 return;
37 }
38
39 // Skip localhost/loopback addresses (common misconfiguration)
40 if (host === 'localhost' || host === '127.0.0.1' || host === '::1') {
41 console.log(`[EmailPoll] Tenant ${tenantId}: Invalid host (${host}), skipping localhost connections`);
42 return;
43 }
44
45 const client = new ImapFlow({
46 host: settings.host,
47 port: settings.port,
48 secure: settings.security === "ssl",
49 auth: {
50 user: settings.username,
51 pass: settings.password
52 },
53 logger: false // Suppress connection errors in logs
54 });
55 try {
56 await client.connect();
57 let lock = await client.getMailboxLock(settings.mailbox || "INBOX");
58 try {
59 // Search for unseen messages
60 const messages = [];
61 for await (let msg of client.fetch({ seen: false }, { source: true })) {
62 const parsed = await simpleParser(msg.source);
63 messages.push(parsed);
64 redis.publish("emails:inbound", JSON.stringify({
65 tenant_id: tenantId,
66 from: parsed.from?.text,
67 to: parsed.to?.text,
68 subject: parsed.subject,
69 text: parsed.text,
70 html: parsed.html
71 }));
72 }
73 if (messages.length > 0) {
74 console.log(`[EmailPoll] Tenant ${tenantId}: Processed ${messages.length} new email(s)`);
75 }
76 } finally {
77 lock.release();
78 }
79 await client.logout();
80 } catch (err) {
81 // Only log if it's not a connection refused error (common for unconfigured)
82 if (err.code !== 'ECONNREFUSED' && err.code !== 'ETIMEDOUT') {
83 console.error(`[EmailPoll] Tenant ${tenantId}: Error polling mailbox - ${err.message}`);
84 }
85 }
86}
87
88/**
89 *
90 */
91async function pollAllTenants() {
92 const tenants = await getTenantEmailSettings();
93 for (const t of tenants) {
94 try {
95 await pollTenantMailbox(t.tenant_id, t.email_automation);
96 } catch (e) { /* log error */ }
97 }
98}
99
100setInterval(pollAllTenants, 60 * 1000); // Poll every minute
101console.log("Email Poll Worker running: polling tenant mailboxes for inbound automation.");