tezx 2.0.11 → 3.0.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 +122 -89
- package/bun/getConnInfo.d.ts +21 -0
- package/bun/getConnInfo.js +9 -0
- package/bun/index.d.ts +10 -4
- package/bun/index.js +8 -4
- package/bun/ws.d.ts +48 -0
- package/bun/ws.js +58 -0
- package/cjs/bun/getConnInfo.js +12 -0
- package/cjs/bun/index.js +35 -7
- package/cjs/bun/ws.js +63 -0
- package/cjs/core/config.js +2 -12
- package/cjs/core/context.js +131 -379
- package/cjs/core/error.js +49 -0
- package/cjs/core/request.js +79 -131
- package/cjs/core/router.js +54 -387
- package/cjs/core/server.js +83 -202
- package/cjs/deno/env.js +4 -4
- package/cjs/deno/getConnInfo.js +18 -0
- package/cjs/deno/index.js +11 -18
- package/cjs/deno/serveStatic.js +53 -0
- package/cjs/deno/ws.js +39 -0
- package/cjs/helper/index.js +46 -10
- package/cjs/index.js +5 -7
- package/cjs/jwt/node.js +94 -0
- package/cjs/jwt/web.js +178 -0
- package/cjs/middleware/basic-auth.js +42 -0
- package/cjs/middleware/bearer-auth.js +34 -0
- package/cjs/middleware/cache-control.js +44 -0
- package/cjs/middleware/cors.js +11 -21
- package/cjs/middleware/detect-bot.js +57 -0
- package/cjs/middleware/i18n.js +73 -60
- package/cjs/middleware/index.js +8 -46
- package/cjs/middleware/logger.js +9 -4
- package/cjs/middleware/pagination.js +3 -2
- package/cjs/middleware/powered-by.js +3 -2
- package/cjs/middleware/rate-limiter.js +38 -0
- package/cjs/middleware/request-id.js +4 -5
- package/cjs/middleware/sanitize-headers.js +22 -0
- package/cjs/middleware/secure-headers copy.js +143 -0
- package/cjs/middleware/secure-headers.js +157 -0
- package/cjs/middleware/{xssProtection.js → xss-protection.js} +5 -8
- package/cjs/node/env.js +7 -7
- package/cjs/node/getConnInfo.js +16 -0
- package/cjs/node/index.js +17 -18
- package/cjs/node/mount-node.js +59 -0
- package/cjs/node/serveStatic.js +56 -0
- package/cjs/node/toWebRequest.js +25 -0
- package/cjs/node/ws.js +82 -0
- package/cjs/registry/RadixRouter.js +148 -0
- package/cjs/registry/index.js +17 -0
- package/cjs/types/headers.js +2 -0
- package/cjs/types/index.js +13 -0
- package/cjs/utils/buffer.js +17 -0
- package/cjs/utils/colors.js +2 -0
- package/cjs/utils/cookie.js +59 -0
- package/cjs/utils/file.js +136 -0
- package/cjs/utils/formData.js +60 -10
- package/cjs/utils/generateID.js +37 -0
- package/cjs/utils/low-level.js +115 -0
- package/cjs/utils/{staticFile.js → mimeTypes.js} +0 -87
- package/cjs/utils/rateLimit.js +41 -0
- package/cjs/utils/response.js +65 -0
- package/cjs/{core/environment.js → utils/runtime.js} +2 -1
- package/cjs/utils/url.js +65 -30
- package/core/config.d.ts +2 -7
- package/core/config.js +2 -12
- package/core/context.d.ts +209 -164
- package/core/context.js +131 -346
- package/core/error.d.ts +96 -0
- package/core/error.js +44 -0
- package/core/request.d.ts +67 -107
- package/core/request.js +78 -130
- package/core/router.d.ts +138 -133
- package/core/router.js +53 -352
- package/core/server.d.ts +99 -38
- package/core/server.js +83 -202
- package/deno/env.js +3 -3
- package/deno/getConnInfo.d.ts +21 -0
- package/deno/getConnInfo.js +15 -0
- package/deno/index.d.ts +9 -4
- package/deno/index.js +7 -4
- package/deno/serveStatic.d.ts +28 -0
- package/deno/serveStatic.js +49 -0
- package/deno/ws.d.ts +42 -0
- package/deno/ws.js +36 -0
- package/helper/index.d.ts +29 -15
- package/helper/index.js +27 -7
- package/index.d.ts +10 -8
- package/index.js +4 -5
- package/jwt/node.d.ts +39 -0
- package/jwt/node.js +87 -0
- package/jwt/web.d.ts +14 -0
- package/jwt/web.js +174 -0
- package/middleware/basic-auth.d.ts +56 -0
- package/middleware/basic-auth.js +38 -0
- package/middleware/bearer-auth.d.ts +53 -0
- package/middleware/bearer-auth.js +30 -0
- package/middleware/cache-control.d.ts +30 -0
- package/middleware/cache-control.js +40 -0
- package/middleware/cors.d.ts +30 -3
- package/middleware/cors.js +12 -22
- package/middleware/detect-bot.d.ts +113 -0
- package/middleware/detect-bot.js +53 -0
- package/middleware/i18n.d.ts +166 -73
- package/middleware/i18n.js +73 -60
- package/middleware/index.d.ts +8 -32
- package/middleware/index.js +8 -44
- package/middleware/logger.d.ts +5 -2
- package/middleware/logger.js +9 -4
- package/middleware/pagination.d.ts +9 -6
- package/middleware/pagination.js +3 -2
- package/middleware/powered-by.d.ts +2 -1
- package/middleware/powered-by.js +3 -2
- package/middleware/{rateLimiter.d.ts → rate-limiter.d.ts} +15 -9
- package/middleware/rate-limiter.js +34 -0
- package/middleware/request-id.d.ts +2 -1
- package/middleware/request-id.js +5 -6
- package/middleware/{sanitizeHeader.d.ts → sanitize-headers.d.ts} +5 -19
- package/middleware/sanitize-headers.js +18 -0
- package/middleware/secure-headers copy.d.ts +15 -0
- package/middleware/secure-headers copy.js +136 -0
- package/middleware/secure-headers.d.ts +132 -0
- package/middleware/secure-headers.js +153 -0
- package/middleware/{xssProtection.d.ts → xss-protection.d.ts} +2 -1
- package/middleware/xss-protection.js +19 -0
- package/node/env.js +4 -4
- package/node/getConnInfo.d.ts +21 -0
- package/node/getConnInfo.js +13 -0
- package/node/index.d.ts +13 -4
- package/node/index.js +11 -4
- package/node/mount-node.d.ts +11 -0
- package/node/mount-node.js +56 -0
- package/node/serveStatic.d.ts +36 -0
- package/node/serveStatic.js +52 -0
- package/node/toWebRequest.js +22 -0
- package/node/ws.d.ts +56 -0
- package/node/ws.js +46 -0
- package/package.json +39 -30
- package/registry/RadixRouter.d.ts +40 -0
- package/registry/RadixRouter.js +144 -0
- package/registry/index.d.ts +2 -0
- package/registry/index.js +1 -0
- package/types/headers.d.ts +2 -0
- package/types/headers.js +1 -0
- package/types/index.d.ts +318 -18
- package/types/index.js +12 -1
- package/utils/buffer.d.ts +1 -0
- package/utils/buffer.js +14 -0
- package/utils/colors.d.ts +24 -0
- package/utils/colors.js +2 -0
- package/utils/cookie.d.ts +55 -0
- package/utils/cookie.js +53 -0
- package/utils/file.d.ts +38 -0
- package/utils/file.js +96 -0
- package/utils/formData.d.ts +41 -1
- package/utils/formData.js +58 -9
- package/utils/generateID.d.ts +42 -0
- package/utils/generateID.js +32 -0
- package/utils/httpStatusMap.d.ts +14 -0
- package/utils/low-level.d.ts +58 -0
- package/utils/low-level.js +108 -0
- package/utils/mimeTypes.d.ts +4 -0
- package/utils/{staticFile.js → mimeTypes.js} +0 -53
- package/utils/rateLimit.d.ts +18 -0
- package/utils/rateLimit.js +37 -0
- package/utils/response.d.ts +18 -0
- package/utils/response.js +58 -0
- package/{core/environment.d.ts → utils/runtime.d.ts} +1 -0
- package/{core/environment.js → utils/runtime.js} +1 -0
- package/utils/url.d.ts +42 -14
- package/utils/url.js +61 -27
- package/bun/adapter.d.ts +0 -127
- package/bun/adapter.js +0 -97
- package/cjs/bun/adapter.js +0 -100
- package/cjs/core/MiddlewareConfigure.js +0 -68
- package/cjs/core/common.js +0 -15
- package/cjs/deno/adpater.js +0 -67
- package/cjs/helper/common.js +0 -17
- package/cjs/middleware/basicAuth.js +0 -71
- package/cjs/middleware/cacheControl.js +0 -90
- package/cjs/middleware/detectBot.js +0 -104
- package/cjs/middleware/detectLocale.js +0 -43
- package/cjs/middleware/lazyLoadModules.js +0 -73
- package/cjs/middleware/rateLimiter.js +0 -24
- package/cjs/middleware/requestTimeout.js +0 -42
- package/cjs/middleware/sanitizeHeader.js +0 -51
- package/cjs/middleware/secureHeaders.js +0 -42
- package/cjs/node/adapter.js +0 -138
- package/cjs/utils/regexRouter.js +0 -58
- package/cjs/utils/state.js +0 -34
- package/cjs/utils/toWebRequest.js +0 -35
- package/cjs/ws/deno.js +0 -20
- package/cjs/ws/index.js +0 -53
- package/cjs/ws/node.js +0 -65
- package/core/MiddlewareConfigure.d.ts +0 -15
- package/core/MiddlewareConfigure.js +0 -63
- package/core/common.d.ts +0 -21
- package/core/common.js +0 -11
- package/deno/adpater.d.ts +0 -38
- package/deno/adpater.js +0 -64
- package/helper/common.d.ts +0 -5
- package/helper/common.js +0 -14
- package/middleware/basicAuth.d.ts +0 -81
- package/middleware/basicAuth.js +0 -67
- package/middleware/cacheControl.d.ts +0 -48
- package/middleware/cacheControl.js +0 -53
- package/middleware/detectBot.d.ts +0 -121
- package/middleware/detectBot.js +0 -98
- package/middleware/detectLocale.d.ts +0 -55
- package/middleware/detectLocale.js +0 -39
- package/middleware/lazyLoadModules.d.ts +0 -72
- package/middleware/lazyLoadModules.js +0 -69
- package/middleware/rateLimiter.js +0 -20
- package/middleware/requestTimeout.d.ts +0 -25
- package/middleware/requestTimeout.js +0 -38
- package/middleware/sanitizeHeader.js +0 -47
- package/middleware/secureHeaders.d.ts +0 -78
- package/middleware/secureHeaders.js +0 -38
- package/middleware/xssProtection.js +0 -22
- package/node/adapter.d.ts +0 -46
- package/node/adapter.js +0 -102
- package/utils/regexRouter.d.ts +0 -66
- package/utils/regexRouter.js +0 -53
- package/utils/state.d.ts +0 -50
- package/utils/state.js +0 -30
- package/utils/staticFile.d.ts +0 -10
- package/utils/toWebRequest.js +0 -32
- package/ws/deno.d.ts +0 -6
- package/ws/deno.js +0 -16
- package/ws/index.d.ts +0 -180
- package/ws/index.js +0 -50
- package/ws/node.d.ts +0 -7
- package/ws/node.js +0 -28
- /package/{utils → node}/toWebRequest.d.ts +0 -0
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.basicAuth = void 0;
|
|
4
|
-
const config_js_1 = require("../core/config.js");
|
|
5
|
-
const colors_js_1 = require("../utils/colors.js");
|
|
6
|
-
const detectBot_js_1 = require("./detectBot.js");
|
|
7
|
-
const basicAuth = (options) => {
|
|
8
|
-
const { validateCredentials, getRealm = () => "Restricted Area", onUnauthorized = (ctx, error) => {
|
|
9
|
-
const realm = getRealm(ctx);
|
|
10
|
-
ctx.setStatus = 401;
|
|
11
|
-
ctx.header("WWW-Authenticate", `Basic realm="${realm}"`);
|
|
12
|
-
ctx.body = { error: error?.message };
|
|
13
|
-
}, rateLimit, supportedMethods = ["basic", "api-key", "bearer-token"], checkAccess, } = options;
|
|
14
|
-
let storage = rateLimit?.storage;
|
|
15
|
-
if (rateLimit && !rateLimit.storage) {
|
|
16
|
-
storage = (0, detectBot_js_1.createRateLimitDefaultStorage)();
|
|
17
|
-
}
|
|
18
|
-
return async function basicAuth(ctx, next) {
|
|
19
|
-
let authMethod;
|
|
20
|
-
let credentials = {};
|
|
21
|
-
const authHeader = ctx.req.headers.get("authorization");
|
|
22
|
-
if (authHeader) {
|
|
23
|
-
if (authHeader.startsWith("Basic ")) {
|
|
24
|
-
authMethod = "basic";
|
|
25
|
-
const base64Credentials = authHeader.split(" ")[1];
|
|
26
|
-
const decoded = Buffer.from(base64Credentials, "base64").toString("utf-8");
|
|
27
|
-
const [username, password] = decoded.split(":");
|
|
28
|
-
credentials = { username, password };
|
|
29
|
-
}
|
|
30
|
-
else if (authHeader.startsWith("Bearer ")) {
|
|
31
|
-
authMethod = "bearer-token";
|
|
32
|
-
credentials = { token: authHeader.split(" ")[1] };
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
else if (ctx.headers.get("x-api-key")) {
|
|
36
|
-
authMethod = "api-key";
|
|
37
|
-
credentials = { apiKey: ctx.headers.get("x-api-key") };
|
|
38
|
-
}
|
|
39
|
-
if (!authMethod || !supportedMethods.includes(authMethod)) {
|
|
40
|
-
config_js_1.GlobalConfig.debugging.error(`${(0, colors_js_1.colorText)("[AUTH]", "bgRed")} Unsupported or missing authentication method.`);
|
|
41
|
-
return onUnauthorized(ctx, new Error("Unsupported authentication method"));
|
|
42
|
-
}
|
|
43
|
-
if (rateLimit) {
|
|
44
|
-
let key = `${ctx.req.remoteAddress.address}:${ctx.req.remoteAddress.port}`;
|
|
45
|
-
const { check, entry } = (0, detectBot_js_1.isRateLimit)(ctx, key, storage, rateLimit.maxRequests, rateLimit.windowMs);
|
|
46
|
-
if (check) {
|
|
47
|
-
const retryAfter = Math.ceil((entry.resetTime - Date.now()) / 1000);
|
|
48
|
-
ctx.headers.set("Retry-After", retryAfter.toString());
|
|
49
|
-
return onUnauthorized(ctx, new Error(`Rate limit exceeded. Retry after ${retryAfter} seconds.`));
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
try {
|
|
53
|
-
const isValid = await validateCredentials(authMethod, credentials, ctx);
|
|
54
|
-
if (!isValid) {
|
|
55
|
-
throw new Error("Invalid credentials.");
|
|
56
|
-
}
|
|
57
|
-
if (checkAccess) {
|
|
58
|
-
const hasAccess = await checkAccess(ctx, credentials);
|
|
59
|
-
if (!hasAccess) {
|
|
60
|
-
return onUnauthorized(ctx, new Error("Access denied."));
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
return await next();
|
|
64
|
-
}
|
|
65
|
-
catch (error) {
|
|
66
|
-
config_js_1.GlobalConfig.debugging.error(`${(0, colors_js_1.colorText)("[AUTH]", "bgRed")} Failure for method: ${ctx.method}`);
|
|
67
|
-
return onUnauthorized(ctx, error);
|
|
68
|
-
}
|
|
69
|
-
};
|
|
70
|
-
};
|
|
71
|
-
exports.basicAuth = basicAuth;
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.cacheControl = void 0;
|
|
37
|
-
const config_js_1 = require("../core/config.js");
|
|
38
|
-
const cacheControl = (options) => {
|
|
39
|
-
const { defaultSettings, useWeakETag = false, rules = [], logEvent = (event, ctx, error) => {
|
|
40
|
-
if (event === "error") {
|
|
41
|
-
config_js_1.GlobalConfig.debugging.error(`[CACHE] ${event.toUpperCase()}: ${error?.message}`);
|
|
42
|
-
}
|
|
43
|
-
else {
|
|
44
|
-
config_js_1.GlobalConfig.debugging.success(`[CACHE] ${event.toUpperCase()} for ${ctx.method} ${ctx.pathname}`);
|
|
45
|
-
}
|
|
46
|
-
}, } = options;
|
|
47
|
-
return async function cacheControl(ctx, next) {
|
|
48
|
-
if (!["GET", "HEAD"].includes(ctx.method)) {
|
|
49
|
-
return await next();
|
|
50
|
-
}
|
|
51
|
-
try {
|
|
52
|
-
await next();
|
|
53
|
-
const matchedRule = rules.find((rule) => rule.condition(ctx)) || null;
|
|
54
|
-
const { maxAge, scope, enableETag, vary } = matchedRule || defaultSettings;
|
|
55
|
-
const cacheControlValue = `${scope}, max-age=${maxAge}`;
|
|
56
|
-
ctx.header("Cache-Control", cacheControlValue);
|
|
57
|
-
const expiresDate = new Date(Date.now() + maxAge * 1000).toUTCString();
|
|
58
|
-
ctx.header("Expires", expiresDate);
|
|
59
|
-
if (vary?.length) {
|
|
60
|
-
ctx.header("Vary", vary.join(", "));
|
|
61
|
-
}
|
|
62
|
-
if (enableETag) {
|
|
63
|
-
const responseBody = typeof ctx.resBody === "string"
|
|
64
|
-
? ctx.resBody
|
|
65
|
-
: JSON.stringify(ctx.resBody ?? "");
|
|
66
|
-
const etag = await generateETag(responseBody, useWeakETag);
|
|
67
|
-
const ifNoneMatch = ctx.req.headers.get("if-none-match");
|
|
68
|
-
if (ifNoneMatch === etag) {
|
|
69
|
-
ctx.setStatus = 304;
|
|
70
|
-
ctx.body = null;
|
|
71
|
-
logEvent("cached", ctx);
|
|
72
|
-
return;
|
|
73
|
-
}
|
|
74
|
-
ctx.header("ETag", etag);
|
|
75
|
-
}
|
|
76
|
-
logEvent("cached", ctx);
|
|
77
|
-
}
|
|
78
|
-
catch (error) {
|
|
79
|
-
logEvent("error", ctx, error);
|
|
80
|
-
ctx.setStatus = 500;
|
|
81
|
-
ctx.body = { error: "Failed to set cache headers." };
|
|
82
|
-
}
|
|
83
|
-
};
|
|
84
|
-
};
|
|
85
|
-
exports.cacheControl = cacheControl;
|
|
86
|
-
const generateETag = async (content, weak = false) => {
|
|
87
|
-
const crypto = await Promise.resolve().then(() => __importStar(require("node:crypto")));
|
|
88
|
-
const hash = crypto.createHash("md5").update(content).digest("hex");
|
|
89
|
-
return weak ? `W/"${hash}"` : `"${hash}"`;
|
|
90
|
-
};
|
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.detectBot = void 0;
|
|
4
|
-
exports.createRateLimitDefaultStorage = createRateLimitDefaultStorage;
|
|
5
|
-
exports.isRateLimit = isRateLimit;
|
|
6
|
-
const config_js_1 = require("../core/config.js");
|
|
7
|
-
const detectBot = (options = {}) => {
|
|
8
|
-
const { botUserAgents = ["bot", "spider", "crawl", "slurp"], maxRequests = 30, windowMs = 60000, isBlacklisted = async () => false, queryKeyBot = "bot", onBotDetected = "block", enableRateLimiting = false, customBotDetector = async () => false, customBlockedResponse = (ctx, { reason }) => {
|
|
9
|
-
ctx.setStatus = 403;
|
|
10
|
-
return ctx.json({ error: `Bot detected: ${reason}` });
|
|
11
|
-
}, storage, confidenceThreshold = 0.5, } = options;
|
|
12
|
-
let store = storage;
|
|
13
|
-
if (enableRateLimiting) {
|
|
14
|
-
store = createRateLimitDefaultStorage();
|
|
15
|
-
}
|
|
16
|
-
return async function detectBot(ctx, next) {
|
|
17
|
-
const detectionResult = {
|
|
18
|
-
isBot: false,
|
|
19
|
-
indicators: [],
|
|
20
|
-
};
|
|
21
|
-
const userAgent = ctx.headers.get("user-agent")?.toLowerCase() || "";
|
|
22
|
-
const remoteAddress = `${ctx.req.remoteAddress?.address}:${ctx.req.remoteAddress?.port}` ||
|
|
23
|
-
"unknown";
|
|
24
|
-
const isBotQuery = ctx.req.query[queryKeyBot] === "true";
|
|
25
|
-
if (botUserAgents.some((agent) => userAgent.includes(agent))) {
|
|
26
|
-
detectionResult.indicators.push("User-Agent");
|
|
27
|
-
}
|
|
28
|
-
if (await isBlacklisted(ctx, remoteAddress)) {
|
|
29
|
-
detectionResult.indicators.push("Blacklisted IP");
|
|
30
|
-
}
|
|
31
|
-
if (isBotQuery) {
|
|
32
|
-
detectionResult.indicators.push("Query Parameter");
|
|
33
|
-
}
|
|
34
|
-
const key = `${ctx.req.remoteAddress.address}:${ctx.req.remoteAddress.port}`;
|
|
35
|
-
if (enableRateLimiting &&
|
|
36
|
-
isRateLimit(ctx, key, store, maxRequests, windowMs).check) {
|
|
37
|
-
detectionResult.indicators.push("Rate Limiting");
|
|
38
|
-
}
|
|
39
|
-
if (await customBotDetector(ctx)) {
|
|
40
|
-
detectionResult.indicators.push("Custom Detector");
|
|
41
|
-
}
|
|
42
|
-
detectionResult.isBot = detectionResult.indicators.length > 0;
|
|
43
|
-
if (detectionResult.indicators.length > 1) {
|
|
44
|
-
detectionResult.reason = "Multiple Indicators";
|
|
45
|
-
const confidence = Math.min(0.3 * detectionResult.indicators.length, 1);
|
|
46
|
-
detectionResult.isBot = confidence >= confidenceThreshold;
|
|
47
|
-
}
|
|
48
|
-
else if (detectionResult.indicators.length === 1) {
|
|
49
|
-
detectionResult.reason = detectionResult.indicators[0];
|
|
50
|
-
}
|
|
51
|
-
if (detectionResult.isBot) {
|
|
52
|
-
config_js_1.GlobalConfig.debugging.warn(`Bot detected: ${detectionResult.reason}`, {
|
|
53
|
-
ip: remoteAddress,
|
|
54
|
-
userAgent,
|
|
55
|
-
indicators: detectionResult.indicators,
|
|
56
|
-
});
|
|
57
|
-
if (onBotDetected === "block") {
|
|
58
|
-
return customBlockedResponse(ctx, detectionResult);
|
|
59
|
-
}
|
|
60
|
-
else if (typeof onBotDetected === "function") {
|
|
61
|
-
return onBotDetected(ctx, detectionResult);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
return await next();
|
|
65
|
-
};
|
|
66
|
-
};
|
|
67
|
-
exports.detectBot = detectBot;
|
|
68
|
-
function createRateLimitDefaultStorage() {
|
|
69
|
-
const store = new Map();
|
|
70
|
-
return {
|
|
71
|
-
get: (key) => store.get(key),
|
|
72
|
-
set: (key, value) => store.set(key, value),
|
|
73
|
-
clearExpired: () => {
|
|
74
|
-
const now = Date.now();
|
|
75
|
-
for (const [key, entry] of store.entries()) {
|
|
76
|
-
if (now >= entry.resetTime) {
|
|
77
|
-
store.delete(key);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
},
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
function isRateLimit(ctx, key, store, maxRequests, windowMs) {
|
|
84
|
-
store.clearExpired();
|
|
85
|
-
const now = Date.now();
|
|
86
|
-
let entry = store.get(key) || { count: 0, resetTime: now + windowMs };
|
|
87
|
-
if (now < entry.resetTime) {
|
|
88
|
-
entry.count++;
|
|
89
|
-
if (entry.count > maxRequests) {
|
|
90
|
-
return {
|
|
91
|
-
check: true,
|
|
92
|
-
entry: entry,
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
else {
|
|
97
|
-
entry = { count: 1, resetTime: now + windowMs };
|
|
98
|
-
}
|
|
99
|
-
store.set(key, entry);
|
|
100
|
-
return {
|
|
101
|
-
check: false,
|
|
102
|
-
entry: entry,
|
|
103
|
-
};
|
|
104
|
-
}
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.detectLocale = void 0;
|
|
4
|
-
const config_js_1 = require("../core/config.js");
|
|
5
|
-
const detectLocale = (options) => {
|
|
6
|
-
const { supportedLocales, defaultLocale = "en", queryKeyLocale = "lang", cookieKeyLocale = "locale", localeContextKey = "locale", customLocaleDetector, } = options;
|
|
7
|
-
return async function detectLocale(ctx, next) {
|
|
8
|
-
let detectedLocale;
|
|
9
|
-
const queryLocale = ctx.req.query[queryKeyLocale];
|
|
10
|
-
if (queryLocale && supportedLocales.includes(queryLocale)) {
|
|
11
|
-
detectedLocale = queryLocale;
|
|
12
|
-
}
|
|
13
|
-
if (!detectedLocale) {
|
|
14
|
-
const cookieLocale = ctx.cookies.get(cookieKeyLocale);
|
|
15
|
-
if (cookieLocale && supportedLocales.includes(cookieLocale)) {
|
|
16
|
-
detectedLocale = cookieLocale;
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
if (!detectedLocale) {
|
|
20
|
-
const acceptLanguage = ctx.req.headers.get("accept-language");
|
|
21
|
-
if (acceptLanguage) {
|
|
22
|
-
const preferredLocales = acceptLanguage
|
|
23
|
-
.split(",")
|
|
24
|
-
.map((lang) => lang.split(";")[0].trim())
|
|
25
|
-
.filter((lang) => supportedLocales.includes(lang));
|
|
26
|
-
detectedLocale = preferredLocales[0];
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
if (!detectedLocale && customLocaleDetector) {
|
|
30
|
-
const customLocale = customLocaleDetector(ctx);
|
|
31
|
-
if (customLocale && supportedLocales.includes(customLocale)) {
|
|
32
|
-
detectedLocale = customLocale;
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
if (!detectedLocale) {
|
|
36
|
-
detectedLocale = defaultLocale;
|
|
37
|
-
}
|
|
38
|
-
ctx[localeContextKey] = detectedLocale;
|
|
39
|
-
config_js_1.GlobalConfig.debugging.success(`Detected locale: ${detectedLocale}`);
|
|
40
|
-
return await next();
|
|
41
|
-
};
|
|
42
|
-
};
|
|
43
|
-
exports.detectLocale = detectLocale;
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.lazyLoadModules = void 0;
|
|
4
|
-
const config_js_1 = require("../core/config.js");
|
|
5
|
-
const lazyLoadModules = (options) => {
|
|
6
|
-
const { moduleKey = (ctx) => ctx.req.params[queryKeyModule] || ctx.req.query[queryKeyModule], getModuleLoader, queryKeyModule = "module", moduleContextKey = "module", cacheTTL = 3600000, enableCache = true, cacheStorage, lifecycleHooks = {}, validateModule, } = options;
|
|
7
|
-
let storage = cacheStorage;
|
|
8
|
-
if (enableCache && !cacheStorage) {
|
|
9
|
-
storage = new Map();
|
|
10
|
-
}
|
|
11
|
-
return async function lazyLoadModules(ctx, next) {
|
|
12
|
-
let moduleName = moduleKey(ctx) ||
|
|
13
|
-
ctx.req.params[queryKeyModule] ||
|
|
14
|
-
ctx.req.query[queryKeyModule];
|
|
15
|
-
if (!moduleName) {
|
|
16
|
-
config_js_1.GlobalConfig.debugging.warn("No module specified for lazy loading.");
|
|
17
|
-
return await next();
|
|
18
|
-
}
|
|
19
|
-
try {
|
|
20
|
-
if (enableCache) {
|
|
21
|
-
const cached = storage.get(moduleName);
|
|
22
|
-
if (cached) {
|
|
23
|
-
if (cached.expiresAt < Date.now()) {
|
|
24
|
-
storage.delete(moduleName);
|
|
25
|
-
}
|
|
26
|
-
else {
|
|
27
|
-
config_js_1.GlobalConfig.debugging.info(`Using cached module: ${moduleName}`);
|
|
28
|
-
ctx[moduleContextKey] = cached?.module;
|
|
29
|
-
lifecycleHooks.onCacheHit?.(moduleName, ctx[moduleContextKey], ctx);
|
|
30
|
-
lifecycleHooks.onComplete?.(moduleName, ctx[moduleContextKey], ctx);
|
|
31
|
-
return await next();
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
if (!getModuleLoader) {
|
|
36
|
-
throw new Error(`No module loader found for module: ${moduleName}`);
|
|
37
|
-
}
|
|
38
|
-
const moduleLoader = await getModuleLoader(ctx);
|
|
39
|
-
if (!moduleLoader) {
|
|
40
|
-
throw new Error(`No loader found for module: ${moduleName}`);
|
|
41
|
-
}
|
|
42
|
-
lifecycleHooks.onLoad?.(moduleName, ctx);
|
|
43
|
-
const module = await moduleLoader();
|
|
44
|
-
if (validateModule && !validateModule(module)) {
|
|
45
|
-
throw new Error(`Module validation failed for: ${moduleName}`);
|
|
46
|
-
}
|
|
47
|
-
if (enableCache) {
|
|
48
|
-
storage.set(moduleName, {
|
|
49
|
-
module,
|
|
50
|
-
expiresAt: Date.now() + cacheTTL,
|
|
51
|
-
});
|
|
52
|
-
lifecycleHooks.onCacheSet?.(moduleName, module, ctx);
|
|
53
|
-
}
|
|
54
|
-
ctx[moduleContextKey] = module;
|
|
55
|
-
if (module.init && typeof module.init === "function") {
|
|
56
|
-
const initResult = await module.init(ctx);
|
|
57
|
-
if (initResult) {
|
|
58
|
-
return initResult;
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
lifecycleHooks.onComplete?.(moduleName, module, ctx);
|
|
62
|
-
config_js_1.GlobalConfig.debugging.success(`Successfully loaded module: ${moduleName}`);
|
|
63
|
-
}
|
|
64
|
-
catch (error) {
|
|
65
|
-
config_js_1.GlobalConfig.debugging.error(`Error loading module: ${moduleName}`, error);
|
|
66
|
-
lifecycleHooks.onError?.(moduleName, error, ctx);
|
|
67
|
-
ctx.setStatus = 500;
|
|
68
|
-
throw error;
|
|
69
|
-
}
|
|
70
|
-
return await next();
|
|
71
|
-
};
|
|
72
|
-
};
|
|
73
|
-
exports.lazyLoadModules = lazyLoadModules;
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.rateLimiter = void 0;
|
|
4
|
-
const detectBot_js_1 = require("./detectBot.js");
|
|
5
|
-
const rateLimiter = (options) => {
|
|
6
|
-
const { maxRequests, windowMs, keyGenerator = (ctx) => `${ctx.req.remoteAddress.address}:${ctx.req.remoteAddress.port}`, storage = (0, detectBot_js_1.createRateLimitDefaultStorage)(), onError = (ctx, retryAfter, error) => {
|
|
7
|
-
ctx.setStatus = 429;
|
|
8
|
-
throw new Error(`Rate limit exceeded. Try again in ${retryAfter} seconds.`);
|
|
9
|
-
}, } = options;
|
|
10
|
-
return async function rateLimiter(ctx, next) {
|
|
11
|
-
const key = keyGenerator(ctx);
|
|
12
|
-
const { check, entry } = (0, detectBot_js_1.isRateLimit)(ctx, key, storage, maxRequests, windowMs);
|
|
13
|
-
if (check) {
|
|
14
|
-
const retryAfter = Math.ceil((entry.resetTime - Date.now()) / 1000);
|
|
15
|
-
ctx.headers.set("Retry-After", retryAfter.toString());
|
|
16
|
-
return onError(ctx, retryAfter, new Error(`Rate limit exceeded. Retry after ${retryAfter} seconds.`));
|
|
17
|
-
}
|
|
18
|
-
ctx.headers.set("X-RateLimit-Limit", maxRequests.toString());
|
|
19
|
-
ctx.headers.set("X-RateLimit-Remaining", (maxRequests - entry.count).toString());
|
|
20
|
-
ctx.headers.set("X-RateLimit-Reset", entry.resetTime.toString());
|
|
21
|
-
return await next();
|
|
22
|
-
};
|
|
23
|
-
};
|
|
24
|
-
exports.rateLimiter = rateLimiter;
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.requestTimeout = void 0;
|
|
4
|
-
const config_js_1 = require("../core/config.js");
|
|
5
|
-
const requestTimeout = (options) => {
|
|
6
|
-
const { getTimeout, onTimeout = (ctx) => {
|
|
7
|
-
ctx.setStatus = 504;
|
|
8
|
-
ctx.body = { error: "Request timed out." };
|
|
9
|
-
}, logTimeoutEvent = (ctx, error) => {
|
|
10
|
-
config_js_1.GlobalConfig.debugging.warn(`[TIMEOUT] ${error.message}: ${ctx.method} ${ctx.path}`);
|
|
11
|
-
}, cleanup = () => {
|
|
12
|
-
}, } = options;
|
|
13
|
-
return async function requestTimeout(ctx, next) {
|
|
14
|
-
let timeoutId = null;
|
|
15
|
-
try {
|
|
16
|
-
const timeout = getTimeout(ctx);
|
|
17
|
-
const timeoutPromise = new Promise((_, reject) => {
|
|
18
|
-
timeoutId = setTimeout(() => {
|
|
19
|
-
const timeoutError = new Error("Request timed out.");
|
|
20
|
-
logTimeoutEvent(ctx, timeoutError);
|
|
21
|
-
reject(timeoutError);
|
|
22
|
-
}, timeout);
|
|
23
|
-
});
|
|
24
|
-
return await Promise.race([next(), timeoutPromise]);
|
|
25
|
-
}
|
|
26
|
-
catch (error) {
|
|
27
|
-
if (error.message === "Request timed out.") {
|
|
28
|
-
onTimeout(ctx, error);
|
|
29
|
-
}
|
|
30
|
-
else {
|
|
31
|
-
throw error;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
finally {
|
|
35
|
-
if (timeoutId) {
|
|
36
|
-
clearTimeout(timeoutId);
|
|
37
|
-
}
|
|
38
|
-
cleanup(ctx);
|
|
39
|
-
}
|
|
40
|
-
};
|
|
41
|
-
};
|
|
42
|
-
exports.requestTimeout = requestTimeout;
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.sanitizeHeaders = void 0;
|
|
4
|
-
const config_js_1 = require("../core/config.js");
|
|
5
|
-
const sanitizeHeaders = (options = {}) => {
|
|
6
|
-
const { whitelist = [], blacklist = [], normalizeKeys = true, allowUnsafeCharacters = false, } = options;
|
|
7
|
-
return async function sanitizeHeaders(ctx, next) {
|
|
8
|
-
const sanitizedHeaders = new Map();
|
|
9
|
-
for (const [key, values] of ctx.headers.entries()) {
|
|
10
|
-
if (!Array.isArray(values) || values.length === 0) {
|
|
11
|
-
continue;
|
|
12
|
-
}
|
|
13
|
-
const normalizedKey = normalizeKeys ? key.toLowerCase() : key;
|
|
14
|
-
if (whitelist.length > 0 &&
|
|
15
|
-
!whitelist.some((r) => r?.toLowerCase() === normalizedKey)) {
|
|
16
|
-
config_js_1.GlobalConfig.debugging.warn(`🚫 Header "${normalizedKey}" not in whitelist - removed`);
|
|
17
|
-
continue;
|
|
18
|
-
}
|
|
19
|
-
if (blacklist.some((r) => r.toLowerCase() === normalizedKey)) {
|
|
20
|
-
config_js_1.GlobalConfig.debugging.warn(`🚫 Header "${normalizedKey}" in blacklist - removed`);
|
|
21
|
-
continue;
|
|
22
|
-
}
|
|
23
|
-
if (!isValidHeaderName(normalizedKey)) {
|
|
24
|
-
config_js_1.GlobalConfig.debugging.warn(`⚠️ Invalid header name: "${normalizedKey}" - removed`);
|
|
25
|
-
continue;
|
|
26
|
-
}
|
|
27
|
-
const sanitizedValues = values
|
|
28
|
-
.map((value) => sanitizeHeaderValue(value, allowUnsafeCharacters))
|
|
29
|
-
.filter(Boolean);
|
|
30
|
-
if (sanitizedValues.length === 0) {
|
|
31
|
-
config_js_1.GlobalConfig.debugging.warn(`⚠️ All values for "${normalizedKey}" invalid - removed`);
|
|
32
|
-
continue;
|
|
33
|
-
}
|
|
34
|
-
sanitizedHeaders.set(normalizedKey, sanitizedValues?.join(", "));
|
|
35
|
-
}
|
|
36
|
-
ctx.headers = new Headers(sanitizedHeaders);
|
|
37
|
-
return await next();
|
|
38
|
-
};
|
|
39
|
-
};
|
|
40
|
-
exports.sanitizeHeaders = sanitizeHeaders;
|
|
41
|
-
const isValidHeaderName = (name) => {
|
|
42
|
-
const HEADER_NAME_REGEX = /^[a-zA-Z0-9\-_]+$/;
|
|
43
|
-
return HEADER_NAME_REGEX.test(name);
|
|
44
|
-
};
|
|
45
|
-
const sanitizeHeaderValue = (value, allowUnsafeCharacters) => {
|
|
46
|
-
let sanitized = value.trim();
|
|
47
|
-
if (!allowUnsafeCharacters) {
|
|
48
|
-
sanitized = sanitized.replace(/[\x00-\x1F\x7F]/g, "");
|
|
49
|
-
}
|
|
50
|
-
return sanitized;
|
|
51
|
-
};
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.secureHeaders = void 0;
|
|
4
|
-
const secureHeaders = (options = {}) => {
|
|
5
|
-
return async function secureHeaders(ctx, next) {
|
|
6
|
-
const resolveValue = (value) => {
|
|
7
|
-
return typeof value === "function" ? value(ctx) : value;
|
|
8
|
-
};
|
|
9
|
-
const contentSecurityPolicy = resolveValue(options.contentSecurityPolicy) ||
|
|
10
|
-
"default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline';";
|
|
11
|
-
const frameGuard = resolveValue(options.frameGuard) ?? true;
|
|
12
|
-
const hsts = resolveValue(options.hsts) ?? true;
|
|
13
|
-
const xssProtection = resolveValue(options.xssProtection) ?? true;
|
|
14
|
-
const noSniff = resolveValue(options.noSniff) ?? true;
|
|
15
|
-
const referrerPolicy = resolveValue(options.referrerPolicy) || "no-referrer";
|
|
16
|
-
const permissionsPolicy = resolveValue(options.permissionsPolicy) ||
|
|
17
|
-
"geolocation=(), microphone=(), camera=()";
|
|
18
|
-
if (contentSecurityPolicy) {
|
|
19
|
-
ctx.headers.set("Content-Security-Policy", contentSecurityPolicy);
|
|
20
|
-
}
|
|
21
|
-
if (frameGuard) {
|
|
22
|
-
ctx.headers.set("X-Frame-Options", "DENY");
|
|
23
|
-
}
|
|
24
|
-
if (hsts) {
|
|
25
|
-
ctx.headers.set("Strict-Transport-Security", "max-age=63072000; includeSubDomains");
|
|
26
|
-
}
|
|
27
|
-
if (xssProtection) {
|
|
28
|
-
ctx.headers.set("X-XSS-Protection", "1; mode=block");
|
|
29
|
-
}
|
|
30
|
-
if (noSniff) {
|
|
31
|
-
ctx.headers.set("X-Content-Type-Options", "nosniff");
|
|
32
|
-
}
|
|
33
|
-
if (referrerPolicy) {
|
|
34
|
-
ctx.headers.set("Referrer-Policy", referrerPolicy);
|
|
35
|
-
}
|
|
36
|
-
if (permissionsPolicy) {
|
|
37
|
-
ctx.headers.set("Permissions-Policy", permissionsPolicy);
|
|
38
|
-
}
|
|
39
|
-
return await next();
|
|
40
|
-
};
|
|
41
|
-
};
|
|
42
|
-
exports.secureHeaders = secureHeaders;
|