befly 3.10.0 → 3.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.
Files changed (79) hide show
  1. package/.gitignore +0 -0
  2. package/README.md +10 -13
  3. package/configs/presetFields.ts +10 -0
  4. package/configs/presetRegexp.ts +225 -0
  5. package/docs/README.md +17 -11
  6. package/docs/api/api.md +15 -1
  7. package/docs/guide/quickstart.md +19 -5
  8. package/docs/infra/redis.md +23 -11
  9. package/docs/quickstart.md +5 -335
  10. package/docs/reference/addon.md +0 -15
  11. package/docs/reference/config.md +1 -1
  12. package/docs/reference/logger.md +3 -3
  13. package/docs/reference/sync.md +99 -73
  14. package/docs/reference/table.md +1 -1
  15. package/package.json +15 -16
  16. package/docs/cipher.md +0 -582
  17. package/docs/database.md +0 -1176
  18. package/tests/_mocks/mockSqliteDb.ts +0 -204
  19. package/tests/addonHelper-cache.test.ts +0 -32
  20. package/tests/api-integration-array-number.test.ts +0 -282
  21. package/tests/apiHandler-routePath-only.test.ts +0 -32
  22. package/tests/befly-config-env.test.ts +0 -78
  23. package/tests/cacheHelper.test.ts +0 -323
  24. package/tests/cacheKeys.test.ts +0 -41
  25. package/tests/checkApi-routePath-strict.test.ts +0 -166
  26. package/tests/checkMenu.test.ts +0 -346
  27. package/tests/checkTable-smoke.test.ts +0 -157
  28. package/tests/cipher.test.ts +0 -249
  29. package/tests/dbDialect-cache.test.ts +0 -23
  30. package/tests/dbDialect.test.ts +0 -46
  31. package/tests/dbHelper-advanced.test.ts +0 -723
  32. package/tests/dbHelper-all-array-types.test.ts +0 -316
  33. package/tests/dbHelper-array-serialization.test.ts +0 -258
  34. package/tests/dbHelper-batch-write.test.ts +0 -90
  35. package/tests/dbHelper-columns.test.ts +0 -234
  36. package/tests/dbHelper-execute.test.ts +0 -187
  37. package/tests/dbHelper-joins.test.ts +0 -221
  38. package/tests/fields-redis-cache.test.ts +0 -127
  39. package/tests/fields-validate.test.ts +0 -99
  40. package/tests/fixtures/scanFilesAddon/node_modules/@befly-addon/demo/apis/sub/b.ts +0 -3
  41. package/tests/fixtures/scanFilesApis/a.ts +0 -3
  42. package/tests/fixtures/scanFilesApis/sub/b.ts +0 -3
  43. package/tests/getClientIp.test.ts +0 -54
  44. package/tests/integration.test.ts +0 -189
  45. package/tests/jwt.test.ts +0 -65
  46. package/tests/loadPlugins-order-smoke.test.ts +0 -75
  47. package/tests/logger.test.ts +0 -325
  48. package/tests/redisHelper.test.ts +0 -495
  49. package/tests/redisKeys.test.ts +0 -9
  50. package/tests/scanConfig.test.ts +0 -144
  51. package/tests/scanFiles-routePath.test.ts +0 -46
  52. package/tests/smoke-sql.test.ts +0 -24
  53. package/tests/sqlBuilder-advanced.test.ts +0 -608
  54. package/tests/sqlBuilder.test.ts +0 -209
  55. package/tests/sync-connection.test.ts +0 -183
  56. package/tests/sync-init-guard.test.ts +0 -105
  57. package/tests/syncApi-insBatch-fields-consistent.test.ts +0 -61
  58. package/tests/syncApi-obsolete-records.test.ts +0 -69
  59. package/tests/syncApi-type-compat.test.ts +0 -72
  60. package/tests/syncDev-permissions.test.ts +0 -81
  61. package/tests/syncMenu-disableMenus-hard-delete.test.ts +0 -88
  62. package/tests/syncMenu-duplicate-path.test.ts +0 -122
  63. package/tests/syncMenu-obsolete-records.test.ts +0 -161
  64. package/tests/syncMenu-parentPath-from-tree.test.ts +0 -75
  65. package/tests/syncMenu-paths.test.ts +0 -59
  66. package/tests/syncTable-apply.test.ts +0 -279
  67. package/tests/syncTable-array-number.test.ts +0 -160
  68. package/tests/syncTable-constants.test.ts +0 -101
  69. package/tests/syncTable-db-integration.test.ts +0 -237
  70. package/tests/syncTable-ddl.test.ts +0 -245
  71. package/tests/syncTable-helpers.test.ts +0 -99
  72. package/tests/syncTable-schema.test.ts +0 -99
  73. package/tests/syncTable-testkit.test.ts +0 -25
  74. package/tests/syncTable-types.test.ts +0 -122
  75. package/tests/tableRef-and-deserialize.test.ts +0 -67
  76. package/tests/util.test.ts +0 -100
  77. package/tests/validator-array-number.test.ts +0 -310
  78. package/tests/validator-default.test.ts +0 -373
  79. package/tests/validator.test.ts +0 -679
package/.gitignore ADDED
File without changes
package/README.md CHANGED
@@ -220,7 +220,7 @@ export const beflyConfig = {
220
220
  redis: {
221
221
  host: "127.0.0.1",
222
222
  port: 6379,
223
- prefix: "befly:"
223
+ prefix: "befly"
224
224
  },
225
225
 
226
226
  // CORS 跨域配置
@@ -238,6 +238,8 @@ export const beflyConfig = {
238
238
  };
239
239
  ```
240
240
 
241
+ > 注意:`redis.prefix` 不要包含 `:`(系统会自动拼接分隔符)。
242
+
241
243
  ### 数据库连接
242
244
 
243
245
  通常你不需要手动连接(框架启动期会完成连接并注入插件实例)。
@@ -263,7 +265,7 @@ await Connect.connectRedis({
263
265
  host: "127.0.0.1",
264
266
  port: 6379,
265
267
  db: 0,
266
- prefix: "befly:"
268
+ prefix: "befly"
267
269
  });
268
270
 
269
271
  // 或:同时连接 SQL 和 Redis
@@ -281,7 +283,7 @@ await Connect.connect({
281
283
  host: "127.0.0.1",
282
284
  port: 6379,
283
285
  db: 0,
284
- prefix: "befly:"
286
+ prefix: "befly"
285
287
  }
286
288
  });
287
289
 
@@ -294,19 +296,14 @@ console.log(status.redis.connected); // true/false
294
296
  await Connect.disconnect();
295
297
  ```
296
298
 
297
- ### 配置文件迁移指南
298
-
299
- 如果你的项目之前使用 `app.config.ts`,请按以下步骤迁移:
299
+ ### 配置文件(当前约定)
300
300
 
301
- 1. **重命名文件**:`app.config.ts` → `befly.config.ts`
302
- 2. **更新导出名**:`config` → `beflyConfig`
301
+ 配置文件名为 `befly.config.ts`,导出名为 `beflyConfig`:
303
302
 
304
303
  ```typescript
305
- // 旧写法
306
- export const config = { ... };
307
-
308
- // 新写法
309
- export const beflyConfig = { ... };
304
+ export const beflyConfig = {
305
+ // ...
306
+ };
310
307
  ```
311
308
 
312
309
  ## 📖 文档
@@ -0,0 +1,10 @@
1
+ /**
2
+ * 预定义的默认字段(API fields 的 @ 引用)
3
+ */
4
+ export const presetFields: Record<string, any> = {
5
+ "@id": { name: "ID", type: "number", min: 1, max: null },
6
+ "@page": { name: "页码", type: "number", min: 1, max: 9999, default: 1 },
7
+ "@limit": { name: "每页数量", type: "number", min: 1, max: 100, default: 30 },
8
+ "@keyword": { name: "关键词", type: "string", min: 0, max: 50 },
9
+ "@state": { name: "状态", type: "number", regex: "^[0-2]$" }
10
+ };
@@ -0,0 +1,225 @@
1
+ /**
2
+ * 内置正则表达式别名
3
+ * 用于表单验证和数据校验
4
+ * 命名规范:小驼峰格式
5
+ */
6
+ export const RegexAliases = {
7
+ // ============================================
8
+ // 数字类型
9
+ // ============================================
10
+ /** 正整数(不含0) */
11
+ number: "^\\d+$",
12
+ /** 整数(含负数) */
13
+ integer: "^-?\\d+$",
14
+ /** 浮点数 */
15
+ float: "^-?\\d+(\\.\\d+)?$",
16
+ /** 正整数(不含0) */
17
+ positive: "^[1-9]\\d*$",
18
+ /** 负整数 */
19
+ negative: "^-\\d+$",
20
+ /** 零 */
21
+ zero: "^0$",
22
+
23
+ // ============================================
24
+ // 字符串类型
25
+ // ============================================
26
+ /** 纯字母 */
27
+ word: "^[a-zA-Z]+$",
28
+ /** 字母和数字 */
29
+ alphanumeric: "^[a-zA-Z0-9]+$",
30
+ /** 字母、数字和下划线 */
31
+ alphanumeric_: "^[a-zA-Z0-9_]+$",
32
+ /** 字母、数字、下划线和短横线 */
33
+ alphanumericDash_: "^[a-zA-Z0-9_-]+$",
34
+ /** 小写字母 */
35
+ lowercase: "^[a-z]+$",
36
+ /** 大写字母 */
37
+ uppercase: "^[A-Z]+$",
38
+
39
+ // ============================================
40
+ // 中文
41
+ // ============================================
42
+ /** 纯中文 */
43
+ chinese: "^[\\u4e00-\\u9fa5]+$",
44
+ /** 中文和字母 */
45
+ chineseWord: "^[\\u4e00-\\u9fa5a-zA-Z]+$",
46
+
47
+ // ============================================
48
+ // 常用格式
49
+ // ============================================
50
+ /** 邮箱地址 */
51
+ email: "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$",
52
+ /** 中国大陆手机号 */
53
+ phone: "^1[3-9]\\d{9}$",
54
+ /** 固定电话(区号-号码) */
55
+ telephone: "^0\\d{2,3}-?\\d{7,8}$",
56
+ /** URL 地址 */
57
+ url: "^https?://",
58
+ /** IPv4 地址 */
59
+ ip: "^((25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.){3}(25[0-5]|2[0-4]\\d|[01]?\\d\\d?)$",
60
+ /** IPv6 地址 */
61
+ ipv6: "^([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$",
62
+ /** 域名 */
63
+ domain: "^(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\\.)+[a-zA-Z]{2,}$",
64
+
65
+ // ============================================
66
+ // 特殊格式
67
+ // ============================================
68
+ /** UUID */
69
+ uuid: "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$",
70
+ /** 十六进制字符串 */
71
+ hex: "^[0-9a-fA-F]+$",
72
+ /** Base64 编码 */
73
+ base64: "^[A-Za-z0-9+/=]+$",
74
+ /** MD5 哈希 */
75
+ md5: "^[a-f0-9]{32}$",
76
+ /** SHA1 哈希 */
77
+ sha1: "^[a-f0-9]{40}$",
78
+ /** SHA256 哈希 */
79
+ sha256: "^[a-f0-9]{64}$",
80
+
81
+ // ============================================
82
+ // 日期时间
83
+ // ============================================
84
+ /** 日期 YYYY-MM-DD */
85
+ date: "^\\d{4}-\\d{2}-\\d{2}$",
86
+ /** 时间 HH:MM:SS */
87
+ time: "^\\d{2}:\\d{2}:\\d{2}$",
88
+ /** ISO 日期时间 */
89
+ datetime: "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}",
90
+ /** 年份 */
91
+ year: "^\\d{4}$",
92
+ /** 月份 01-12 */
93
+ month: "^(0[1-9]|1[0-2])$",
94
+ /** 日期 01-31 */
95
+ day: "^(0[1-9]|[12]\\d|3[01])$",
96
+
97
+ // ============================================
98
+ // 代码相关
99
+ // ============================================
100
+ /** 变量名 */
101
+ variable: "^[a-zA-Z_][a-zA-Z0-9_]*$",
102
+ /** 常量名(全大写) */
103
+ constant: "^[A-Z][A-Z0-9_]*$",
104
+ /** 包名(小写+连字符) */
105
+ package: "^[a-z][a-z0-9-]*$",
106
+
107
+ // ============================================
108
+ // 证件相关
109
+ // ============================================
110
+ /** 中国身份证号(18位) */
111
+ idCard: "^\\d{17}[\\dXx]$",
112
+ /** 护照号 */
113
+ passport: "^[a-zA-Z0-9]{5,17}$",
114
+
115
+ // ============================================
116
+ // 账号相关(国内常用)
117
+ // ============================================
118
+ /** 银行卡号(16-19位数字) */
119
+ bankCard: "^\\d{16,19}$",
120
+ /** 微信号(6-20位,字母开头,可包含字母、数字、下划线、减号) */
121
+ wechat: "^[a-zA-Z][a-zA-Z0-9_-]{5,19}$",
122
+ /** QQ号(5-11位数字,首位非0) */
123
+ qq: "^[1-9]\\d{4,10}$",
124
+ /** 支付宝账号(手机号或邮箱) */
125
+ alipay: "^(1[3-9]\\d{9}|[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,})$",
126
+ /** 用户名(4-20位,字母开头,可包含字母、数字、下划线) */
127
+ username: "^[a-zA-Z][a-zA-Z0-9_]{3,19}$",
128
+ /** 昵称(2-20位,支持中文、字母、数字) */
129
+ nickname: "^[\\u4e00-\\u9fa5a-zA-Z0-9]{2,20}$",
130
+
131
+ // ============================================
132
+ // 密码强度
133
+ // ============================================
134
+ /** 弱密码(至少6位) */
135
+ passwordWeak: "^.{6,}$",
136
+ /** 中等密码(至少8位,包含字母和数字) */
137
+ passwordMedium: "^(?=.*[a-zA-Z])(?=.*\\d).{8,}$",
138
+ /** 强密码(至少8位,包含大小写字母、数字和特殊字符) */
139
+ passwordStrong: "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[!@#$%^&*]).{8,}$",
140
+
141
+ // ============================================
142
+ // 其他常用
143
+ // ============================================
144
+ /** 车牌号(新能源+普通) */
145
+ licensePlate: "^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领][A-Z][A-HJ-NP-Z0-9]{4,5}[A-HJ-NP-Z0-9挂学警港澳]$",
146
+ /** 邮政编码 */
147
+ postalCode: "^\\d{6}$",
148
+ /** 版本号(语义化版本) */
149
+ semver: "^\\d+\\.\\d+\\.\\d+(-[a-zA-Z0-9.]+)?(\\+[a-zA-Z0-9.]+)?$",
150
+ /** 颜色值(十六进制) */
151
+ colorHex: "^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$",
152
+
153
+ // ============================================
154
+ // 空值
155
+ // ============================================
156
+ /** 空字符串 */
157
+ empty: "^$",
158
+ /** 非空 */
159
+ notempty: ".+"
160
+ } as const;
161
+
162
+ /**
163
+ * 正则别名类型
164
+ */
165
+ export type RegexAliasName = keyof typeof RegexAliases;
166
+
167
+ // ============================================
168
+ // 正则表达式缓存(性能优化)
169
+ // ============================================
170
+ const regexCache = new Map<string, RegExp>();
171
+
172
+ /**
173
+ * 获取正则表达式字符串
174
+ * @param name 正则别名(以 @ 开头)或自定义正则字符串
175
+ * @returns 正则表达式字符串
176
+ */
177
+ export function getRegex(name: string): string {
178
+ if (name.startsWith("@")) {
179
+ const alias = name.slice(1) as RegexAliasName;
180
+ return RegexAliases[alias] || name;
181
+ }
182
+ return name;
183
+ }
184
+
185
+ /**
186
+ * 获取编译后的正则表达式对象(带缓存)
187
+ * @param pattern 正则别名或正则字符串
188
+ * @param flags 正则标志(如 'i', 'g')
189
+ * @returns 编译后的 RegExp 对象
190
+ */
191
+ export function getCompiledRegex(pattern: string, flags?: string): RegExp {
192
+ const regexStr = getRegex(pattern);
193
+ const cacheKey = `${regexStr}:${flags || ""}`;
194
+
195
+ let cached = regexCache.get(cacheKey);
196
+ if (!cached) {
197
+ cached = new RegExp(regexStr, flags);
198
+ regexCache.set(cacheKey, cached);
199
+ }
200
+ return cached;
201
+ }
202
+
203
+ /**
204
+ * 验证值是否匹配正则(使用缓存)
205
+ * @param value 要验证的值
206
+ * @param pattern 正则别名或正则字符串
207
+ * @returns 是否匹配
208
+ */
209
+ export function matchRegex(value: string, pattern: string): boolean {
210
+ return getCompiledRegex(pattern).test(value);
211
+ }
212
+
213
+ /**
214
+ * 清除正则缓存
215
+ */
216
+ export function clearRegexCache(): void {
217
+ regexCache.clear();
218
+ }
219
+
220
+ /**
221
+ * 获取缓存大小
222
+ */
223
+ export function getRegexCacheSize(): number {
224
+ return regexCache.size;
225
+ }
package/docs/README.md CHANGED
@@ -2,18 +2,24 @@
2
2
 
3
3
  > Befly 是基于 Bun 的高性能 API 框架
4
4
 
5
+ ## 文档原则
6
+
7
+ - 文档只描述**当前实现**:不提供迁移指引、不保留兼容说明、不出现“已迁移/跳转页/占位页”。
8
+ - 如文档与代码行为不一致:**以代码为准**,应直接修正文档。
9
+ - 规则以“可执行/可验证”为标准:给出明确约束(例如 pathname-only、严格校验规则),避免含糊表述。
10
+
5
11
  ## 快速开始
6
12
 
7
13
  - [Quickstart 快速入门](./guide/quickstart.md) - 5 分钟搭建第一个 API 服务
8
14
 
9
15
  ## 核心概念
10
16
 
11
- | 文档 | 说明 |
12
- | ---------------------------------------- | ---------------------------- |
13
- | [API 开发](./api/api.md) | API 定义、字段验证、权限控制 |
14
- | [Table 表结构](./reference/table.md) | JSON 表定义格式、字段类型 |
15
- | [Database 数据库](./plugins/database.md) | CRUD 操作、事务、批量操作 |
16
- | [Config 配置](./reference/config.md) | 配置文件、环境分离 |
17
+ | 文档 | 说明 |
18
+ | ---------------------------------------- | --------------------------------------------------------------------- |
19
+ | [API 开发](./api/api.md) | API 定义、字段验证、权限控制([强约束清单](./api/api.md#强约束清单)) |
20
+ | [Table 表结构](./reference/table.md) | JSON 表定义格式、字段类型 |
21
+ | [Database 数据库](./plugins/database.md) | CRUD 操作、事务、批量操作 |
22
+ | [Config 配置](./reference/config.md) | 配置文件、环境分离 |
17
23
 
18
24
  ## 扩展开发
19
25
 
@@ -41,9 +47,9 @@
41
47
 
42
48
  ## 命令工具
43
49
 
44
- | 文档 | 说明 |
45
- | -------------------------------- | --------------------- |
46
- | [Sync 同步](./reference/sync.md) | 数据库、API、菜单同步 |
50
+ | 文档 | 说明 |
51
+ | -------------------------------- | --------------------------------------------------------------------- |
52
+ | [Sync 同步](./reference/sync.md) | 数据库、API、菜单同步([强约束清单](./reference/sync.md#强约束清单)) |
47
53
 
48
54
  ## 实战示例
49
55
 
@@ -57,7 +63,7 @@
57
63
 
58
64
  ### 开发篇
59
65
 
60
- 2. **[API](./api/api.md)** - API 路由定义、字段验证、权限控制、响应格式
66
+ 2. **[API](./api/api.md)** - API 路由定义、字段验证、权限控制、响应格式([强约束清单](./api/api.md#强约束清单))
61
67
  3. **[Table](./reference/table.md)** - 表定义格式、字段类型、系统字段、命名规范
62
68
  4. **[Database](./plugins/database.md)** - 数据库连接、CRUD 操作、事务处理、批量操作
63
69
  5. **[Config](./reference/config.md)** - 配置文件结构、环境分离、运行时配置
@@ -77,7 +83,7 @@
77
83
 
78
84
  ### 运维篇
79
85
 
80
- 13. **[Sync](./reference/sync.md)** - syncTable、syncData(内部:syncApi、syncMenu、syncDev)
86
+ 13. **[Sync](./reference/sync.md)** - syncTable、syncApi、syncMenu、syncDev、syncCache([强约束清单](./reference/sync.md#强约束清单)
81
87
 
82
88
  ## 常用链接
83
89
 
package/docs/api/api.md CHANGED
@@ -8,6 +8,7 @@
8
8
  - [目录](#目录)
9
9
  - [概述](#概述)
10
10
  - [核心特性](#核心特性)
11
+ - [强约束清单](#强约束清单)
11
12
  - [目录结构](#目录结构-1)
12
13
  - [项目 API](#项目-api)
13
14
  - [Addon API](#addon-api)
@@ -78,6 +79,15 @@ Befly 框架的 API 系统是一套基于约定优于配置的接口开发体系
78
79
 
79
80
  ---
80
81
 
82
+ ## 强约束清单
83
+
84
+ - **权限/路由匹配只看 pathname**:系统内部用于路由匹配与权限判断的值均为 `url.pathname`(例如 `/api/user/login`),与 method 无关。
85
+ - **禁止写法**:禁止把权限或路由路径写成 `POST/api/...` 或 `POST /api/...`(这些只是一种“请求行展示写法”,不能进入任何存储/配置/权限集合)。
86
+ - **routePath 必须严格合法**:必须以 `/api/` 开头、不得包含空格、不得出现 `/api//`。
87
+ - **同一路径多方法共用权限**:同一个 pathname(例如 `/api/user/login`)即使同时注册 GET/POST,也共用同一套权限集合。
88
+
89
+ ---
90
+
81
91
  ## 目录结构
82
92
 
83
93
  ### 项目 API
@@ -139,7 +149,7 @@ export default {
139
149
  fields: {}, // 字段定义(验证规则)
140
150
  required: [], // 必填字段列表
141
151
  rawBody: false // 是否保留原始请求体
142
- // 缓存/限流:已统一迁移为 Hook 能力(见 hook 文档/配置),不再挂载在接口定义上
152
+ // 缓存/限流:当前实现为 Hook 能力(见 hook 文档/配置),不再挂载在接口定义上
143
153
  } as ApiRoute;
144
154
  ```
145
155
 
@@ -1276,6 +1286,8 @@ if (!ctx.user?.id) {
1276
1286
  | `addonAdmin/apis/auth/login.ts` | `POST /api/addon/addonAdmin/auth/login` |
1277
1287
  | `addonAdmin/apis/admin/list.ts` | `POST /api/addon/addonAdmin/admin/list` |
1278
1288
 
1289
+ > 注意:上表中的 `POST /api/...` 是“请求行示意”。系统内部生成并存储的 `ctx.route` / 数据库 `routePath` / 角色权限 `role.apis` / Redis 权限缓存都只使用 `url.pathname`(例如 `/api/user/login`),与 method 无关;权限数据禁止写成 `POST /api/...` 或 `POST/api/...`。
1290
+
1279
1291
  ### 多方法注册
1280
1292
 
1281
1293
  当 `method: 'GET,POST'` 时,会同时注册两个路由:
@@ -1283,6 +1295,8 @@ if (!ctx.user?.id) {
1283
1295
  - `GET /api/user/search`
1284
1296
  - `POST /api/user/search`
1285
1297
 
1298
+ > 权限校验只看 pathname:如果同一个 pathname 同时注册了多个 method,它们共享同一套权限(以 `/api/user/search` 作为权限值)。
1299
+
1286
1300
  ---
1287
1301
 
1288
1302
  ## BeflyContext 对象
@@ -121,6 +121,8 @@ export default {
121
121
  | `apis/user/register.ts` | `POST /api/user/register` |
122
122
  | `apis/article/list.ts` | `POST /api/article/list` |
123
123
 
124
+ > 注意:上面的写法是“HTTP 请求方法 + url.pathname”。系统内部生成并存储的 `routePath`、角色权限 `role.apis` 以及 Redis 权限缓存都只使用 `url.pathname`(例如 `/api/user/login`),与 method 无关,且禁止写成 `POST/api/...` 或 `POST /api/...`。
125
+
124
126
  ---
125
127
 
126
128
  ## 配置数据库
@@ -135,7 +137,7 @@ export default {
135
137
  "type": "mysql",
136
138
  "host": "127.0.0.1",
137
139
  "port": 3306,
138
- "user": "root",
140
+ "username": "root",
139
141
  "password": "your_password",
140
142
  "database": "my_api"
141
143
  },
@@ -213,19 +215,31 @@ CREATE DATABASE my_api CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
213
215
  服务启动时会在**主进程**自动执行同步流程:
214
216
 
215
217
  1. `syncTable()`:同步表结构
216
- 2. `syncData()`:固定顺序执行 `syncApi` → `syncMenu` → `syncDev`
218
+ 2. `syncApi()`:同步接口定义
219
+ 3. `syncMenu()`:同步菜单定义
220
+ 4. `syncDev()`:同步开发账户/开发角色
221
+ 5. `syncCache()`:缓存同步收尾(cacheApis + cacheMenus + rebuildRoleApiPermissions)
217
222
 
218
223
  如需手动触发,可在代码中调用(一般不建议在请求路径中调用):
219
224
 
220
225
  ```typescript
221
- import { syncData } from "../sync/syncData.js";
222
226
  import { syncTable } from "../sync/syncTable.js";
227
+ import { syncApi } from "../sync/syncApi.js";
228
+ import { syncMenu } from "../sync/syncMenu.js";
229
+ import { syncDev } from "../sync/syncDev.js";
230
+ import { syncCache } from "../sync/syncCache.js";
231
+ import { checkMenu } from "../checks/checkMenu.js";
223
232
  import { scanSources } from "../utils/scanSources.js";
224
233
 
225
- // ctx:BeflyContext(需已具备 ctx.db / ctx.redis / ctx.config)
234
+ // ctx:BeflyContext(需已具备 ctx.db / ctx.redis / ctx.cache / ctx.config)
226
235
  const sources = await scanSources();
236
+ const checkedMenus = await checkMenu(sources.addons, { disableMenus: ctx.config.disableMenus || [] });
237
+
227
238
  await syncTable(ctx, sources.tables);
228
- await syncData();
239
+ await syncApi(ctx, sources.apis as any);
240
+ await syncMenu(ctx, checkedMenus);
241
+ await syncDev(ctx, { devEmail: ctx.config.devEmail, devPassword: ctx.config.devPassword });
242
+ await syncCache(ctx);
229
243
  ```
230
244
 
231
245
  ### 验证同步结果
@@ -220,9 +220,11 @@ const count = await befly.redis.expireBatch([
220
220
  ### saddBatch - 批量添加 Set 成员
221
221
 
222
222
  ```typescript
223
+ import { CacheKeys } from "befly/lib/cacheKeys";
224
+
223
225
  const count = await befly.redis.saddBatch([
224
- { key: "role:admin:apis", members: ["/api/user"] },
225
- { key: "role:editor:apis", members: ["/api/article"] }
226
+ { key: CacheKeys.roleApis("admin"), members: ["/api/user"] },
227
+ { key: CacheKeys.roleApis("editor"), members: ["/api/article"] }
226
228
  ]);
227
229
  // 返回: 成功添加的总成员数量
228
230
  ```
@@ -230,9 +232,11 @@ const count = await befly.redis.saddBatch([
230
232
  ### sismemberBatch - 批量检查 Set 成员
231
233
 
232
234
  ```typescript
235
+ import { CacheKeys } from "befly/lib/cacheKeys";
236
+
233
237
  const results = await befly.redis.sismemberBatch([
234
- { key: "role:admin:apis", member: "/api/user" },
235
- { key: "role:admin:apis", member: "/api/user/delete" }
238
+ { key: CacheKeys.roleApis("admin"), member: "/api/user" },
239
+ { key: CacheKeys.roleApis("admin"), member: "/api/user/delete" }
236
240
  ]);
237
241
  // 返回: [true, false]
238
242
  ```
@@ -285,11 +289,11 @@ const id = await befly.db.insData({
285
289
  import { CacheKeys } from "befly/lib/cacheKeys";
286
290
 
287
291
  // 获取键名
288
- const key = CacheKeys.apisAll(); // 'befly:apis:all'
289
- const key = CacheKeys.menusAll(); // 'befly:menus:all'
290
- const key = CacheKeys.roleInfo("admin"); // 'befly:role:info:admin'
291
- const key = CacheKeys.roleApis("admin"); // 'befly:role:apis:admin'
292
- const key = CacheKeys.tableColumns("user"); // 'befly:table:columns:user'
292
+ const apisAllKey = CacheKeys.apisAll(); // 'befly:apis:all'
293
+ const menusAllKey = CacheKeys.menusAll(); // 'befly:menus:all'
294
+ const adminRoleInfoKey = CacheKeys.roleInfo("admin"); // 'befly:role:info:admin'
295
+ const adminRoleApisKey = CacheKeys.roleApis("admin"); // 'befly:role:apis:admin'
296
+ const userTableColumnsKey = CacheKeys.tableColumns("user"); // 'befly:table:columns:user'
293
297
  ```
294
298
 
295
299
  ### 键名前缀
@@ -304,7 +308,12 @@ Redis 插件支持配置全局前缀,避免键名冲突:
304
308
  }
305
309
  ```
306
310
 
307
- 所有键会自动添加前缀:`myapp:user:1`
311
+ 所有键会自动添加前缀,最终写入 Redis 的 key 形如:`myapp:<key>`。
312
+
313
+ - 例如:你调用 `befly.redis.getString("user:1")`,实际访问的是 `myapp:user:1`
314
+ - 例如:你调用 `befly.redis.sismember(CacheKeys.roleApis("admin"), "/api/user")`,实际访问的是 `myapp:befly:role:apis:admin`
315
+
316
+ > 注意:`redis.prefix` **不允许包含** `:`,因为 RedisHelper 会自动拼接分隔符 `:`。
308
317
 
309
318
  ---
310
319
 
@@ -313,6 +322,8 @@ Redis 插件支持配置全局前缀,避免键名冲突:
313
322
  ### 场景1:表结构缓存
314
323
 
315
324
  DbHelper 自动缓存表字段信息,避免重复查询数据库。
325
+
326
+ ```typescript
316
327
  // 计数 + 过期:常用于限流/风控
317
328
  // 更推荐:直接使用 Befly Core 内置的 rateLimit hook(通过 configs 配置即可)
318
329
 
@@ -323,8 +334,9 @@ const key = `ratelimit:${ctx.ip}:${ctx.route}`;
323
334
  const count = await befly.redis.incrWithExpire(key, windowSeconds);
324
335
 
325
336
  if (count > limit) {
326
- return befly.tool.No("请求过于频繁");
337
+ return befly.tool.No("请求过于频繁");
327
338
  }
339
+ ```
328
340
 
329
341
  ### 场景2:接口权限缓存
330
342