@podosoft/podokit 0.10.1 → 0.10.2
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/templates/modules/rate-limit/files/apps/api/src/rate-limit/rate-limit.module.ts
CHANGED
|
@@ -11,6 +11,7 @@ import Redis from "ioredis";
|
|
|
11
11
|
const ttlSeconds = Number(process.env.RATE_LIMIT_TTL ?? 60);
|
|
12
12
|
const limit = Number(process.env.RATE_LIMIT_MAX ?? 100);
|
|
13
13
|
const runtimeLimit = Number(process.env.RATE_LIMIT_RUNTIME_MAX ?? 1000);
|
|
14
|
+
const unthrottledHealthPaths = new Set(["/health", "/health/ready"]);
|
|
14
15
|
|
|
15
16
|
function forwardedClient(headers: unknown): string | undefined {
|
|
16
17
|
if (!headers || typeof headers !== "object") return undefined;
|
|
@@ -29,6 +30,9 @@ class ProxyAwareThrottlerGuard extends ThrottlerGuard {
|
|
|
29
30
|
const { req } = this.getRequestResponse(requestProps.context);
|
|
30
31
|
const method = typeof req.method === "string" ? req.method : "";
|
|
31
32
|
const path = typeof req.path === "string" ? req.path : "";
|
|
33
|
+
if (method === "GET" && unthrottledHealthPaths.has(path)) {
|
|
34
|
+
return Promise.resolve(true);
|
|
35
|
+
}
|
|
32
36
|
if (method === "GET" && path === "/site/settings") {
|
|
33
37
|
return super.handleRequest({ ...requestProps, limit: runtimeLimit });
|
|
34
38
|
}
|
|
@@ -13,19 +13,28 @@ const origin = { origin: base };
|
|
|
13
13
|
// to the module default and allow a little headroom.
|
|
14
14
|
const limit = Number(process.env.RATE_LIMIT_MAX ?? 100);
|
|
15
15
|
|
|
16
|
-
test("rate limit:
|
|
16
|
+
test("rate limit: health probes stay available while ordinary routes return 429 @smoke", async ({
|
|
17
|
+
playwright,
|
|
18
|
+
}) => {
|
|
17
19
|
const ctx = await playwright.request.newContext({ baseURL: base, extraHTTPHeaders: origin });
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
20
|
+
for (const path of ["/api/health", "/api/health/ready"]) {
|
|
21
|
+
for (let i = 0; i < limit + 5; i++) {
|
|
22
|
+
const response = await ctx.get(path);
|
|
23
|
+
expect(response.status(), `${path} request ${i + 1}`).toBe(200);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// The redis module is added automatically and exposes a public demo cache
|
|
28
|
+
// route, so it provides a stable ordinary endpoint for the global limit.
|
|
29
|
+
const probe = await ctx.get("/api/cache/rate-limit-probe");
|
|
21
30
|
test.skip(probe.status() >= 500, "throttler storage (redis) not reachable");
|
|
22
31
|
|
|
23
32
|
let seen200 = probe.status() === 200;
|
|
24
33
|
let got429 = false;
|
|
25
34
|
for (let i = 0; i < limit + 5 && !got429; i++) {
|
|
26
|
-
const
|
|
27
|
-
if (
|
|
28
|
-
if (
|
|
35
|
+
const response = await ctx.get("/api/cache/rate-limit-probe");
|
|
36
|
+
if (response.status() === 200) seen200 = true;
|
|
37
|
+
if (response.status() === 429) got429 = true;
|
|
29
38
|
}
|
|
30
39
|
// A fresh window serves the first requests (200) and rejects once the limit is
|
|
31
40
|
// crossed (429).
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
"Ensure Redis is running: docker compose -f infra/docker/docker-compose.yml up -d",
|
|
23
23
|
"Tune RATE_LIMIT_TTL / RATE_LIMIT_MAX / RATE_LIMIT_RUNTIME_MAX in .env.",
|
|
24
24
|
"Exceeding the limit returns HTTP 429. Skip a route with @SkipThrottle() or tune it with @Throttle().",
|
|
25
|
+
"GET /health and GET /health/ready bypass request quotas so orchestrator probes remain reliable.",
|
|
25
26
|
"The limit is shared across API replicas because it is stored in Redis.",
|
|
26
27
|
"PodoKit's web proxy forwards the resolved client address, so visitors receive independent counters."
|
|
27
28
|
]
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@podosoft/podokit",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.2",
|
|
4
4
|
"description": "An opinionated but extensible starter toolkit and CLI for full-stack TypeScript apps with NestJS, SvelteKit, TailwindCSS, shadcn-svelte, Docker, and k3s.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|