File size: 2,005 Bytes
4ec5069
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
// محدّد طلبات توليد الصور:
// - طابور عالمي يحترم 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;
}