|
1 | 1 | import { Router } from 'express'; |
2 | 2 | import { query } from '../services/db.js'; |
3 | 3 | import { requireAuth } from '../middleware/auth.js'; |
| 4 | +import { applyInboxRules } from '../services/inboxRules.js'; |
4 | 5 |
|
5 | 6 | const router = Router(); |
6 | 7 | router.use(requireAuth); |
@@ -57,6 +58,95 @@ router.get('/', async (req, res) => { |
57 | 58 | } |
58 | 59 | }); |
59 | 60 |
|
| 61 | +router.post('/run', async (req, res) => { |
| 62 | + const imapMgr = req.app.get('imapManager'); |
| 63 | + const { accountId } = req.body; |
| 64 | + |
| 65 | + let accountIds; |
| 66 | + try { |
| 67 | + if (accountId) { |
| 68 | + const owned = await query( |
| 69 | + 'SELECT id FROM email_accounts WHERE id = $1 AND user_id = $2', |
| 70 | + [accountId, req.session.userId] |
| 71 | + ); |
| 72 | + if (!owned.rows.length) return res.status(404).json({ error: 'Account not found' }); |
| 73 | + accountIds = [accountId]; |
| 74 | + } else { |
| 75 | + const accts = await query( |
| 76 | + 'SELECT id FROM email_accounts WHERE user_id = $1', |
| 77 | + [req.session.userId] |
| 78 | + ); |
| 79 | + accountIds = accts.rows.map(r => r.id); |
| 80 | + } |
| 81 | + } catch (err) { |
| 82 | + console.error('POST /rules/run account lookup error:', err.message); |
| 83 | + return res.status(500).json({ error: 'Failed to run rules' }); |
| 84 | + } |
| 85 | + |
| 86 | + let processed = 0; |
| 87 | + let matched = 0; |
| 88 | + |
| 89 | + for (const acctId of accountIds) { |
| 90 | + try { |
| 91 | + const rulesCheck = await query( |
| 92 | + 'SELECT COUNT(*) AS cnt FROM inbox_rules WHERE user_id = $1 AND enabled = true AND (account_id IS NULL OR account_id = $2)', |
| 93 | + [req.session.userId, acctId] |
| 94 | + ); |
| 95 | + if (parseInt(rulesCheck.rows[0].cnt, 10) === 0) continue; |
| 96 | + |
| 97 | + const acctResult = await query( |
| 98 | + 'SELECT id, user_id, folder_mappings FROM email_accounts WHERE id = $1', |
| 99 | + [acctId] |
| 100 | + ); |
| 101 | + const account = acctResult.rows[0]; |
| 102 | + if (!account) continue; |
| 103 | + |
| 104 | + const msgResult = await query( |
| 105 | + `SELECT id, uid, folder, from_email, from_name, to_addresses, subject, has_attachments, is_read |
| 106 | + FROM messages |
| 107 | + WHERE account_id = $1 AND lower(folder) = 'inbox' |
| 108 | + LIMIT 1000`, |
| 109 | + [acctId] |
| 110 | + ); |
| 111 | + if (!msgResult.rows.length) continue; |
| 112 | + |
| 113 | + const messages = msgResult.rows.map(row => { |
| 114 | + let toArr = []; |
| 115 | + try { |
| 116 | + const raw = typeof row.to_addresses === 'string' |
| 117 | + ? JSON.parse(row.to_addresses) |
| 118 | + : row.to_addresses; |
| 119 | + if (Array.isArray(raw)) { |
| 120 | + toArr = raw.map(a => ({ email: a.address || a.email || '', name: a.name || '' })); |
| 121 | + } |
| 122 | + } catch {} |
| 123 | + return { |
| 124 | + id: row.id, |
| 125 | + uid: row.uid, |
| 126 | + folder: row.folder, |
| 127 | + fromEmail: row.from_email || '', |
| 128 | + fromName: row.from_name || '', |
| 129 | + to: toArr, |
| 130 | + subject: row.subject || '', |
| 131 | + hasAttachments: !!row.has_attachments, |
| 132 | + isRead: !!row.is_read, |
| 133 | + is_read: !!row.is_read, |
| 134 | + parsedHeaders: {}, |
| 135 | + }; |
| 136 | + }); |
| 137 | + |
| 138 | + const before = messages.length; |
| 139 | + const { remaining } = await applyInboxRules(messages, account, imapMgr); |
| 140 | + processed += before; |
| 141 | + matched += before - remaining.length; |
| 142 | + } catch (err) { |
| 143 | + console.error(`POST /rules/run error for account ${acctId}:`, err.message); |
| 144 | + } |
| 145 | + } |
| 146 | + |
| 147 | + res.json({ processed, matched }); |
| 148 | +}); |
| 149 | + |
60 | 150 | router.post('/', async (req, res) => { |
61 | 151 | const { name, accountId, conditionLogic, conditions, actions, enabled, stopProcessing } = req.body; |
62 | 152 | if (!Array.isArray(conditions) || !Array.isArray(actions)) { |
|
0 commit comments