// محدّد طلبات توليد الصور: // - طابور عالمي يحترم 40 طلب/دقيقة لمزود NVIDIA // - حد لكل مستخدم: 3 صور كل 3 ساعات (يُتتبّع عبر معرّف العميل) const GLOBAL_MAX_PER_MIN = 40; const USER_MAX = 3; const USER_WINDOW_MS = 3 * 60 * 60 * 1000; // 3 ساعات let windowStart = Date.now(); let countThisMinute = 0; const userHits = new Map(); // clientId -> [timestamps] // طابور لضمان عدم تجاوز 40/دقيقة const queue = []; let processing = false; function resetWindowIfNeeded() { const now = Date.now(); if (now - windowStart >= 60_000) { windowStart = now; countThisMinute = 0; } } export function checkUserQuota(clientId) { const now = Date.now(); const hits = (userHits.get(clientId) || []).filter(t => now - t < USER_WINDOW_MS); userHits.set(clientId, hits); if (hits.length >= USER_MAX) { const oldest = hits[0]; const retryMs = USER_WINDOW_MS - (now - oldest); return { allowed: false, retryMs, remaining: 0 }; } return { allowed: true, remaining: USER_MAX - hits.length }; } export function recordUserHit(clientId) { const hits = userHits.get(clientId) || []; hits.push(Date.now()); userHits.set(clientId, hits); } // نفّذ مهمة عبر الطابور العالمي (يضمن <= 40/دقيقة) export function enqueue(task) { return new Promise((resolve, reject) => { queue.push({ task, resolve, reject }); drain(); }); } async function drain() { if (processing) return; processing = true; while (queue.length) { resetWindowIfNeeded(); if (countThisMinute >= GLOBAL_MAX_PER_MIN) { const wait = 60_000 - (Date.now() - windowStart); await new Promise(r => setTimeout(r, Math.max(wait, 250))); continue; } const { task, resolve, reject } = queue.shift(); countThisMinute++; try { resolve(await task()); } catch (e) { reject(e); } } processing = false; }