Spaces:
Paused
Paused
File size: 1,995 Bytes
ce9e053 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | // 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 };
|