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");
6const redisConfig = require('./config/redis');
7const redis = new Redis(redisConfig);
8const db = require('./services/db');
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");
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`);
31 const { host, port, username, password } = settings;
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`);
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`);
45 const client = new ImapFlow({
48 secure: settings.security === "ssl",
50 user: settings.username,
51 pass: settings.password
53 logger: false // Suppress connection errors in logs
56 await client.connect();
57 let lock = await client.getMailboxLock(settings.mailbox || "INBOX");
59 // Search for unseen 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({
66 from: parsed.from?.text,
68 subject: parsed.subject,
73 if (messages.length > 0) {
74 console.log(`[EmailPoll] Tenant ${tenantId}: Processed ${messages.length} new email(s)`);
79 await client.logout();
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}`);
91async function pollAllTenants() {
92 const tenants = await getTenantEmailSettings();
93 for (const t of tenants) {
95 await pollTenantMailbox(t.tenant_id, t.email_automation);
96 } catch (e) { /* log error */ }
100setInterval(pollAllTenants, 60 * 1000); // Poll every minute
101console.log("Email Poll Worker running: polling tenant mailboxes for inbound automation.");