befly 3.10.18 → 3.11.1

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.
Files changed (223) hide show
  1. package/README.md +83 -307
  2. package/dist/befly.config.d.ts +7 -0
  3. package/{befly.config.ts → dist/befly.config.js} +11 -36
  4. package/dist/befly.js +15621 -0
  5. package/dist/befly.min.js +21 -0
  6. package/dist/checks/checkApi.d.ts +1 -0
  7. package/{checks/checkApi.ts → dist/checks/checkApi.js} +12 -30
  8. package/dist/checks/checkHook.d.ts +1 -0
  9. package/dist/checks/checkHook.js +86 -0
  10. package/dist/checks/checkMenu.d.ts +7 -0
  11. package/{checks/checkMenu.ts → dist/checks/checkMenu.js} +18 -53
  12. package/dist/checks/checkPlugin.d.ts +1 -0
  13. package/dist/checks/checkPlugin.js +86 -0
  14. package/dist/checks/checkTable.d.ts +6 -0
  15. package/{checks/checkTable.ts → dist/checks/checkTable.js} +17 -41
  16. package/dist/configs/presetFields.d.ts +4 -0
  17. package/{configs/presetFields.ts → dist/configs/presetFields.js} +1 -1
  18. package/dist/configs/presetRegexp.d.ts +145 -0
  19. package/{utils/regex.ts → dist/configs/presetRegexp.js} +8 -31
  20. package/dist/hooks/auth.d.ts +7 -0
  21. package/{hooks/auth.ts → dist/hooks/auth.js} +8 -10
  22. package/dist/hooks/cors.d.ts +11 -0
  23. package/{hooks/cors.ts → dist/hooks/cors.js} +5 -13
  24. package/dist/hooks/parser.d.ts +14 -0
  25. package/{hooks/parser.ts → dist/hooks/parser.js} +31 -45
  26. package/dist/hooks/permission.d.ts +14 -0
  27. package/{hooks/permission.ts → dist/hooks/permission.js} +16 -25
  28. package/dist/hooks/validator.d.ts +11 -0
  29. package/{hooks/validator.ts → dist/hooks/validator.js} +9 -14
  30. package/dist/index.d.ts +26 -0
  31. package/{main.ts → dist/index.js} +61 -100
  32. package/dist/lib/asyncContext.d.ts +21 -0
  33. package/dist/lib/asyncContext.js +27 -0
  34. package/dist/lib/cacheHelper.d.ts +95 -0
  35. package/{lib/cacheHelper.ts → dist/lib/cacheHelper.js} +45 -105
  36. package/dist/lib/cacheKeys.d.ts +23 -0
  37. package/{lib/cacheKeys.ts → dist/lib/cacheKeys.js} +5 -10
  38. package/dist/lib/cipher.d.ts +153 -0
  39. package/{lib/cipher.ts → dist/lib/cipher.js} +23 -44
  40. package/dist/lib/connect.d.ts +91 -0
  41. package/{lib/connect.ts → dist/lib/connect.js} +47 -88
  42. package/dist/lib/dbDialect.d.ts +87 -0
  43. package/{lib/dbDialect.ts → dist/lib/dbDialect.js} +32 -112
  44. package/dist/lib/dbHelper.d.ts +204 -0
  45. package/{lib/dbHelper.ts → dist/lib/dbHelper.js} +82 -241
  46. package/dist/lib/dbUtils.d.ts +68 -0
  47. package/{lib/dbUtils.ts → dist/lib/dbUtils.js} +51 -126
  48. package/dist/lib/jwt.d.ts +13 -0
  49. package/{lib/jwt.ts → dist/lib/jwt.js} +11 -32
  50. package/dist/lib/logger.d.ts +42 -0
  51. package/dist/lib/logger.js +1144 -0
  52. package/dist/lib/redisHelper.d.ts +185 -0
  53. package/{lib/redisHelper.ts → dist/lib/redisHelper.js} +97 -141
  54. package/dist/lib/sqlBuilder.d.ts +160 -0
  55. package/{lib/sqlBuilder.ts → dist/lib/sqlBuilder.js} +132 -278
  56. package/dist/lib/sqlCheck.d.ts +23 -0
  57. package/{lib/sqlCheck.ts → dist/lib/sqlCheck.js} +24 -41
  58. package/dist/lib/validator.d.ts +45 -0
  59. package/{lib/validator.ts → dist/lib/validator.js} +44 -61
  60. package/dist/loader/loadApis.d.ts +12 -0
  61. package/{loader/loadApis.ts → dist/loader/loadApis.js} +10 -20
  62. package/dist/loader/loadHooks.d.ts +7 -0
  63. package/dist/loader/loadHooks.js +35 -0
  64. package/dist/loader/loadPlugins.d.ts +8 -0
  65. package/{loader/loadPlugins.ts → dist/loader/loadPlugins.js} +14 -26
  66. package/dist/paths.d.ts +93 -0
  67. package/{paths.ts → dist/paths.js} +6 -19
  68. package/dist/plugins/cache.d.ts +16 -0
  69. package/{plugins/cache.ts → dist/plugins/cache.js} +7 -12
  70. package/dist/plugins/cipher.d.ts +12 -0
  71. package/{plugins/cipher.ts → dist/plugins/cipher.js} +4 -6
  72. package/dist/plugins/config.d.ts +12 -0
  73. package/dist/plugins/config.js +8 -0
  74. package/dist/plugins/db.d.ts +16 -0
  75. package/{plugins/db.ts → dist/plugins/db.js} +11 -17
  76. package/dist/plugins/jwt.d.ts +12 -0
  77. package/dist/plugins/jwt.js +12 -0
  78. package/dist/plugins/logger.d.ts +32 -0
  79. package/{plugins/logger.ts → dist/plugins/logger.js} +5 -8
  80. package/dist/plugins/redis.d.ts +16 -0
  81. package/{plugins/redis.ts → dist/plugins/redis.js} +9 -12
  82. package/dist/plugins/tool.d.ts +81 -0
  83. package/{plugins/tool.ts → dist/plugins/tool.js} +9 -30
  84. package/dist/router/api.d.ts +14 -0
  85. package/dist/router/api.js +107 -0
  86. package/dist/router/static.d.ts +9 -0
  87. package/{router/static.ts → dist/router/static.js} +20 -34
  88. package/dist/scripts/ensureDist.d.ts +1 -0
  89. package/dist/scripts/ensureDist.js +296 -0
  90. package/dist/sync/syncApi.d.ts +3 -0
  91. package/{sync/syncApi.ts → dist/sync/syncApi.js} +35 -55
  92. package/dist/sync/syncCache.d.ts +2 -0
  93. package/{sync/syncCache.ts → dist/sync/syncCache.js} +1 -6
  94. package/dist/sync/syncDev.d.ts +6 -0
  95. package/{sync/syncDev.ts → dist/sync/syncDev.js} +29 -62
  96. package/dist/sync/syncMenu.d.ts +14 -0
  97. package/{sync/syncMenu.ts → dist/sync/syncMenu.js} +65 -125
  98. package/dist/sync/syncTable.d.ts +151 -0
  99. package/{sync/syncTable.ts → dist/sync/syncTable.js} +172 -379
  100. package/{types → dist/types}/api.d.ts +12 -51
  101. package/dist/types/api.js +4 -0
  102. package/{types → dist/types}/befly.d.ts +32 -227
  103. package/dist/types/befly.js +4 -0
  104. package/{types → dist/types}/cache.d.ts +7 -15
  105. package/dist/types/cache.js +4 -0
  106. package/dist/types/cipher.d.ts +27 -0
  107. package/dist/types/cipher.js +7 -0
  108. package/{types → dist/types}/common.d.ts +8 -33
  109. package/dist/types/common.js +5 -0
  110. package/{types → dist/types}/context.d.ts +3 -5
  111. package/dist/types/context.js +4 -0
  112. package/{types → dist/types}/crypto.d.ts +0 -3
  113. package/dist/types/crypto.js +4 -0
  114. package/dist/types/database.d.ts +138 -0
  115. package/dist/types/database.js +4 -0
  116. package/dist/types/hook.d.ts +17 -0
  117. package/dist/types/hook.js +6 -0
  118. package/dist/types/jwt.d.ts +75 -0
  119. package/dist/types/jwt.js +4 -0
  120. package/dist/types/logger.d.ts +59 -0
  121. package/dist/types/logger.js +6 -0
  122. package/dist/types/plugin.d.ts +16 -0
  123. package/dist/types/plugin.js +6 -0
  124. package/dist/types/redis.d.ts +71 -0
  125. package/dist/types/redis.js +4 -0
  126. package/{types/roleApisCache.ts → dist/types/roleApisCache.d.ts} +0 -2
  127. package/dist/types/roleApisCache.js +8 -0
  128. package/dist/types/sync.d.ts +92 -0
  129. package/dist/types/sync.js +4 -0
  130. package/dist/types/table.d.ts +34 -0
  131. package/dist/types/table.js +4 -0
  132. package/dist/types/validate.d.ts +67 -0
  133. package/dist/types/validate.js +4 -0
  134. package/dist/utils/calcPerfTime.d.ts +4 -0
  135. package/{utils/calcPerfTime.ts → dist/utils/calcPerfTime.js} +3 -3
  136. package/dist/utils/convertBigIntFields.d.ts +11 -0
  137. package/{utils/convertBigIntFields.ts → dist/utils/convertBigIntFields.js} +5 -9
  138. package/dist/utils/cors.d.ts +8 -0
  139. package/{utils/cors.ts → dist/utils/cors.js} +1 -3
  140. package/dist/utils/disableMenusGlob.d.ts +13 -0
  141. package/{utils/disableMenusGlob.ts → dist/utils/disableMenusGlob.js} +9 -29
  142. package/dist/utils/fieldClear.d.ts +11 -0
  143. package/{utils/fieldClear.ts → dist/utils/fieldClear.js} +15 -33
  144. package/dist/utils/getClientIp.d.ts +6 -0
  145. package/{utils/getClientIp.ts → dist/utils/getClientIp.js} +1 -7
  146. package/dist/utils/importDefault.d.ts +1 -0
  147. package/dist/utils/importDefault.js +29 -0
  148. package/dist/utils/isDirentDirectory.d.ts +2 -0
  149. package/{utils/isDirentDirectory.ts → dist/utils/isDirentDirectory.js} +3 -8
  150. package/dist/utils/loadMenuConfigs.d.ts +29 -0
  151. package/{utils/loadMenuConfigs.ts → dist/utils/loadMenuConfigs.js} +66 -52
  152. package/dist/utils/mergeAndConcat.d.ts +7 -0
  153. package/dist/utils/mergeAndConcat.js +72 -0
  154. package/dist/utils/processAtSymbol.d.ts +4 -0
  155. package/{utils/processFields.ts → dist/utils/processAtSymbol.js} +5 -9
  156. package/dist/utils/processInfo.d.ts +24 -0
  157. package/{utils/process.ts → dist/utils/processInfo.js} +2 -18
  158. package/dist/utils/response.d.ts +20 -0
  159. package/{utils/response.ts → dist/utils/response.js} +28 -49
  160. package/dist/utils/scanAddons.d.ts +17 -0
  161. package/{utils/scanAddons.ts → dist/utils/scanAddons.js} +7 -41
  162. package/dist/utils/scanConfig.d.ts +26 -0
  163. package/{utils/scanConfig.ts → dist/utils/scanConfig.js} +28 -66
  164. package/dist/utils/scanCoreBuiltins.d.ts +3 -0
  165. package/dist/utils/scanCoreBuiltins.js +65 -0
  166. package/dist/utils/scanFiles.d.ts +30 -0
  167. package/{utils/scanFiles.ts → dist/utils/scanFiles.js} +44 -71
  168. package/dist/utils/scanSources.d.ts +10 -0
  169. package/dist/utils/scanSources.js +46 -0
  170. package/dist/utils/sortModules.d.ts +28 -0
  171. package/{utils/sortModules.ts → dist/utils/sortModules.js} +26 -66
  172. package/dist/utils/util.d.ts +84 -0
  173. package/dist/utils/util.js +262 -0
  174. package/package.json +26 -34
  175. package/.gitignore +0 -0
  176. package/bunfig.toml +0 -3
  177. package/checks/checkHook.ts +0 -48
  178. package/checks/checkPlugin.ts +0 -48
  179. package/configs/presetRegexp.ts +0 -225
  180. package/docs/README.md +0 -98
  181. package/docs/api/api.md +0 -1921
  182. package/docs/guide/examples.md +0 -926
  183. package/docs/guide/quickstart.md +0 -354
  184. package/docs/hooks/auth.md +0 -38
  185. package/docs/hooks/cors.md +0 -28
  186. package/docs/hooks/hook.md +0 -838
  187. package/docs/hooks/parser.md +0 -19
  188. package/docs/hooks/rateLimit.md +0 -47
  189. package/docs/infra/redis.md +0 -628
  190. package/docs/plugins/cipher.md +0 -61
  191. package/docs/plugins/database.md +0 -189
  192. package/docs/plugins/plugin.md +0 -986
  193. package/docs/reference/addon.md +0 -510
  194. package/docs/reference/config.md +0 -573
  195. package/docs/reference/logger.md +0 -495
  196. package/docs/reference/sync.md +0 -478
  197. package/docs/reference/table.md +0 -763
  198. package/docs/reference/validator.md +0 -620
  199. package/lib/asyncContext.ts +0 -43
  200. package/lib/logger.ts +0 -811
  201. package/loader/loadHooks.ts +0 -51
  202. package/plugins/config.ts +0 -13
  203. package/plugins/jwt.ts +0 -15
  204. package/router/api.ts +0 -130
  205. package/tsconfig.json +0 -8
  206. package/types/database.d.ts +0 -541
  207. package/types/hook.d.ts +0 -25
  208. package/types/jwt.d.ts +0 -118
  209. package/types/logger.d.ts +0 -65
  210. package/types/plugin.d.ts +0 -19
  211. package/types/redis.d.ts +0 -83
  212. package/types/sync.d.ts +0 -398
  213. package/types/table.d.ts +0 -216
  214. package/types/validate.d.ts +0 -69
  215. package/utils/arrayKeysToCamel.ts +0 -18
  216. package/utils/configTypes.ts +0 -3
  217. package/utils/genShortId.ts +0 -12
  218. package/utils/importDefault.ts +0 -21
  219. package/utils/keysToCamel.ts +0 -22
  220. package/utils/keysToSnake.ts +0 -22
  221. package/utils/pickFields.ts +0 -19
  222. package/utils/scanSources.ts +0 -64
  223. package/utils/sqlLog.ts +0 -37
@@ -1,838 +0,0 @@
1
- # Hook 钩子开发指南
2
-
3
- > 本文档详细介绍 Befly 框架的 Hook 钩子系统,包括钩子结构、执行顺序、内置钩子及自定义钩子开发。
4
-
5
- ## 目录
6
-
7
- - [Hook 钩子开发指南](#hook-钩子开发指南)
8
- - [目录](#目录)
9
- - [概述](#概述)
10
- - [核心特性](#核心特性)
11
- - [与插件的区别](#与插件的区别)
12
- - [Hook 结构](#hook-结构)
13
- - [基础结构](#基础结构)
14
- - [完整类型定义](#完整类型定义)
15
- - [执行顺序](#执行顺序)
16
- - [order 值规范](#order-值规范)
17
- - [执行流程图](#执行流程图)
18
- - [内置钩子](#内置钩子)
19
- - [cors - 跨域处理](#cors---跨域处理)
20
- - [auth - 身份认证](#auth---身份认证)
21
- - [parser - 参数解析](#parser---参数解析)
22
- - [requestLogger - 请求日志](#requestlogger---请求日志)
23
- - [validator - 参数验证](#validator---参数验证)
24
- - [permission - 权限检查](#permission---权限检查)
25
- - [自定义钩子开发](#自定义钩子开发)
26
- - [基础钩子](#基础钩子)
27
- - [请求拦截钩子](#请求拦截钩子)
28
- - [限流钩子](#限流钩子)
29
- - [审计日志钩子](#审计日志钩子)
30
- - [中断请求](#中断请求)
31
- - [禁用钩子](#禁用钩子)
32
- - [最佳实践](#最佳实践)
33
- - [1. 合理设置 order](#1-合理设置-order)
34
- - [2. 提前检查 ctx.api](#2-提前检查-ctxapi)
35
- - [3. 错误处理](#3-错误处理)
36
- - [4. 避免重复处理](#4-避免重复处理)
37
- - [5. 性能考虑](#5-性能考虑)
38
- - [常见问题](#常见问题)
39
- - [Q1: 钩子执行顺序如何确定?](#q1-钩子执行顺序如何确定)
40
- - [Q2: 钩子可以访问插件吗?](#q2-钩子可以访问插件吗)
41
- - [Q3: 如何在钩子间传递数据?](#q3-如何在钩子间传递数据)
42
- - [Q4: 钩子抛出异常会怎样?](#q4-钩子抛出异常会怎样)
43
- - [Q5: 可以动态修改钩子吗?](#q5-可以动态修改钩子吗)
44
-
45
- ---
46
-
47
- ## 概述
48
-
49
- Befly Hook 系统是请求处理的中间件机制,采用串联模式依次执行。每个 Hook 可以访问请求上下文、修改数据、或提前中断请求。
50
-
51
- ### 核心特性
52
-
53
- - **串联执行**:按 order 顺序依次执行,无 next 调用
54
- - **可中断**:设置 `ctx.response` 可提前中断后续处理
55
- - **上下文共享**:所有 Hook 共享同一个 RequestContext
56
- - **可禁用**:通过配置禁用指定钩子
57
-
58
- ### 与插件的区别
59
-
60
- | 特性 | Hook 钩子 | Plugin 插件 |
61
- | -------- | -------------- | -------------------- |
62
- | 执行时机 | 每次请求时执行 | 应用启动时初始化一次 |
63
- | 作用范围 | 请求级别 | 应用级别 |
64
- | 访问对象 | befly + ctx | befly |
65
- | 主要用途 | 请求处理中间件 | 功能模块封装 |
66
- | 排序方式 | order 数值 | after 依赖关系 |
67
-
68
- ---
69
-
70
- ## Hook 结构
71
-
72
- ### 基础结构
73
-
74
- ```typescript
75
- import type { Hook } from "befly/types/hook";
76
-
77
- const hook: Hook = {
78
- // 执行顺序(数字越小越先执行)
79
- order: 10,
80
-
81
- // 处理函数
82
- handler: async (befly, ctx) => {
83
- // 处理逻辑
84
- // 可以访问 befly 全局对象和 ctx 请求上下文
85
- }
86
- };
87
-
88
- export default hook;
89
- ```
90
-
91
- ### 完整类型定义
92
-
93
- ```typescript
94
- /**
95
- * 钩子处理函数类型
96
- */
97
- type HookHandler = (befly: BeflyContext, ctx: RequestContext) => Promise<void> | void;
98
-
99
- /**
100
- * 钩子配置类型
101
- */
102
- interface Hook {
103
- /** 钩子名称(运行时自动生成,无需手动设置) */
104
- name?: string;
105
-
106
- /** 依赖的钩子列表(在这些钩子之后执行) */
107
- after?: string[];
108
-
109
- /** 执行顺序(数字越小越先执行) */
110
- order?: number;
111
-
112
- /** 钩子处理函数 */
113
- handler: HookHandler;
114
-
115
- /** 钩子配置(可选) */
116
- config?: Record<string, any>;
117
-
118
- /** 钩子描述(可选) */
119
- description?: string;
120
- }
121
- ```
122
-
123
- ---
124
-
125
- ## 执行顺序
126
-
127
- ### order 值规范
128
-
129
- | order | 钩子名称 | 职责说明 |
130
- | ----- | ------------- | ---------------- |
131
- | 2 | cors | CORS 跨域处理 |
132
- | 3 | auth | JWT 身份认证 |
133
- | 4 | parser | 请求参数解析 |
134
- | 5 | requestLogger | 请求日志记录 |
135
- | 6 | validator | 参数验证 |
136
- | 6 | permission | 权限检查 |
137
- | 10-99 | 自定义钩子 | 业务逻辑钩子 |
138
- | 100+ | 后置钩子 | 响应处理、清理等 |
139
-
140
- **建议**:
141
-
142
- - 1-9:框架核心钩子
143
- - 10-99:业务逻辑钩子
144
- - 100+:后置处理钩子
145
-
146
- ### 执行流程图
147
-
148
- ```
149
- 请求进入
150
-
151
- ┌─────────────────────────────────────────────────┐
152
- │ cors (order: 2) │
153
- │ - 设置 CORS 响应头 │
154
- │ - 处理 OPTIONS 预检请求 → 可能中断 │
155
- ├─────────────────────────────────────────────────┤
156
- │ auth (order: 3) │
157
- │ - 解析 Authorization Header │
158
- │ - 验证 JWT Token │
159
- │ - 设置 ctx.user │
160
- ├─────────────────────────────────────────────────┤
161
- │ parser (order: 4) │
162
- │ - GET: 解析 URL 查询参数 │
163
- │ - POST: 解析 JSON/XML 请求体 │
164
- │ - 根据 fields 过滤字段 │
165
- │ - 设置 ctx.body → 可能中断(格式错误) │
166
- ├─────────────────────────────────────────────────┤
167
- │ requestLogger (order: 5) │
168
- │ - 记录请求日志 │
169
- ├─────────────────────────────────────────────────┤
170
- │ validator (order: 6) │
171
- │ - 验证必填字段 │
172
- │ - 验证类型、长度、正则 → 可能中断 │
173
- ├─────────────────────────────────────────────────┤
174
- │ permission (order: 6) │
175
- │ - 检查 auth 配置 │
176
- │ - 验证登录状态 │
177
- │ - 检查角色权限 → 可能中断 │
178
- ├─────────────────────────────────────────────────┤
179
- │ [自定义钩子 order: 10-99] │
180
- │ - 限流、审计、数据预处理等 │
181
- ├─────────────────────────────────────────────────┤
182
- │ API Handler │
183
- │ - 执行业务逻辑 │
184
- │ - 返回响应结果 │
185
- ├─────────────────────────────────────────────────┤
186
- │ FinalResponse │
187
- │ - 格式化响应 │
188
- │ - 记录请求日志 │
189
- └─────────────────────────────────────────────────┘
190
-
191
- 响应返回
192
- ```
193
-
194
- ---
195
-
196
- ## 内置钩子
197
-
198
- 内置钩子除本页总览外,也提供分文档说明(更聚焦配置与行为):
199
-
200
- - [cors Hook](./cors.md)
201
- - [auth Hook](./auth.md)
202
- - [parser Hook](./parser.md)
203
- - [rateLimit Hook](./rateLimit.md)
204
-
205
- ### cors - 跨域处理
206
-
207
- 处理 CORS 跨域请求,设置响应头。
208
-
209
- ```typescript
210
- // hooks/cors.ts
211
- const hook: Hook = {
212
- order: 2,
213
- handler: async (befly, ctx) => {
214
- const req = ctx.req;
215
-
216
- // 合并默认配置和用户配置
217
- const defaultConfig: CorsConfig = {
218
- origin: "*",
219
- methods: "GET, POST, PUT, DELETE, OPTIONS",
220
- allowedHeaders: "Content-Type, Authorization, authorization, token",
221
- exposedHeaders: "Content-Range, X-Content-Range, Authorization, authorization, token",
222
- maxAge: 86400,
223
- credentials: "true"
224
- };
225
-
226
- const corsConfig = { ...defaultConfig, ...(beflyConfig.cors || {}) };
227
-
228
- // 设置 CORS 响应头
229
- ctx.corsHeaders = setCorsOptions(req, corsConfig);
230
-
231
- // 处理 OPTIONS 预检请求
232
- if (req.method === "OPTIONS") {
233
- ctx.response = new Response(null, {
234
- status: 204,
235
- headers: ctx.corsHeaders
236
- });
237
- return; // 中断后续处理
238
- }
239
- }
240
- };
241
- ```
242
-
243
- **配置**:
244
-
245
- ```json
246
- // befly.development.json
247
- {
248
- "cors": {
249
- "origin": "https://example.com",
250
- "methods": "GET, POST",
251
- "allowedHeaders": "Content-Type, Authorization",
252
- "maxAge": 3600,
253
- "credentials": "true"
254
- }
255
- }
256
- ```
257
-
258
- ---
259
-
260
- ### auth - 身份认证
261
-
262
- 解析 JWT Token,设置用户信息。
263
-
264
- ```typescript
265
- // hooks/auth.ts
266
- const hook: Hook = {
267
- order: 3,
268
- handler: async (befly, ctx) => {
269
- const authHeader = ctx.req.headers.get("authorization");
270
-
271
- if (authHeader && authHeader.startsWith("Bearer ")) {
272
- const token = authHeader.substring(7);
273
-
274
- try {
275
- const payload = await befly.jwt.verify(token);
276
- ctx.user = payload;
277
- } catch (error: any) {
278
- ctx.user = {};
279
- }
280
- } else {
281
- ctx.user = {};
282
- }
283
- }
284
- };
285
- ```
286
-
287
- **特点**:
288
-
289
- - 不中断请求,仅设置 `ctx.user`
290
- - Token 无效时设置空对象 `{}`
291
- - 权限检查由 permission 钩子负责
292
-
293
- ---
294
-
295
- ### parser - 参数解析
296
-
297
- 解析请求参数,根据 API 字段定义过滤。
298
-
299
- ```typescript
300
- // hooks/parser.ts
301
- const hook: Hook = {
302
- order: 4,
303
- handler: async (befly, ctx) => {
304
- if (!ctx.api) return;
305
-
306
- // GET 请求:解析查询参数
307
- if (ctx.req.method === "GET") {
308
- const url = new URL(ctx.req.url);
309
- const params = Object.fromEntries(url.searchParams);
310
-
311
- if (ctx.api.rawBody) {
312
- ctx.body = params;
313
- } else if (isPlainObject(ctx.api.fields) && !isEmpty(ctx.api.fields)) {
314
- ctx.body = pickFields(params, Object.keys(ctx.api.fields));
315
- } else {
316
- ctx.body = params;
317
- }
318
- }
319
- // POST 请求:解析 JSON/XML
320
- else if (ctx.req.method === "POST") {
321
- const contentType = ctx.req.headers.get("content-type") || "";
322
-
323
- if (contentType.includes("application/json")) {
324
- const body = await ctx.req.json();
325
- // 过滤字段...
326
- ctx.body = pickFields(body, Object.keys(ctx.api.fields));
327
- } else if (contentType.includes("application/xml")) {
328
- // XML 解析...
329
- } else {
330
- ctx.response = ErrorResponse(ctx, "无效的请求参数格式");
331
- return;
332
- }
333
- }
334
- }
335
- };
336
- ```
337
-
338
- **支持格式**:
339
-
340
- - `application/json`
341
- - `application/xml` / `text/xml`
342
-
343
- ---
344
-
345
- ### requestLogger - 请求日志
346
-
347
- 在参数解析后记录请求日志。
348
-
349
- ```typescript
350
- // hooks/requestLogger.ts
351
- const hook: Hook = {
352
- order: 5,
353
- handler: async (befly, ctx) => {
354
- if (!ctx.api) return;
355
-
356
- const logData = {
357
- requestId: ctx.requestId,
358
- route: ctx.route,
359
- ip: ctx.ip,
360
- userId: ctx.user?.id || "",
361
- nickname: ctx.user?.nickname || "",
362
- roleCode: ctx.user?.roleCode || ""
363
- };
364
-
365
- // 截断大请求体
366
- if (ctx.body && Object.keys(ctx.body).length > 0) {
367
- logData.body = truncateBody(ctx.body);
368
- }
369
-
370
- Logger.info(logData, "请求日志");
371
- }
372
- };
373
- ```
374
-
375
- **特点**:
376
-
377
- - 自动截断超长字段(500 字符)
378
- - 不记录非 API 请求
379
-
380
- ---
381
-
382
- ### validator - 参数验证
383
-
384
- 验证请求参数类型、必填、长度等。
385
-
386
- ```typescript
387
- // hooks/validator.ts
388
- const hook: Hook = {
389
- order: 6,
390
- handler: async (befly, ctx) => {
391
- if (!ctx.api) return;
392
- if (!ctx.api.fields) return;
393
-
394
- // 验证参数
395
- const result = Validator.validate(ctx.body, ctx.api.fields, ctx.api.required || []);
396
-
397
- if (result.code !== 0) {
398
- ctx.response = ErrorResponse(ctx, result.firstError || "参数验证失败", 1, null, result.fieldErrors);
399
- return;
400
- }
401
- }
402
- };
403
- ```
404
-
405
- **验证内容**:
406
-
407
- - 必填字段检查
408
- - 类型验证(string/number/text 等)
409
- - 长度/范围验证(min/max)
410
- - 正则验证(regex)
411
-
412
- ---
413
-
414
- ### permission - 权限检查
415
-
416
- 检查用户权限,验证 API 访问授权。
417
-
418
- ```typescript
419
- // hooks/permission.ts
420
- import { CacheKeys } from "befly/lib/cacheKeys";
421
-
422
- const hook: Hook = {
423
- order: 6,
424
- handler: async (befly, ctx) => {
425
- if (!ctx.api) return;
426
-
427
- // 1. 接口无需权限
428
- if (ctx.api.auth === false) {
429
- return;
430
- }
431
-
432
- // 2. 用户未登录
433
- if (!ctx.user || !ctx.user.id) {
434
- ctx.response = ErrorResponse(ctx, "未登录");
435
- return;
436
- }
437
-
438
- // 3. 开发者权限(最高权限)
439
- if (ctx.user.roleCode === "dev") {
440
- return;
441
- }
442
-
443
- // 4. 角色权限检查
444
- let hasPermission = false;
445
- if (ctx.user.roleCode && befly.redis) {
446
- // 统一格式:METHOD/path(与写入缓存保持一致)
447
- const apiPath = normalizeApiPath(ctx.req.method, new URL(ctx.req.url).pathname);
448
-
449
- // 极简方案:每个角色一个 Set,直接判断成员是否存在
450
- const roleApisKey = CacheKeys.roleApis(ctx.user.roleCode);
451
- hasPermission = await befly.redis.sismember(roleApisKey, apiPath);
452
- }
453
-
454
- if (!hasPermission) {
455
- const apiLabel = ctx.api?.name ? ctx.api.name : apiPath;
456
- ctx.response = ErrorResponse(ctx, `无权访问 ${apiLabel} 接口`);
457
- return;
458
- }
459
- }
460
- };
461
- ```
462
-
463
- **权限层级**:
464
-
465
- 1. `auth: false` - 公开接口,无需检查
466
- 2. 未登录 - 返回"未登录"
467
- 3. `roleCode: 'dev'` - 开发者,最高权限
468
- 4. Redis 权限集合 - 检查角色是否有 API 权限
469
-
470
- ---
471
-
472
- ## 自定义钩子开发
473
-
474
- ### 基础钩子
475
-
476
- ```typescript
477
- // hooks/myHook.ts(项目钩子名:app_myHook)
478
- import type { Hook } from "befly/types/hook";
479
-
480
- const hook: Hook = {
481
- order: 10,
482
- handler: async (befly, ctx) => {
483
- befly.logger.info({ route: ctx.route }, "自定义钩子执行");
484
- }
485
- };
486
-
487
- export default hook;
488
- ```
489
-
490
- ---
491
-
492
- ### 请求拦截钩子
493
-
494
- 拦截特定请求:
495
-
496
- ```typescript
497
- // hooks/blacklist.ts
498
- import type { Hook } from "befly/types/hook";
499
- import { ErrorResponse } from "befly/utils/response";
500
-
501
- const hook: Hook = {
502
- order: 1, // 最先执行
503
- handler: async (befly, ctx) => {
504
- // IP 黑名单检查
505
- const blacklist = ["192.168.1.100", "10.0.0.1"];
506
-
507
- if (blacklist.includes(ctx.ip)) {
508
- ctx.response = ErrorResponse(ctx, "您的 IP 已被禁止访问", 403);
509
- return;
510
- }
511
- }
512
- };
513
-
514
- export default hook;
515
- ```
516
-
517
- ---
518
-
519
- ### 限流钩子
520
-
521
- 基于 Redis 的请求限流:
522
-
523
- > 说明:Befly Core 已内置 `rateLimit` 钩子(默认启用)。
524
- >
525
- > 默认行为:按 IP 对所有 API 进行限流(默认阈值:$1000/60$,即 60 秒最多 1000 次)。
526
- >
527
- > - 关闭:`rateLimit.enable = 0`
528
- > - 覆盖:配置 `rules`(更细粒度)或调整 `defaultLimit/defaultWindow`
529
- > - 跳过:配置 `skipRoutes`(命中后直接跳过限流,优先级最高)
530
- >
531
- > 规则选择:当多条 `rules` 同时命中时,会优先选择更“具体”的规则(精确 > 前缀 > 通配);
532
- > 同等具体度按 `rules` 的先后顺序。
533
- >
534
- > key 行为:当 `key = user` 且请求上下文中没有 `ctx.user.id` 时,会回退为按 IP 计数,避免所有匿名请求共享同一个计数桶。
535
-
536
- 配置示例(`configs/befly.common.json`):
537
-
538
- ```json
539
- {
540
- "rateLimit": {
541
- "enable": 1,
542
- "defaultLimit": 1000,
543
- "defaultWindow": 60,
544
- "key": "ip",
545
- "skipRoutes": ["/api/health", "/api/metrics"],
546
- "rules": [
547
- {
548
- "route": "/api/auth/*",
549
- "limit": 20,
550
- "window": 60,
551
- "key": "ip"
552
- },
553
- {
554
- "route": "/api/order/create",
555
- "limit": 5,
556
- "window": 60,
557
- "key": "user"
558
- }
559
- ]
560
- }
561
- }
562
- ```
563
-
564
- ```typescript
565
- // hooks/rateLimit.ts
566
- import type { Hook } from "befly/types/hook";
567
- import { ErrorResponse } from "befly/utils/response";
568
-
569
- const hook: Hook = {
570
- order: 7,
571
- handler: async (befly, ctx) => {
572
- if (!ctx.api || !befly.redis) return;
573
-
574
- // 限流配置:10 次/60 秒
575
- const limit = 10;
576
- const window = 60;
577
-
578
- // 限流 key:IP + 路由
579
- const key = `ratelimit:${ctx.ip}:${ctx.route}`;
580
-
581
- // 原子计数 + 首次设置过期
582
- const count = await befly.redis.incrWithExpire(key, window);
583
-
584
- // 超过限制
585
- if (count > limit) {
586
- ctx.response = ErrorResponse(ctx, "请求过于频繁,请稍后再试", 1);
587
- return;
588
- }
589
- }
590
- };
591
-
592
- export default hook;
593
- ```
594
-
595
- ---
596
-
597
- ### 审计日志钩子
598
-
599
- 记录操作审计日志:
600
-
601
- ```typescript
602
- // hooks/audit.ts
603
- import type { Hook } from "befly/types/hook";
604
-
605
- const hook: Hook = {
606
- order: 100, // 在 handler 执行后
607
- handler: async (befly, ctx) => {
608
- // 只记录写操作
609
- if (!ctx.api || ctx.req.method === "GET") return;
610
- if (!ctx.user?.id) return;
611
-
612
- // 记录审计日志
613
- try {
614
- await befly.db.insData({
615
- table: "audit_log",
616
- data: {
617
- userId: ctx.user.id,
618
- username: ctx.user.username || "",
619
- route: ctx.route,
620
- method: ctx.req.method,
621
- ip: ctx.ip,
622
- requestBody: JSON.stringify(ctx.body).substring(0, 1000),
623
- operateTime: Date.now()
624
- }
625
- });
626
- } catch (error) {
627
- befly.logger.error({ err: error }, "审计日志记录失败");
628
- }
629
- }
630
- };
631
-
632
- export default hook;
633
- ```
634
-
635
- ---
636
-
637
- ## 中断请求
638
-
639
- 设置 `ctx.response` 可以中断后续 Hook 和 API Handler 的执行:
640
-
641
- ```typescript
642
- import { ErrorResponse } from "befly/utils/response";
643
-
644
- const hook: Hook = {
645
- order: 5,
646
- handler: async (befly, ctx) => {
647
- // 条件判断
648
- if (someCondition) {
649
- // 设置 response 中断请求
650
- ctx.response = ErrorResponse(ctx, "请求被拦截", 1);
651
- return; // 必须 return
652
- }
653
-
654
- // 继续执行后续钩子...
655
- }
656
- };
657
- ```
658
-
659
- **ErrorResponse 函数**:
660
-
661
- ```typescript
662
- ErrorResponse(ctx, msg, code?, data?, detail?)
663
- // ctx: RequestContext - 请求上下文
664
- // msg: string - 错误消息
665
- // code: number - 错误码(默认 1)
666
- // data: any - 附加数据(默认 null)
667
- // detail: any - 详细信息(默认 null)
668
- ```
669
-
670
- ---
671
-
672
- ## 禁用钩子
673
-
674
- 在配置文件中设置 `disableHooks` 数组:
675
-
676
- ```json
677
- // befly.development.json
678
- {
679
- "disableHooks": ["requestLogger", "permission"]
680
- }
681
- ```
682
-
683
- 被禁用的钩子不会被加载和执行。
684
-
685
- ---
686
-
687
- ## 最佳实践
688
-
689
- ### 1. 合理设置 order
690
-
691
- ```typescript
692
- // ✅ 推荐:使用适当的 order 值
693
- const hook: Hook = {
694
- order: 15, // 在核心钩子之后
695
- handler: async (befly, ctx) => {
696
- /* ... */
697
- }
698
- };
699
-
700
- // ❌ 避免:使用过小的 order 值
701
- const hook: Hook = {
702
- order: 1, // 可能影响核心钩子
703
- handler: async (befly, ctx) => {
704
- /* ... */
705
- }
706
- };
707
- ```
708
-
709
- ### 2. 提前检查 ctx.api
710
-
711
- ```typescript
712
- // ✅ 推荐:检查 ctx.api 是否存在
713
- const hook: Hook = {
714
- handler: async (befly, ctx) => {
715
- if (!ctx.api) return; // 非 API 请求直接跳过
716
- // ...
717
- }
718
- };
719
- ```
720
-
721
- ### 3. 错误处理
722
-
723
- ```typescript
724
- // ✅ 推荐:捕获异常避免影响请求
725
- const hook: Hook = {
726
- handler: async (befly, ctx) => {
727
- try {
728
- await someOperation();
729
- } catch (error) {
730
- befly.logger.error({ err: error }, "钩子执行失败");
731
- // 根据业务决定是否中断请求
732
- }
733
- }
734
- };
735
- ```
736
-
737
- ### 4. 避免重复处理
738
-
739
- ```typescript
740
- // ✅ 推荐:检查是否已处理
741
- const hook: Hook = {
742
- handler: async (befly, ctx) => {
743
- if (ctx.response) return; // 已有响应,跳过
744
- // ...
745
- }
746
- };
747
- ```
748
-
749
- ### 5. 性能考虑
750
-
751
- ````typescript
752
- // ✅ 推荐:异步操作使用 Promise
753
- const hook: Hook = {
754
- handler: async (befly, ctx) => {
755
- // 可以并行的操作
756
- const [result1, result2] = await Promise.all([operation1(), operation2()]);
757
- }
758
- };
759
-
760
- ### 6. 直接使用字段,避免多余变量
761
-
762
- 仅用于“转发参数 / 改名 / 只用一次”的中间变量不要定义;优先直接使用来源对象字段(例如 `ctx.body.xxx`、`payload.xxx`)。
763
-
764
- ```typescript
765
- // ❌ 避免:只为转发参数而改名/多一层
766
- const hook: Hook = {
767
- handler: async (befly, ctx) => {
768
- const payload = await befly.jwt.verify(token); // token 解析略
769
- const userId = payload.id;
770
- setCtxUser(userId, payload.roleCode, payload.nickname, payload.roleType);
771
- }
772
- };
773
-
774
- // ✅ 推荐:直接转发字段
775
- const hook2: Hook = {
776
- handler: async (befly, ctx) => {
777
- const payload = await befly.jwt.verify(token); // token 解析略
778
- setCtxUser(payload.id, payload.roleCode, payload.nickname, payload.roleType);
779
- }
780
- };
781
-
782
- // ✅ 例外:需要类型收窄/非空校验/复用(>=2 次)时再定义变量
783
- const hook3: Hook = {
784
- handler: async (befly, ctx) => {
785
- const payload = await befly.jwt.verify(token); // token 解析略
786
- const userId = payload.id;
787
- if (typeof userId !== "number") return;
788
-
789
- setCtxUser(userId, payload.roleCode, payload.nickname, payload.roleType);
790
- }
791
- };
792
- ````
793
-
794
- ````
795
-
796
- ---
797
-
798
- ## 常见问题
799
-
800
- ### Q1: 钩子执行顺序如何确定?
801
-
802
- 按 `order` 值从小到大排序执行,相同 `order` 值按文件名字母顺序。
803
-
804
- ### Q2: 钩子可以访问插件吗?
805
-
806
- 可以,通过 `befly` 参数访问所有插件:
807
-
808
- ```typescript
809
- const hook: Hook = {
810
- handler: async (befly, ctx) => {
811
- await befly.db.getOne({
812
- /* ... */
813
- });
814
- await befly.redis.get("key");
815
- befly.logger.info("日志");
816
- }
817
- };
818
- ````
819
-
820
- ### Q3: 如何在钩子间传递数据?
821
-
822
- 通过 `ctx` 上下文传递:
823
-
824
- ```typescript
825
- // 钩子 A(order: 10)
826
- ctx.customData = { key: "value" };
827
-
828
- // 钩子 B(order: 20)
829
- const data = ctx.customData;
830
- ```
831
-
832
- ### Q4: 钩子抛出异常会怎样?
833
-
834
- 未捕获的异常会被全局错误处理捕获,返回 500 错误响应。建议在钩子内部处理异常。
835
-
836
- ### Q5: 可以动态修改钩子吗?
837
-
838
- 不支持运行时动态修改,所有钩子在应用启动时加载。