@lytjs/middleware-rate-limit 6.6.0

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/README.md ADDED
@@ -0,0 +1,40 @@
1
+ # @lytjs/middleware-rate-limit
2
+
3
+ > LytJS 限流中间件。
4
+
5
+ [![npm version](https://img.shields.io/npm/v/@lytjs/middleware-rate-limit.svg)](https://www.npmjs.com/package/@lytjs/middleware-rate-limit)
6
+ [![license](https://img.shields.io/npm/l/@lytjs/middleware-rate-limit.svg)](https://gitee.com/lytjs/lytjs/blob/main/LICENSE)
7
+
8
+ ## 简介
9
+
10
+ `@lytjs/middleware-rate-limit` 是 LytJS 框架的限流中间件,支持多种限流策略。
11
+
12
+ ### 核心特性
13
+
14
+ - **滑动窗口算法**:支持滑动窗口限流
15
+ - **自定义键生成**:可自定义限流键
16
+ - **存储抽象**:支持自定义存储
17
+ - **零依赖**:不引入任何外部依赖
18
+
19
+ ## 安装
20
+
21
+ ```bash
22
+ npm install @lytjs/middleware-rate-limit
23
+ ```
24
+
25
+ 或使用 pnpm:
26
+
27
+ ```bash
28
+ pnpm add @lytjs/middleware-rate-limit
29
+ ```
30
+
31
+ ## 许可证
32
+
33
+ MIT License - [查看许可证](https://gitee.com/lytjs/lytjs/blob/main/LICENSE)
34
+
35
+ ## 贡献指南
36
+
37
+ 欢迎提交 Issue 和 Pull Request!
38
+
39
+ - [Gitee 仓库](https://gitee.com/lytjs/lytjs)
40
+ - [问题反馈](https://gitee.com/lytjs/lytjs/issues)
package/dist/index.cjs ADDED
@@ -0,0 +1,48 @@
1
+ 'use strict';
2
+
3
+ var commonRateLimit = require('@lytjs/common-rate-limit');
4
+
5
+ // src/rate-limit.ts
6
+ function createRateLimitMiddleware(options) {
7
+ const limiter = commonRateLimit.createRateLimiter({
8
+ max: options.max,
9
+ windowMs: options.windowMs
10
+ });
11
+ const keyGenerator = options.keyGenerator || ((request, _ctx) => request.headers.get("x-forwarded-for") || "unknown");
12
+ return async (request, ctx, next) => {
13
+ const key = keyGenerator(request, ctx);
14
+ const now = Date.now();
15
+ const result = limiter.check(key);
16
+ const info = {
17
+ remaining: result.remaining,
18
+ reset: result.reset,
19
+ limit: result.limit
20
+ };
21
+ ctx.rateLimit = info;
22
+ if (!result.allowed) {
23
+ const headers = new Headers({
24
+ "X-RateLimit-Limit": String(info.limit),
25
+ "X-RateLimit-Remaining": String(info.remaining),
26
+ "X-RateLimit-Reset": String(info.reset),
27
+ "Retry-After": String(Math.ceil((result.reset - now) / 1e3)),
28
+ "Content-Type": "application/json"
29
+ });
30
+ return new Response(JSON.stringify({ error: "\u8BF7\u6C42\u8FC7\u591A" }), {
31
+ status: 429,
32
+ headers
33
+ });
34
+ }
35
+ await next();
36
+ if (ctx.response) {
37
+ const newResponse = new Response(ctx.response.body, ctx.response);
38
+ newResponse.headers.set("X-RateLimit-Limit", String(info.limit));
39
+ newResponse.headers.set("X-RateLimit-Remaining", String(info.remaining));
40
+ newResponse.headers.set("X-RateLimit-Reset", String(info.reset));
41
+ return newResponse;
42
+ }
43
+ };
44
+ }
45
+
46
+ exports.createRateLimitMiddleware = createRateLimitMiddleware;
47
+ //# sourceMappingURL=index.cjs.map
48
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/rate-limit.ts"],"names":["createRateLimiter"],"mappings":";;;;;AAaO,SAAS,0BAA0B,OAAA,EAAuC;AAC/E,EAAA,MAAM,UAAUA,iCAAA,CAAkB;AAAA,IAChC,KAAK,OAAA,CAAQ,GAAA;AAAA,IACb,UAAU,OAAA,CAAQ;AAAA,GACnB,CAAA;AACD,EAAA,MAAM,YAAA,GACJ,OAAA,CAAQ,YAAA,KACP,CAAC,OAAA,EAAkB,SAClB,OAAA,CAAQ,OAAA,CAAQ,GAAA,CAAI,iBAAiB,CAAA,IAAK,SAAA,CAAA;AAE9C,EAAA,OAAO,OAAO,OAAA,EAAkB,GAAA,EAAwB,IAAA,KAA8B;AACpF,IAAA,MAAM,GAAA,GAAM,YAAA,CAAa,OAAA,EAAS,GAAG,CAAA;AACrC,IAAA,MAAM,GAAA,GAAM,KAAK,GAAA,EAAI;AACrB,IAAA,MAAM,MAAA,GAAS,OAAA,CAAQ,KAAA,CAAM,GAAG,CAAA;AAEhC,IAAA,MAAM,IAAA,GAAsB;AAAA,MAC1B,WAAW,MAAA,CAAO,SAAA;AAAA,MAClB,OAAO,MAAA,CAAO,KAAA;AAAA,MACd,OAAO,MAAA,CAAO;AAAA,KAChB;AAEA,IAAA,GAAA,CAAI,SAAA,GAAY,IAAA;AAEhB,IAAA,IAAI,CAAC,OAAO,OAAA,EAAS;AACnB,MAAA,MAAM,OAAA,GAAU,IAAI,OAAA,CAAQ;AAAA,QAC1B,mBAAA,EAAqB,MAAA,CAAO,IAAA,CAAK,KAAK,CAAA;AAAA,QACtC,uBAAA,EAAyB,MAAA,CAAO,IAAA,CAAK,SAAS,CAAA;AAAA,QAC9C,mBAAA,EAAqB,MAAA,CAAO,IAAA,CAAK,KAAK,CAAA;AAAA,QACtC,aAAA,EAAe,OAAO,IAAA,CAAK,IAAA,CAAA,CAAM,OAAO,KAAA,GAAQ,GAAA,IAAO,GAAI,CAAC,CAAA;AAAA,QAC5D,cAAA,EAAgB;AAAA,OACjB,CAAA;AAED,MAAA,OAAO,IAAI,SAAS,IAAA,CAAK,SAAA,CAAU,EAAE,KAAA,EAAO,0BAAA,EAAQ,CAAA,EAAG;AAAA,QACrD,MAAA,EAAQ,GAAA;AAAA,QACR;AAAA,OACD,CAAA;AAAA,IACH;AAEA,IAAA,MAAM,IAAA,EAAK;AAEX,IAAA,IAAI,IAAI,QAAA,EAAU;AAChB,MAAA,MAAM,cAAc,IAAI,QAAA,CAAS,IAAI,QAAA,CAAS,IAAA,EAAM,IAAI,QAAQ,CAAA;AAChE,MAAA,WAAA,CAAY,QAAQ,GAAA,CAAI,mBAAA,EAAqB,MAAA,CAAO,IAAA,CAAK,KAAK,CAAC,CAAA;AAC/D,MAAA,WAAA,CAAY,QAAQ,GAAA,CAAI,uBAAA,EAAyB,MAAA,CAAO,IAAA,CAAK,SAAS,CAAC,CAAA;AACvE,MAAA,WAAA,CAAY,QAAQ,GAAA,CAAI,mBAAA,EAAqB,MAAA,CAAO,IAAA,CAAK,KAAK,CAAC,CAAA;AAE/D,MAAA,OAAO,WAAA;AAAA,IACT;AAAA,EACF,CAAA;AACF","file":"index.cjs","sourcesContent":["/**\n * 限流中间件实现\n */\nimport type { RateLimitOptions, RateLimitInfo } from './types';\nimport type { Middleware, MiddlewareContext } from '@lytjs/middleware';\nimport { createRateLimiter } from '@lytjs/common-rate-limit';\n\n/**\n * 创建限流中间件\n *\n * @param options - 限流配置选项\n * @returns 限流中间件函数\n */\nexport function createRateLimitMiddleware(options: RateLimitOptions): Middleware {\n const limiter = createRateLimiter({\n max: options.max,\n windowMs: options.windowMs,\n });\n const keyGenerator =\n options.keyGenerator ||\n ((request: Request, _ctx: MiddlewareContext) =>\n request.headers.get('x-forwarded-for') || 'unknown');\n\n return async (request: Request, ctx: MiddlewareContext, next: () => Promise<void>) => {\n const key = keyGenerator(request, ctx);\n const now = Date.now();\n const result = limiter.check(key);\n\n const info: RateLimitInfo = {\n remaining: result.remaining,\n reset: result.reset,\n limit: result.limit,\n };\n\n ctx.rateLimit = info;\n\n if (!result.allowed) {\n const headers = new Headers({\n 'X-RateLimit-Limit': String(info.limit),\n 'X-RateLimit-Remaining': String(info.remaining),\n 'X-RateLimit-Reset': String(info.reset),\n 'Retry-After': String(Math.ceil((result.reset - now) / 1000)),\n 'Content-Type': 'application/json',\n });\n\n return new Response(JSON.stringify({ error: '请求过多' }), {\n status: 429,\n headers,\n });\n }\n\n await next();\n\n if (ctx.response) {\n const newResponse = new Response(ctx.response.body, ctx.response);\n newResponse.headers.set('X-RateLimit-Limit', String(info.limit));\n newResponse.headers.set('X-RateLimit-Remaining', String(info.remaining));\n newResponse.headers.set('X-RateLimit-Reset', String(info.reset));\n\n return newResponse;\n }\n };\n}\n"]}
package/dist/index.mjs ADDED
@@ -0,0 +1,46 @@
1
+ import { createRateLimiter } from '@lytjs/common-rate-limit';
2
+
3
+ // src/rate-limit.ts
4
+ function createRateLimitMiddleware(options) {
5
+ const limiter = createRateLimiter({
6
+ max: options.max,
7
+ windowMs: options.windowMs
8
+ });
9
+ const keyGenerator = options.keyGenerator || ((request, _ctx) => request.headers.get("x-forwarded-for") || "unknown");
10
+ return async (request, ctx, next) => {
11
+ const key = keyGenerator(request, ctx);
12
+ const now = Date.now();
13
+ const result = limiter.check(key);
14
+ const info = {
15
+ remaining: result.remaining,
16
+ reset: result.reset,
17
+ limit: result.limit
18
+ };
19
+ ctx.rateLimit = info;
20
+ if (!result.allowed) {
21
+ const headers = new Headers({
22
+ "X-RateLimit-Limit": String(info.limit),
23
+ "X-RateLimit-Remaining": String(info.remaining),
24
+ "X-RateLimit-Reset": String(info.reset),
25
+ "Retry-After": String(Math.ceil((result.reset - now) / 1e3)),
26
+ "Content-Type": "application/json"
27
+ });
28
+ return new Response(JSON.stringify({ error: "\u8BF7\u6C42\u8FC7\u591A" }), {
29
+ status: 429,
30
+ headers
31
+ });
32
+ }
33
+ await next();
34
+ if (ctx.response) {
35
+ const newResponse = new Response(ctx.response.body, ctx.response);
36
+ newResponse.headers.set("X-RateLimit-Limit", String(info.limit));
37
+ newResponse.headers.set("X-RateLimit-Remaining", String(info.remaining));
38
+ newResponse.headers.set("X-RateLimit-Reset", String(info.reset));
39
+ return newResponse;
40
+ }
41
+ };
42
+ }
43
+
44
+ export { createRateLimitMiddleware };
45
+ //# sourceMappingURL=index.mjs.map
46
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/rate-limit.ts"],"names":[],"mappings":";;;AAaO,SAAS,0BAA0B,OAAA,EAAuC;AAC/E,EAAA,MAAM,UAAU,iBAAA,CAAkB;AAAA,IAChC,KAAK,OAAA,CAAQ,GAAA;AAAA,IACb,UAAU,OAAA,CAAQ;AAAA,GACnB,CAAA;AACD,EAAA,MAAM,YAAA,GACJ,OAAA,CAAQ,YAAA,KACP,CAAC,OAAA,EAAkB,SAClB,OAAA,CAAQ,OAAA,CAAQ,GAAA,CAAI,iBAAiB,CAAA,IAAK,SAAA,CAAA;AAE9C,EAAA,OAAO,OAAO,OAAA,EAAkB,GAAA,EAAwB,IAAA,KAA8B;AACpF,IAAA,MAAM,GAAA,GAAM,YAAA,CAAa,OAAA,EAAS,GAAG,CAAA;AACrC,IAAA,MAAM,GAAA,GAAM,KAAK,GAAA,EAAI;AACrB,IAAA,MAAM,MAAA,GAAS,OAAA,CAAQ,KAAA,CAAM,GAAG,CAAA;AAEhC,IAAA,MAAM,IAAA,GAAsB;AAAA,MAC1B,WAAW,MAAA,CAAO,SAAA;AAAA,MAClB,OAAO,MAAA,CAAO,KAAA;AAAA,MACd,OAAO,MAAA,CAAO;AAAA,KAChB;AAEA,IAAA,GAAA,CAAI,SAAA,GAAY,IAAA;AAEhB,IAAA,IAAI,CAAC,OAAO,OAAA,EAAS;AACnB,MAAA,MAAM,OAAA,GAAU,IAAI,OAAA,CAAQ;AAAA,QAC1B,mBAAA,EAAqB,MAAA,CAAO,IAAA,CAAK,KAAK,CAAA;AAAA,QACtC,uBAAA,EAAyB,MAAA,CAAO,IAAA,CAAK,SAAS,CAAA;AAAA,QAC9C,mBAAA,EAAqB,MAAA,CAAO,IAAA,CAAK,KAAK,CAAA;AAAA,QACtC,aAAA,EAAe,OAAO,IAAA,CAAK,IAAA,CAAA,CAAM,OAAO,KAAA,GAAQ,GAAA,IAAO,GAAI,CAAC,CAAA;AAAA,QAC5D,cAAA,EAAgB;AAAA,OACjB,CAAA;AAED,MAAA,OAAO,IAAI,SAAS,IAAA,CAAK,SAAA,CAAU,EAAE,KAAA,EAAO,0BAAA,EAAQ,CAAA,EAAG;AAAA,QACrD,MAAA,EAAQ,GAAA;AAAA,QACR;AAAA,OACD,CAAA;AAAA,IACH;AAEA,IAAA,MAAM,IAAA,EAAK;AAEX,IAAA,IAAI,IAAI,QAAA,EAAU;AAChB,MAAA,MAAM,cAAc,IAAI,QAAA,CAAS,IAAI,QAAA,CAAS,IAAA,EAAM,IAAI,QAAQ,CAAA;AAChE,MAAA,WAAA,CAAY,QAAQ,GAAA,CAAI,mBAAA,EAAqB,MAAA,CAAO,IAAA,CAAK,KAAK,CAAC,CAAA;AAC/D,MAAA,WAAA,CAAY,QAAQ,GAAA,CAAI,uBAAA,EAAyB,MAAA,CAAO,IAAA,CAAK,SAAS,CAAC,CAAA;AACvE,MAAA,WAAA,CAAY,QAAQ,GAAA,CAAI,mBAAA,EAAqB,MAAA,CAAO,IAAA,CAAK,KAAK,CAAC,CAAA;AAE/D,MAAA,OAAO,WAAA;AAAA,IACT;AAAA,EACF,CAAA;AACF","file":"index.mjs","sourcesContent":["/**\n * 限流中间件实现\n */\nimport type { RateLimitOptions, RateLimitInfo } from './types';\nimport type { Middleware, MiddlewareContext } from '@lytjs/middleware';\nimport { createRateLimiter } from '@lytjs/common-rate-limit';\n\n/**\n * 创建限流中间件\n *\n * @param options - 限流配置选项\n * @returns 限流中间件函数\n */\nexport function createRateLimitMiddleware(options: RateLimitOptions): Middleware {\n const limiter = createRateLimiter({\n max: options.max,\n windowMs: options.windowMs,\n });\n const keyGenerator =\n options.keyGenerator ||\n ((request: Request, _ctx: MiddlewareContext) =>\n request.headers.get('x-forwarded-for') || 'unknown');\n\n return async (request: Request, ctx: MiddlewareContext, next: () => Promise<void>) => {\n const key = keyGenerator(request, ctx);\n const now = Date.now();\n const result = limiter.check(key);\n\n const info: RateLimitInfo = {\n remaining: result.remaining,\n reset: result.reset,\n limit: result.limit,\n };\n\n ctx.rateLimit = info;\n\n if (!result.allowed) {\n const headers = new Headers({\n 'X-RateLimit-Limit': String(info.limit),\n 'X-RateLimit-Remaining': String(info.remaining),\n 'X-RateLimit-Reset': String(info.reset),\n 'Retry-After': String(Math.ceil((result.reset - now) / 1000)),\n 'Content-Type': 'application/json',\n });\n\n return new Response(JSON.stringify({ error: '请求过多' }), {\n status: 429,\n headers,\n });\n }\n\n await next();\n\n if (ctx.response) {\n const newResponse = new Response(ctx.response.body, ctx.response);\n newResponse.headers.set('X-RateLimit-Limit', String(info.limit));\n newResponse.headers.set('X-RateLimit-Remaining', String(info.remaining));\n newResponse.headers.set('X-RateLimit-Reset', String(info.reset));\n\n return newResponse;\n }\n };\n}\n"]}
package/package.json ADDED
@@ -0,0 +1,60 @@
1
+ {
2
+ "name": "@lytjs/middleware-rate-limit",
3
+ "version": "6.6.0",
4
+ "description": "LytJS 速率限制中间件",
5
+ "type": "module",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.mjs",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.mjs",
13
+ "require": "./dist/index.cjs"
14
+ },
15
+ "./package.json": "./package.json"
16
+ },
17
+ "files": [
18
+ "dist",
19
+ "README.md",
20
+ "LICENSE"
21
+ ],
22
+ "sideEffects": false,
23
+ "scripts": {
24
+ "dev": "tsup --watch",
25
+ "build": "tsup",
26
+ "test": "vitest run",
27
+ "test:watch": "vitest",
28
+ "test:coverage": "vitest run --coverage",
29
+ "type-check": "tsc --noEmit",
30
+ "lint": "eslint \"src/**/*.ts\" \"tests/**/*.ts\"",
31
+ "clean": "rm -rf dist node_modules .turbo"
32
+ },
33
+ "dependencies": {
34
+ "@lytjs/common-is": "workspace:*",
35
+ "@lytjs/common-rate-limit": "workspace:*"
36
+ },
37
+ "devDependencies": {
38
+ "tsup": "^8.3.6",
39
+ "typescript": "^5.7.3",
40
+ "vitest": "^3.0.0"
41
+ },
42
+ "peerDependencies": {},
43
+ "publishConfig": {
44
+ "access": "public",
45
+ "registry": "https://registry.npmjs.org/"
46
+ },
47
+ "repository": {
48
+ "type": "git",
49
+ "url": "https://gitee.com/lytjs/lytjs.git",
50
+ "directory": "packages/ecosystem/packages/web-framework/packages/middleware-rate-limit"
51
+ },
52
+ "keywords": [
53
+ "lytjs",
54
+ "middleware",
55
+ "rate-limit",
56
+ "throttle"
57
+ ],
58
+ "author": "LytJS Team",
59
+ "license": "MIT"
60
+ }