// src/queue.js — Rate limiting, retry, per-domain throttle const pRetry = require("p-retry"); // ── Per-domain rate limiter ─────────────────────────────────────────────── const domainLastRequest = new Map(); const DOMAIN_DELAY_MS = 1000; // একই domain-এ ১ সেকেন্ড gap async function domainThrottle(url) { try { const domain = new URL(url).hostname; const last = domainLastRequest.get(domain) || 0; const wait = DOMAIN_DELAY_MS - (Date.now() - last); if (wait > 0) await new Promise((r) => setTimeout(r, wait)); domainLastRequest.set(domain, Date.now()); } catch (_) {} } // ── Retry wrapper ───────────────────────────────────────────────────────── async function withRetry(fn, options = {}) { const { retries = 3, minTimeout = 1000, onRetry } = options; return pRetry(fn, { retries, minTimeout, maxTimeout: 10000, randomize: true, onFailedAttempt: (err) => { console.warn(`⚠️ Retry ${err.attemptNumber}/${retries + 1}: ${err.message}`); if (onRetry) onRetry(err); }, }); } // ── Webhook sender ──────────────────────────────────────────────────────── async function sendWebhook(url, data) { if (!url) return; const fetch = require("node-fetch"); try { await fetch(url, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ ...data, timestamp: new Date().toISOString() }), timeout: 10000, }); console.log(`📡 Webhook sent → ${url}`); } catch (err) { console.warn(`⚠️ Webhook failed: ${err.message}`); } } module.exports = { domainThrottle, withRetry, sendWebhook };