diff --git a/.gitignore b/.gitignore index 52f31cf6..956c3b23 100644 --- a/.gitignore +++ b/.gitignore @@ -129,3 +129,6 @@ docker-compose.dev.yml # New layout development files new-layouts/ + +# Generated CRM/accounting documents (runtime) — never commit +backend/storage/business-docs/ diff --git a/backend/src/routes/adminExpenses.js b/backend/src/routes/adminExpenses.js index a79923f5..4ef9eb1b 100644 --- a/backend/src/routes/adminExpenses.js +++ b/backend/src/routes/adminExpenses.js @@ -48,19 +48,11 @@ function diskUpload(subdir) { const inboundUpload = diskUpload('inbound'); const proofUpload = diskUpload('expenses/proof'); -function requireFlag(key, code) { - return async (req, res, next) => { - try { - const row = await db('feature_flags').where({ key }).first(); - const enabled = row && (row.value === true || row.value === 1 || row.value === '1'); - if (!enabled) return res.status(403).json({ error: `${key} feature is disabled`, code }); - return next(); - } catch (err) { return next(err); } - }; -} -const requireIncoming = requireFlag('incomingInvoices', 'INCOMING_INVOICES_DISABLED'); -const requireExpenses = requireFlag('expenses', 'EXPENSES_DISABLED'); -const requireAccounting = requireFlag('accounting', 'ACCOUNTING_DISABLED'); +// Shared cached feature gate (PR #622 nit 2) — replaces the former local copy. +const { requireFeatureFlag } = require('../middleware/requireFeatureFlag'); +const requireIncoming = requireFeatureFlag('incomingInvoices', 'INCOMING_INVOICES_DISABLED'); +const requireExpenses = requireFeatureFlag('expenses', 'EXPENSES_DISABLED'); +const requireAccounting = requireFeatureFlag('accounting', 'ACCOUNTING_DISABLED'); router.use(adminAuth); diff --git a/backend/src/routes/adminLedger.js b/backend/src/routes/adminLedger.js index fdd8cc83..3a948edb 100644 --- a/backend/src/routes/adminLedger.js +++ b/backend/src/routes/adminLedger.js @@ -21,17 +21,9 @@ const ledgerService = require('../services/ledgerService'); const router = express.Router(); const toInt = (v) => { const n = parseInt(v, 10); return Number.isFinite(n) ? n : undefined; }; -function requireFlag(key, code) { - return async (req, res, next) => { - try { - const row = await db('feature_flags').where({ key }).first(); - const enabled = row && (row.value === true || row.value === 1 || row.value === '1'); - if (!enabled) return res.status(403).json({ error: `${key} feature is disabled`, code }); - return next(); - } catch (err) { return next(err); } - }; -} -const requireAccounting = requireFlag('accounting', 'ACCOUNTING_DISABLED'); +// Shared cached feature gate (PR #622 nit 2) — replaces the former local copy. +const { requireFeatureFlag } = require('../middleware/requireFeatureFlag'); +const requireAccounting = requireFeatureFlag('accounting', 'ACCOUNTING_DISABLED'); router.use(adminAuth); router.use(requireAccounting); diff --git a/backend/src/services/emailIntakeService.js b/backend/src/services/emailIntakeService.js index 2a3bd3ff..e96bff60 100644 --- a/backend/src/services/emailIntakeService.js +++ b/backend/src/services/emailIntakeService.js @@ -202,6 +202,9 @@ async function roundTripTest({ timeoutMs = 30000, intervalMs = 3000 } = {}) { const client = makeImapClient(cfg); await connectWithTimeout(client); const started = Date.now(); + // Backoff (PR #622 nit 4): some IMAP servers throttle frequent SELECT/SEARCH. + // Grow the gap ×1.5 (cap 8s) so a 30s test does ~5 polls, not ~10. + let delay = intervalMs; try { // eslint-disable-next-line no-constant-condition while (true) { @@ -219,7 +222,8 @@ async function roundTripTest({ timeoutMs = 30000, intervalMs = 3000 } = {}) { return { ok: false, sent: true, reason: 'not_received', recipient }; } // eslint-disable-next-line no-await-in-loop - await new Promise((r) => setTimeout(r, intervalMs)); + await new Promise((r) => setTimeout(r, delay)); + delay = Math.min(Math.round(delay * 1.5), 8000); } } finally { await client.logout().catch(() => {}); diff --git a/backend/storage/business-docs/quote/2026/Q-2026-0001.pdf b/backend/storage/business-docs/quote/2026/Q-2026-0001.pdf deleted file mode 100644 index 6089659d..00000000 --- a/backend/storage/business-docs/quote/2026/Q-2026-0001.pdf +++ /dev/null @@ -1 +0,0 @@ -pdf \ No newline at end of file