ayiin 2.0.40 → 2.0.42
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.
- package/dist/index.d.ts +1 -1
- package/dist/methods/limiter.d.ts +0 -1
- package/dist/methods/limiter.js +26 -24
- package/dist/types.d.ts +9 -4
- package/package.json +17 -17
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import Methods from "./methods";
|
|
2
|
-
export type { DurationInput,
|
|
2
|
+
export type { DurationInput, RateLimitStore, RateLimiterOptions, } from "./types";
|
|
3
3
|
export default class Ayiin extends Methods {
|
|
4
4
|
version: string;
|
|
5
5
|
constructor();
|
package/dist/methods/limiter.js
CHANGED
|
@@ -44,7 +44,7 @@ class MemoryStore {
|
|
|
44
44
|
// 🔹 Class Limiter
|
|
45
45
|
class Limiter extends tools_1.default {
|
|
46
46
|
createRateLimit = (options) => {
|
|
47
|
-
const store = options.store ?? new MemoryStore();
|
|
47
|
+
const store = options.store ?? new MemoryStore();
|
|
48
48
|
return async (req, res, next) => {
|
|
49
49
|
const now = Date.now();
|
|
50
50
|
const userId = options.identifyUser
|
|
@@ -57,14 +57,17 @@ class Limiter extends tools_1.default {
|
|
|
57
57
|
const blockedUntil = new Date(blockedUntilStr).getTime();
|
|
58
58
|
const remainingMs = blockedUntil - now;
|
|
59
59
|
if (remainingMs > 0) {
|
|
60
|
-
const msg = options.messageFormat
|
|
61
|
-
?
|
|
60
|
+
const msg = typeof options.messageFormat === "function"
|
|
61
|
+
? options.messageFormat({
|
|
62
62
|
api: options.keyPrefix,
|
|
63
63
|
remainMs: remainingMs,
|
|
64
64
|
remain: this.formatRemainingTime(remainingMs),
|
|
65
|
-
limit: options.limit
|
|
65
|
+
limit: Array.isArray(options.limit)
|
|
66
|
+
? options.limit[0]
|
|
67
|
+
: options.limit,
|
|
66
68
|
})
|
|
67
|
-
:
|
|
69
|
+
: (options.messageFormat ??
|
|
70
|
+
`[FloodWait] - Silakan coba lagi dalam ${this.formatRemainingTime(remainingMs)}.`);
|
|
68
71
|
return res.status(429).json({ success: false, error: msg });
|
|
69
72
|
}
|
|
70
73
|
}
|
|
@@ -75,45 +78,44 @@ class Limiter extends tools_1.default {
|
|
|
75
78
|
if (hits === 1) {
|
|
76
79
|
await store.expire(rateKey, windowMs);
|
|
77
80
|
}
|
|
78
|
-
|
|
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
|
+
}
|
|
91
|
+
if (hits > currentLimit) {
|
|
92
|
+
level += 1;
|
|
93
|
+
await store.set(levelKey, level.toString(), refreshAtMs);
|
|
79
94
|
let floodwaitMs;
|
|
80
|
-
let level;
|
|
81
95
|
if (Array.isArray(options.floodwaitMs)) {
|
|
82
|
-
// escalation mode
|
|
83
|
-
level = parseInt((await store.get(levelKey)) ?? "0", 10) + 1;
|
|
84
|
-
await store.set(levelKey, level.toString(), refreshAtMs);
|
|
85
96
|
const idx = Math.min(level - 1, options.floodwaitMs.length - 1);
|
|
86
97
|
const floodwait = options.floodwaitMs[idx];
|
|
87
98
|
floodwaitMs = this.toMilliseconds(floodwait ?? options.floodwaitMs[0]);
|
|
88
99
|
}
|
|
89
100
|
else {
|
|
90
|
-
// fixed mode
|
|
91
101
|
floodwaitMs = this.toMilliseconds(options.floodwaitMs);
|
|
92
102
|
}
|
|
93
103
|
const untilDate = new Date(now + floodwaitMs).toISOString();
|
|
94
104
|
await store.set(floodKey, untilDate, floodwaitMs);
|
|
95
|
-
const msg = options.messageFormat
|
|
96
|
-
?
|
|
105
|
+
const msg = typeof options.messageFormat === "function"
|
|
106
|
+
? options.messageFormat({
|
|
97
107
|
api: options.keyPrefix,
|
|
98
108
|
remainMs: floodwaitMs,
|
|
99
109
|
remain: this.formatRemainingTime(floodwaitMs),
|
|
100
110
|
level,
|
|
101
|
-
limit:
|
|
111
|
+
limit: currentLimit,
|
|
102
112
|
})
|
|
103
|
-
:
|
|
113
|
+
: (options.messageFormat ??
|
|
114
|
+
`[FloodWait] - Terlalu banyak permintaan. Silakan coba lagi dalam ${this.formatRemainingTime(floodwaitMs)}.`);
|
|
104
115
|
return res.status(429).json({ success: false, error: msg });
|
|
105
116
|
}
|
|
106
117
|
next();
|
|
107
118
|
};
|
|
108
119
|
};
|
|
109
|
-
// internal formatter
|
|
110
|
-
formatMessage(template, ctx) {
|
|
111
|
-
return template
|
|
112
|
-
.replace("%(api)s", ctx.api)
|
|
113
|
-
.replace("%(remainMs)s", ctx.remainMs.toString())
|
|
114
|
-
.replace("%(remain)s", ctx.remain)
|
|
115
|
-
.replace("%(level)s", ctx.level?.toString() ?? "1")
|
|
116
|
-
.replace("%(limit)s", ctx.limit?.toString() ?? "");
|
|
117
|
-
}
|
|
118
120
|
}
|
|
119
121
|
exports.Limiter = Limiter;
|
package/dist/types.d.ts
CHANGED
|
@@ -12,12 +12,17 @@ export interface DurationInput {
|
|
|
12
12
|
}
|
|
13
13
|
export interface RateLimiterOptions {
|
|
14
14
|
keyPrefix: string;
|
|
15
|
-
limit: number;
|
|
15
|
+
limit: number | readonly [number, ...number[]];
|
|
16
16
|
windowMs: DurationInput;
|
|
17
|
-
floodwaitMs: DurationInput | DurationInput[];
|
|
17
|
+
floodwaitMs: DurationInput | readonly [DurationInput, ...DurationInput[]];
|
|
18
18
|
identifyUser?: (req: Request, res: Response) => string | Response<any, Record<string, any>>;
|
|
19
19
|
store?: RateLimitStore;
|
|
20
20
|
refreshAt?: DurationInput;
|
|
21
|
-
messageFormat?: string
|
|
21
|
+
messageFormat?: string | ((ctx: {
|
|
22
|
+
api: string;
|
|
23
|
+
remainMs: number;
|
|
24
|
+
remain: string;
|
|
25
|
+
level?: number;
|
|
26
|
+
limit?: number;
|
|
27
|
+
}) => string);
|
|
22
28
|
}
|
|
23
|
-
export type FloodwaitPlaceholder = "%(api)s" | "%(remainMs)s" | "%(remain)s" | "%(level)s" | "%(limit)s";
|
package/package.json
CHANGED
|
@@ -1,30 +1,30 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ayiin",
|
|
3
|
-
"version": "2.0.
|
|
4
|
-
"
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
"bin": {
|
|
8
|
-
"axd": "./dist/bin/cli.js"
|
|
9
|
-
},
|
|
10
|
-
"scripts": {
|
|
11
|
-
"build": "tsc"
|
|
3
|
+
"version": "2.0.42",
|
|
4
|
+
"author": {
|
|
5
|
+
"name": "AyiinXd",
|
|
6
|
+
"url": "https://github.com/AyiinXd"
|
|
12
7
|
},
|
|
13
8
|
"repository": {
|
|
14
9
|
"type": "git",
|
|
15
10
|
"url": "git+https://github.com/AyiinXd/ayiin.git"
|
|
16
11
|
},
|
|
17
|
-
"
|
|
18
|
-
|
|
19
|
-
"
|
|
12
|
+
"main": "./dist/index.js",
|
|
13
|
+
"devDependencies": {
|
|
14
|
+
"@types/express": "^5.0.6",
|
|
15
|
+
"@types/node": "^26.3.0",
|
|
16
|
+
"typescript": "^7"
|
|
20
17
|
},
|
|
18
|
+
"bin": {
|
|
19
|
+
"axd": "./dist/bin/cli.js"
|
|
20
|
+
},
|
|
21
|
+
"description": "Library Pribadi Yang Gak Ada Isinya",
|
|
21
22
|
"keywords": [
|
|
22
23
|
"ayiin"
|
|
23
24
|
],
|
|
24
25
|
"license": "GPL-3.0-only",
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
}
|
|
26
|
+
"scripts": {
|
|
27
|
+
"build": "tsc"
|
|
28
|
+
},
|
|
29
|
+
"types": "./dist/index.d.ts"
|
|
30
30
|
}
|