@lytjs/middleware-rate-limit 6.6.0 → 6.8.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/dist/index.cjs CHANGED
@@ -8,7 +8,7 @@ function createRateLimitMiddleware(options) {
8
8
  max: options.max,
9
9
  windowMs: options.windowMs
10
10
  });
11
- const keyGenerator = options.keyGenerator || ((request, _ctx) => request.headers.get("x-forwarded-for") || "unknown");
11
+ const keyGenerator = options.keyGenerator || ((request, _ctx) => request.headers["x-forwarded-for"]?.[0] || "unknown");
12
12
  return async (request, ctx, next) => {
13
13
  const key = keyGenerator(request, ctx);
14
14
  const now = Date.now();
@@ -20,24 +20,28 @@ function createRateLimitMiddleware(options) {
20
20
  };
21
21
  ctx.rateLimit = info;
22
22
  if (!result.allowed) {
23
- const headers = new Headers({
23
+ const headers = {
24
24
  "X-RateLimit-Limit": String(info.limit),
25
25
  "X-RateLimit-Remaining": String(info.remaining),
26
26
  "X-RateLimit-Reset": String(info.reset),
27
27
  "Retry-After": String(Math.ceil((result.reset - now) / 1e3)),
28
28
  "Content-Type": "application/json"
29
- });
30
- return new Response(JSON.stringify({ error: "\u8BF7\u6C42\u8FC7\u591A" }), {
29
+ };
30
+ return {
31
31
  status: 429,
32
- headers
33
- });
32
+ headers,
33
+ body: JSON.stringify({ error: "\u8BF7\u6C42\u8FC7\u591A" })
34
+ };
34
35
  }
35
36
  await next();
36
37
  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));
38
+ const newResponse = { ...ctx.response };
39
+ newResponse.headers = {
40
+ ...newResponse.headers,
41
+ "X-RateLimit-Limit": String(info.limit),
42
+ "X-RateLimit-Remaining": String(info.remaining),
43
+ "X-RateLimit-Reset": String(info.reset)
44
+ };
41
45
  return newResponse;
42
46
  }
43
47
  };
@@ -1 +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"]}
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,EAAc,IAAA,KACb,OAAA,CAAQ,OAAA,CAA8C,iBAAiB,CAAA,GAAI,CAAC,CAAA,IAAK,SAAA,CAAA;AAEtF,EAAA,OAAO,OAAO,OAAA,EAAc,GAAA,EAAwB,IAAA,KAA8B;AAChF,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,GAA6C;AAAA,QACjD,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,OAClB;AAEA,MAAA,OAAO;AAAA,QACL,MAAA,EAAQ,GAAA;AAAA,QACR,OAAA;AAAA,QACA,MAAM,IAAA,CAAK,SAAA,CAAU,EAAE,KAAA,EAAO,4BAAQ;AAAA,OACxC;AAAA,IACF;AAEA,IAAA,MAAM,IAAA,EAAK;AAEX,IAAA,IAAI,IAAI,QAAA,EAAU;AAChB,MAAA,MAAM,WAAA,GAAc,EAAE,GAAG,GAAA,CAAI,QAAA,EAAS;AACtC,MAAA,WAAA,CAAY,OAAA,GAAU;AAAA,QACpB,GAAG,WAAA,CAAY,OAAA;AAAA,QACf,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;AAAA,OACxC;AAEA,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: any, _ctx: MiddlewareContext) =>\n (request.headers as Record<string, string | string[]>)['x-forwarded-for']?.[0] || 'unknown');\n\n return async (request: any, 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: Record<string, string | string[]> = {\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 {\n status: 429,\n headers,\n body: JSON.stringify({ error: '请求过多' }),\n } as any;\n }\n\n await next();\n\n if (ctx.response) {\n const newResponse = { ...ctx.response } as any;\n newResponse.headers = {\n ...newResponse.headers,\n 'X-RateLimit-Limit': String(info.limit),\n 'X-RateLimit-Remaining': String(info.remaining),\n 'X-RateLimit-Reset': String(info.reset),\n };\n\n return newResponse;\n }\n };\n}\n"]}
@@ -0,0 +1,38 @@
1
+ import { Middleware } from '@lytjs/middleware';
2
+
3
+ /**
4
+ * 限流中间件配置选项
5
+ */
6
+ interface RateLimitOptions {
7
+ /** 时间窗口大小(毫秒) */
8
+ windowMs: number;
9
+ /** 时间窗口内最大请求数 */
10
+ max: number;
11
+ /** 键生成函数,用于标识请求来源 */
12
+ keyGenerator?: (request: Request, ctx: unknown) => string;
13
+ }
14
+ /**
15
+ * 限流信息
16
+ */
17
+ interface RateLimitInfo {
18
+ /** 剩余请求数 */
19
+ remaining: number;
20
+ /** 重置时间戳(毫秒) */
21
+ reset: number;
22
+ /** 限制总数 */
23
+ limit: number;
24
+ }
25
+
26
+ /**
27
+ * 限流中间件实现
28
+ */
29
+
30
+ /**
31
+ * 创建限流中间件
32
+ *
33
+ * @param options - 限流配置选项
34
+ * @returns 限流中间件函数
35
+ */
36
+ declare function createRateLimitMiddleware(options: RateLimitOptions): Middleware;
37
+
38
+ export { type RateLimitInfo, type RateLimitOptions, createRateLimitMiddleware };
@@ -0,0 +1,38 @@
1
+ import { Middleware } from '@lytjs/middleware';
2
+
3
+ /**
4
+ * 限流中间件配置选项
5
+ */
6
+ interface RateLimitOptions {
7
+ /** 时间窗口大小(毫秒) */
8
+ windowMs: number;
9
+ /** 时间窗口内最大请求数 */
10
+ max: number;
11
+ /** 键生成函数,用于标识请求来源 */
12
+ keyGenerator?: (request: Request, ctx: unknown) => string;
13
+ }
14
+ /**
15
+ * 限流信息
16
+ */
17
+ interface RateLimitInfo {
18
+ /** 剩余请求数 */
19
+ remaining: number;
20
+ /** 重置时间戳(毫秒) */
21
+ reset: number;
22
+ /** 限制总数 */
23
+ limit: number;
24
+ }
25
+
26
+ /**
27
+ * 限流中间件实现
28
+ */
29
+
30
+ /**
31
+ * 创建限流中间件
32
+ *
33
+ * @param options - 限流配置选项
34
+ * @returns 限流中间件函数
35
+ */
36
+ declare function createRateLimitMiddleware(options: RateLimitOptions): Middleware;
37
+
38
+ export { type RateLimitInfo, type RateLimitOptions, createRateLimitMiddleware };
package/dist/index.mjs CHANGED
@@ -6,7 +6,7 @@ function createRateLimitMiddleware(options) {
6
6
  max: options.max,
7
7
  windowMs: options.windowMs
8
8
  });
9
- const keyGenerator = options.keyGenerator || ((request, _ctx) => request.headers.get("x-forwarded-for") || "unknown");
9
+ const keyGenerator = options.keyGenerator || ((request, _ctx) => request.headers["x-forwarded-for"]?.[0] || "unknown");
10
10
  return async (request, ctx, next) => {
11
11
  const key = keyGenerator(request, ctx);
12
12
  const now = Date.now();
@@ -18,24 +18,28 @@ function createRateLimitMiddleware(options) {
18
18
  };
19
19
  ctx.rateLimit = info;
20
20
  if (!result.allowed) {
21
- const headers = new Headers({
21
+ const headers = {
22
22
  "X-RateLimit-Limit": String(info.limit),
23
23
  "X-RateLimit-Remaining": String(info.remaining),
24
24
  "X-RateLimit-Reset": String(info.reset),
25
25
  "Retry-After": String(Math.ceil((result.reset - now) / 1e3)),
26
26
  "Content-Type": "application/json"
27
- });
28
- return new Response(JSON.stringify({ error: "\u8BF7\u6C42\u8FC7\u591A" }), {
27
+ };
28
+ return {
29
29
  status: 429,
30
- headers
31
- });
30
+ headers,
31
+ body: JSON.stringify({ error: "\u8BF7\u6C42\u8FC7\u591A" })
32
+ };
32
33
  }
33
34
  await next();
34
35
  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));
36
+ const newResponse = { ...ctx.response };
37
+ newResponse.headers = {
38
+ ...newResponse.headers,
39
+ "X-RateLimit-Limit": String(info.limit),
40
+ "X-RateLimit-Remaining": String(info.remaining),
41
+ "X-RateLimit-Reset": String(info.reset)
42
+ };
39
43
  return newResponse;
40
44
  }
41
45
  };
@@ -1 +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"]}
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,EAAc,IAAA,KACb,OAAA,CAAQ,OAAA,CAA8C,iBAAiB,CAAA,GAAI,CAAC,CAAA,IAAK,SAAA,CAAA;AAEtF,EAAA,OAAO,OAAO,OAAA,EAAc,GAAA,EAAwB,IAAA,KAA8B;AAChF,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,GAA6C;AAAA,QACjD,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,OAClB;AAEA,MAAA,OAAO;AAAA,QACL,MAAA,EAAQ,GAAA;AAAA,QACR,OAAA;AAAA,QACA,MAAM,IAAA,CAAK,SAAA,CAAU,EAAE,KAAA,EAAO,4BAAQ;AAAA,OACxC;AAAA,IACF;AAEA,IAAA,MAAM,IAAA,EAAK;AAEX,IAAA,IAAI,IAAI,QAAA,EAAU;AAChB,MAAA,MAAM,WAAA,GAAc,EAAE,GAAG,GAAA,CAAI,QAAA,EAAS;AACtC,MAAA,WAAA,CAAY,OAAA,GAAU;AAAA,QACpB,GAAG,WAAA,CAAY,OAAA;AAAA,QACf,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;AAAA,OACxC;AAEA,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: any, _ctx: MiddlewareContext) =>\n (request.headers as Record<string, string | string[]>)['x-forwarded-for']?.[0] || 'unknown');\n\n return async (request: any, 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: Record<string, string | string[]> = {\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 {\n status: 429,\n headers,\n body: JSON.stringify({ error: '请求过多' }),\n } as any;\n }\n\n await next();\n\n if (ctx.response) {\n const newResponse = { ...ctx.response } as any;\n newResponse.headers = {\n ...newResponse.headers,\n 'X-RateLimit-Limit': String(info.limit),\n 'X-RateLimit-Remaining': String(info.remaining),\n 'X-RateLimit-Reset': String(info.reset),\n };\n\n return newResponse;\n }\n };\n}\n"]}
package/package.json CHANGED
@@ -1,60 +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
- }
1
+ {
2
+ "name": "@lytjs/middleware-rate-limit",
3
+ "version": "6.8.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": "^6.8.0",
35
+ "@lytjs/common-rate-limit": "^6.8.0"
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
+ }