Spaces:
Paused
Paused
| // 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 }; | |