befly 3.17.11 → 3.17.13

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/hooks/auth.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { toSessionTtlSeconds } from "../utils/toSessionTtlSeconds.js";
2
2
 
3
3
  export default {
4
- deps: ["cors"],
4
+ deps: [],
5
5
  handler: async (befly, ctx) => {
6
6
  const authHeader = ctx.req.headers.get("authorization");
7
7
  const ttlSeconds = toSessionTtlSeconds(befly.config?.session?.expireDays);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "befly",
3
- "version": "3.17.11",
4
- "gitHead": "d6f49d8da98a65d26d4d4866ff3a3671e178c1fb",
3
+ "version": "3.17.13",
4
+ "gitHead": "1259303ee55a5aa686be914e961dc42767c0136b",
5
5
  "private": false,
6
6
  "description": "Befly - 为 Bun 专属打造的 JavaScript API 接口框架核心引擎",
7
7
  "keywords": [
package/router/api.js CHANGED
@@ -5,6 +5,7 @@
5
5
 
6
6
  import { Logger } from "../lib/logger.js";
7
7
  // 相对导入
8
+ import { setCorsOptions } from "../utils/cors.js";
8
9
  import { getClientIp } from "../utils/getClientIp.js";
9
10
  import { FinalResponse } from "../utils/response.js";
10
11
  import { genShortId } from "../utils/util.js";
@@ -19,17 +20,41 @@ export function apiHandler(apis, hooks, context) {
19
20
  return async (req, server) => {
20
21
  // 1. 生成请求 ID
21
22
  const requestId = genShortId();
23
+ const now = Date.now();
24
+
25
+ const corsHeaders = setCorsOptions(req, context.config?.cors || {});
26
+ corsHeaders["X-Request-ID"] = requestId;
22
27
 
23
- // 2. 创建请求上下文
28
+ // 2. OPTIONS 预检请求:直接返回,不走 hooks,不打日志
29
+ if (req.method === "OPTIONS") {
30
+ return new Response(null, {
31
+ status: 204,
32
+ headers: corsHeaders
33
+ });
34
+ }
35
+
36
+ // 3. 创建请求上下文
24
37
  const url = new URL(req.url);
25
38
  // 只用接口路径做存在性判断与匹配:不要把 method 拼进 key
26
39
  // 说明:apisMap 的 key 来源于 scanFiles/loadApis 生成的 path(例如 /api/core/xxx)
27
40
 
28
41
  const clientIp = getClientIp(req, server);
29
42
 
30
- const now = Date.now();
43
+ const apiData = apis[url.pathname];
44
+ if (!apiData) {
45
+ return Response.json(
46
+ {
47
+ code: 1,
48
+ msg: "接口不存在"
49
+ },
50
+ {
51
+ headers: corsHeaders
52
+ }
53
+ );
54
+ }
31
55
 
32
56
  const ctx = {
57
+ // 请求的参数
33
58
  method: req.method,
34
59
  body: {},
35
60
  req: req,
@@ -37,24 +62,19 @@ export function apiHandler(apis, hooks, context) {
37
62
  ip: clientIp,
38
63
  headers: req.headers,
39
64
  requestId: requestId,
40
- corsHeaders: {
41
- "X-Request-ID": requestId
42
- }
65
+ corsHeaders: corsHeaders,
66
+ // 接口的参数
67
+ apiPath: apiData.apiPath,
68
+ apiName: apiData.name,
69
+ filePath: apiData.filePath,
70
+ handler: apiData.handler,
71
+ method: apiData.method,
72
+ body: apiData.body,
73
+ auth: apiData.auth,
74
+ fields: apiData.fields,
75
+ required: apiData.required
43
76
  };
44
77
 
45
- const apiData = apis[url.pathname];
46
- if (apiData) {
47
- ctx.apiPath = apiData.apiPath;
48
- ctx.apiName = apiData.name;
49
- ctx.filePath = apiData.filePath;
50
- ctx.handler = apiData.handler;
51
- ctx.method = apiData.method;
52
- ctx.body = apiData.body;
53
- ctx.auth = apiData.auth;
54
- ctx.fields = apiData.fields;
55
- ctx.required = apiData.required;
56
- }
57
-
58
78
  try {
59
79
  // 4. 串联执行所有钩子
60
80
  for (const hook of hooks) {
@@ -67,49 +87,33 @@ export function apiHandler(apis, hooks, context) {
67
87
  }
68
88
 
69
89
  // hooks 全部通过后记录请求日志(拦截请求仅由 ErrorResponse 记录)
70
- if (ctx.handler && req.method !== "OPTIONS") {
71
- const logData = {
72
- event: "request",
73
- requestId: requestId,
74
- method: req.method,
75
- apiPath: ctx.apiPath,
76
- apiName: ctx.apiName,
77
- ip: clientIp,
78
- now: now,
79
- userId: ctx.userId,
80
- nickname: ctx.nickname,
81
- roleCode: ctx.roleCode,
82
- roleType: ctx.roleType
83
- };
84
-
85
- if (ctx.body && Object.keys(ctx.body).length > 0) {
86
- logData["body"] = ctx.body;
87
- }
90
+ const logData = {
91
+ event: "request",
92
+ requestId: requestId,
93
+ method: req.method,
94
+ apiPath: ctx.apiPath,
95
+ apiName: ctx.apiName,
96
+ ip: clientIp,
97
+ now: now,
98
+ userId: ctx.userId,
99
+ nickname: ctx.nickname,
100
+ roleCode: ctx.roleCode,
101
+ roleType: ctx.roleType
102
+ };
88
103
 
89
- Logger.info("请求", logData);
104
+ if (ctx.body && Object.keys(ctx.body).length > 0) {
105
+ logData["body"] = ctx.body;
90
106
  }
91
107
 
108
+ Logger.info("请求", logData);
109
+
92
110
  // 5. 执行 API handler
93
- if (!ctx.handler) {
94
- if (req.method !== "OPTIONS") {
95
- ctx.response = Response.json(
96
- {
97
- code: 1,
98
- msg: "接口不存在"
99
- },
100
- {
101
- headers: ctx.corsHeaders
102
- }
103
- );
104
- }
105
- } else if (ctx.handler) {
106
- const result = await ctx.handler(context, ctx);
111
+ const result = await ctx.handler(context, ctx);
107
112
 
108
- if (result instanceof Response) {
109
- ctx.response = result;
110
- } else {
111
- ctx.result = result;
112
- }
113
+ if (result instanceof Response) {
114
+ ctx.response = result;
115
+ } else {
116
+ ctx.result = result;
113
117
  }
114
118
 
115
119
  // 7. 返回响应(自动处理 response/result/日志)
package/utils/cors.js CHANGED
@@ -5,13 +5,24 @@
5
5
  * @returns CORS 响应头对象
6
6
  */
7
7
  export function setCorsOptions(req, config = {}) {
8
- const origin = config.origin || "*";
8
+ const defaultConfig = {
9
+ origin: "*",
10
+ methods: "GET, POST, OPTIONS",
11
+ allowedHeaders: "Content-Type, Authorization, authorization, token",
12
+ exposedHeaders: "Content-Range, X-Content-Range, Authorization, authorization, token",
13
+ maxAge: 86400,
14
+ credentials: "true"
15
+ };
16
+
17
+ const merged = Object.assign({}, defaultConfig, config || {});
18
+ const origin = merged.origin;
19
+
9
20
  return {
10
21
  "Access-Control-Allow-Origin": origin === "*" ? req.headers.get("origin") || "*" : origin,
11
- "Access-Control-Allow-Methods": config.methods || "GET, POST, PUT, DELETE, OPTIONS",
12
- "Access-Control-Allow-Headers": config.allowedHeaders || "Content-Type, Authorization, authorization, token",
13
- "Access-Control-Expose-Headers": config.exposedHeaders || "Content-Range, X-Content-Range, Authorization, authorization, token",
14
- "Access-Control-Max-Age": String(config.maxAge || 86400),
15
- "Access-Control-Allow-Credentials": config.credentials || "true"
22
+ "Access-Control-Allow-Methods": merged.methods,
23
+ "Access-Control-Allow-Headers": merged.allowedHeaders,
24
+ "Access-Control-Expose-Headers": merged.exposedHeaders,
25
+ "Access-Control-Max-Age": String(merged.maxAge),
26
+ "Access-Control-Allow-Credentials": merged.credentials
16
27
  };
17
28
  }
package/utils/response.js CHANGED
@@ -9,17 +9,17 @@ import { isString } from "./is.js";
9
9
  * @param code - 错误码,默认 1
10
10
  * @param data - 附加数据,默认 null
11
11
  * @param detail - 详细信息,用于标记具体提示位置,默认 null
12
- * @param reasonCode - 拦截原因标识(用于统计/聚合),默认 null
12
+ * @param reason - 拦截原因标识(用于统计/聚合),默认 null
13
13
  * @returns Response 对象
14
14
  */
15
- export function ErrorResponse(ctx, msg, code = 1, data = null, detail = null, reasonCode = null) {
15
+ export function ErrorResponse(ctx, msg, code = 1, data = null, detail = null, reason = null) {
16
16
  // 记录拦截日志
17
17
  if (ctx.requestId) {
18
18
  // requestId/apiPath/user/duration 等字段由调用方显式写入日志上下文,避免在 msg 中重复拼接
19
19
  Logger.info("请求已拦截", {
20
20
  event: "request_blocked",
21
- reason: msg,
22
- reasonCode: reasonCode,
21
+ msg: msg,
22
+ reason: reason,
23
23
  code: code,
24
24
  detail: detail
25
25
  });
@@ -30,7 +30,8 @@ export function ErrorResponse(ctx, msg, code = 1, data = null, detail = null, re
30
30
  code: code,
31
31
  msg: msg,
32
32
  data: data,
33
- detail: detail
33
+ detail: detail,
34
+ reason: reason
34
35
  },
35
36
  {
36
37
  headers: ctx.corsHeaders
package/hooks/cors.js DELETED
@@ -1,39 +0,0 @@
1
- // 相对导入
2
- import { setCorsOptions } from "../utils/cors.js";
3
-
4
- /**
5
- * CORS 跨域处理钩子
6
- * 设置跨域响应头并处理 OPTIONS 预检请求
7
- */
8
- export default {
9
- deps: [],
10
- handler: async (befly, ctx) => {
11
- const req = ctx.req;
12
-
13
- // 合并默认配置和用户配置
14
- const defaultConfig = {
15
- origin: "*",
16
- methods: "GET, POST, OPTIONS",
17
- allowedHeaders: "Content-Type, Authorization, authorization, token",
18
- exposedHeaders: "Content-Range, X-Content-Range, Authorization, authorization, token",
19
- maxAge: 86400,
20
- credentials: "true"
21
- };
22
-
23
- const corsConfig = Object.assign({}, defaultConfig, befly.config && befly.config.cors ? befly.config.cors : {});
24
-
25
- // 设置 CORS 响应头
26
- const headers = setCorsOptions(req, corsConfig);
27
-
28
- ctx.corsHeaders = headers;
29
-
30
- // 处理 OPTIONS 预检请求
31
- if (req.method === "OPTIONS") {
32
- ctx.response = new Response(null, {
33
- status: 204,
34
- headers: headers
35
- });
36
- return;
37
- }
38
- }
39
- };