ayiin 2.0.42 → 2.0.44

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -40,18 +40,47 @@ class MemoryStore {
40
40
  entry.expireAt = Date.now() + ttlMs;
41
41
  }
42
42
  }
43
+ refresh() {
44
+ this.store.clear();
45
+ }
43
46
  }
44
47
  // 🔹 Class Limiter
45
48
  class Limiter extends tools_1.default {
46
49
  createRateLimit = (options) => {
47
50
  const store = options.store ?? new MemoryStore();
51
+ if (options.refreshOnStart) {
52
+ if (typeof store.refresh === "function") {
53
+ store.refresh();
54
+ console.log(`[RateLimiter] ${store.constructor.name} refreshed on start.`);
55
+ }
56
+ else {
57
+ console.warn(`[RateLimiter] ${store.constructor.name} does not implement refresh() method.`);
58
+ }
59
+ }
48
60
  return async (req, res, next) => {
49
61
  const now = Date.now();
50
62
  const userId = options.identifyUser
51
63
  ? options.identifyUser(req, res)
52
64
  : (req.ip ?? "unknown");
65
+ // key storage
53
66
  const floodKey = `${options.keyPrefix}:flood:${userId}`;
54
67
  const levelKey = `${options.keyPrefix}:level:${userId}`;
68
+ const rateKey = `${options.keyPrefix}:rate:${userId}`;
69
+ const windowMs = this.toMilliseconds(options.windowMs);
70
+ const refreshAtMs = this.toMilliseconds(options.refreshAt ?? { hours: 24 });
71
+ // ambil level sekarang
72
+ let level = parseInt((await store.get(levelKey)) ?? "0", 10);
73
+ // tentukan limit sesuai level
74
+ let currentLimit;
75
+ if (Array.isArray(options.limit)) {
76
+ const idx = Math.min(level, options.limit.length - 1);
77
+ const limit = options.limit[idx];
78
+ currentLimit = limit;
79
+ }
80
+ else {
81
+ currentLimit = Number(options.limit);
82
+ }
83
+ // cek jika user terblock atau tidak
55
84
  const blockedUntilStr = await store.get(floodKey);
56
85
  if (blockedUntilStr) {
57
86
  const blockedUntil = new Date(blockedUntilStr).getTime();
@@ -62,32 +91,20 @@ class Limiter extends tools_1.default {
62
91
  api: options.keyPrefix,
63
92
  remainMs: remainingMs,
64
93
  remain: this.formatRemainingTime(remainingMs),
65
- limit: Array.isArray(options.limit)
66
- ? options.limit[0]
67
- : options.limit,
94
+ limit: currentLimit,
95
+ level,
68
96
  })
69
97
  : (options.messageFormat ??
70
98
  `[FloodWait] - Silakan coba lagi dalam ${this.formatRemainingTime(remainingMs)}.`);
71
99
  return res.status(429).json({ success: false, error: msg });
72
100
  }
73
101
  }
74
- const rateKey = `${options.keyPrefix}:rate:${userId}`;
102
+ // hitung permintaan sekarang
75
103
  const hits = await store.incr(rateKey);
76
- const windowMs = this.toMilliseconds(options.windowMs);
77
- const refreshAtMs = this.toMilliseconds(options.refreshAt ?? { hours: 24 });
78
104
  if (hits === 1) {
79
105
  await store.expire(rateKey, windowMs);
80
106
  }
81
- // ambil level sekarang
82
- let level = parseInt((await store.get(levelKey)) ?? "0", 10);
83
- // tentukan limit sesuai level
84
- let currentLimit;
85
- if (Array.isArray(options.limit)) {
86
- currentLimit = options.limit[Math.min(level, options.limit.length - 1)];
87
- }
88
- else {
89
- currentLimit = Number(options.limit);
90
- }
107
+ // cek level jika permintaan melebihi limit
91
108
  if (hits > currentLimit) {
92
109
  level += 1;
93
110
  await store.set(levelKey, level.toString(), refreshAtMs);
package/dist/types.d.ts CHANGED
@@ -4,6 +4,7 @@ export interface RateLimitStore {
4
4
  set(key: string, value: string, ttlMs?: number): Promise<void>;
5
5
  incr(key: string): Promise<number>;
6
6
  expire(key: string, ttlMs: number): Promise<void>;
7
+ refresh?(): Promise<void> | void;
7
8
  }
8
9
  export interface DurationInput {
9
10
  hours?: number;
@@ -18,6 +19,7 @@ export interface RateLimiterOptions {
18
19
  identifyUser?: (req: Request, res: Response) => string | Response<any, Record<string, any>>;
19
20
  store?: RateLimitStore;
20
21
  refreshAt?: DurationInput;
22
+ refreshOnStart?: boolean;
21
23
  messageFormat?: string | ((ctx: {
22
24
  api: string;
23
25
  remainMs: number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ayiin",
3
- "version": "2.0.42",
3
+ "version": "2.0.44",
4
4
  "author": {
5
5
  "name": "AyiinXd",
6
6
  "url": "https://github.com/AyiinXd"