befly 3.10.18 → 3.10.19
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 +83 -307
- package/dist/befly.config.d.ts +7 -0
- package/{befly.config.ts → dist/befly.config.js} +10 -33
- package/dist/checks/checkApi.d.ts +1 -0
- package/{checks/checkApi.ts → dist/checks/checkApi.js} +11 -28
- package/dist/checks/checkHook.d.ts +1 -0
- package/{checks/checkHook.ts → dist/checks/checkHook.js} +11 -20
- package/dist/checks/checkMenu.d.ts +7 -0
- package/{checks/checkMenu.ts → dist/checks/checkMenu.js} +18 -53
- package/dist/checks/checkPlugin.d.ts +1 -0
- package/{checks/checkPlugin.ts → dist/checks/checkPlugin.js} +11 -20
- package/dist/checks/checkTable.d.ts +6 -0
- package/{checks/checkTable.ts → dist/checks/checkTable.js} +17 -41
- package/dist/configs/presetFields.d.ts +4 -0
- package/{configs/presetFields.ts → dist/configs/presetFields.js} +1 -1
- package/dist/configs/presetRegexp.d.ts +145 -0
- package/{utils/regex.ts → dist/configs/presetRegexp.js} +8 -31
- package/dist/hooks/auth.d.ts +5 -0
- package/{hooks/auth.ts → dist/hooks/auth.js} +6 -10
- package/dist/hooks/cors.d.ts +9 -0
- package/{hooks/cors.ts → dist/hooks/cors.js} +3 -13
- package/dist/hooks/parser.d.ts +12 -0
- package/{hooks/parser.ts → dist/hooks/parser.js} +29 -44
- package/dist/hooks/permission.d.ts +12 -0
- package/{hooks/permission.ts → dist/hooks/permission.js} +14 -25
- package/dist/hooks/validator.d.ts +9 -0
- package/{hooks/validator.ts → dist/hooks/validator.js} +7 -14
- package/dist/lib/asyncContext.d.ts +21 -0
- package/dist/lib/asyncContext.js +27 -0
- package/dist/lib/cacheHelper.d.ts +95 -0
- package/{lib/cacheHelper.ts → dist/lib/cacheHelper.js} +45 -105
- package/dist/lib/cacheKeys.d.ts +23 -0
- package/{lib/cacheKeys.ts → dist/lib/cacheKeys.js} +5 -10
- package/dist/lib/cipher.d.ts +153 -0
- package/{lib/cipher.ts → dist/lib/cipher.js} +23 -44
- package/dist/lib/connect.d.ts +91 -0
- package/{lib/connect.ts → dist/lib/connect.js} +47 -88
- package/dist/lib/dbDialect.d.ts +87 -0
- package/{lib/dbDialect.ts → dist/lib/dbDialect.js} +32 -112
- package/dist/lib/dbHelper.d.ts +204 -0
- package/{lib/dbHelper.ts → dist/lib/dbHelper.js} +83 -240
- package/dist/lib/dbUtils.d.ts +68 -0
- package/{lib/dbUtils.ts → dist/lib/dbUtils.js} +51 -125
- package/dist/lib/jwt.d.ts +13 -0
- package/{lib/jwt.ts → dist/lib/jwt.js} +11 -32
- package/dist/lib/logger.d.ts +32 -0
- package/{lib/logger.ts → dist/lib/logger.js} +202 -279
- package/dist/lib/redisHelper.d.ts +185 -0
- package/{lib/redisHelper.ts → dist/lib/redisHelper.js} +97 -141
- package/dist/lib/sqlBuilder.d.ts +160 -0
- package/{lib/sqlBuilder.ts → dist/lib/sqlBuilder.js} +132 -278
- package/dist/lib/sqlCheck.d.ts +23 -0
- package/{lib/sqlCheck.ts → dist/lib/sqlCheck.js} +24 -41
- package/dist/lib/validator.d.ts +45 -0
- package/{lib/validator.ts → dist/lib/validator.js} +44 -61
- package/dist/loader/loadApis.d.ts +12 -0
- package/{loader/loadApis.ts → dist/loader/loadApis.js} +9 -19
- package/dist/loader/loadHooks.d.ts +8 -0
- package/{loader/loadHooks.ts → dist/loader/loadHooks.js} +7 -21
- package/dist/loader/loadPlugins.d.ts +8 -0
- package/{loader/loadPlugins.ts → dist/loader/loadPlugins.js} +10 -22
- package/dist/main.d.ts +26 -0
- package/{main.ts → dist/main.js} +60 -99
- package/dist/paths.d.ts +93 -0
- package/{paths.ts → dist/paths.js} +6 -19
- package/dist/plugins/cache.d.ts +14 -0
- package/{plugins/cache.ts → dist/plugins/cache.js} +5 -12
- package/dist/plugins/cipher.d.ts +10 -0
- package/{plugins/cipher.ts → dist/plugins/cipher.js} +2 -6
- package/dist/plugins/config.d.ts +10 -0
- package/dist/plugins/config.js +6 -0
- package/dist/plugins/db.d.ts +14 -0
- package/{plugins/db.ts → dist/plugins/db.js} +9 -17
- package/dist/plugins/jwt.d.ts +10 -0
- package/dist/plugins/jwt.js +10 -0
- package/dist/plugins/logger.d.ts +28 -0
- package/{plugins/logger.ts → dist/plugins/logger.js} +3 -8
- package/dist/plugins/redis.d.ts +14 -0
- package/{plugins/redis.ts → dist/plugins/redis.js} +7 -12
- package/dist/plugins/tool.d.ts +79 -0
- package/{plugins/tool.ts → dist/plugins/tool.js} +7 -30
- package/dist/router/api.d.ts +14 -0
- package/dist/router/api.js +107 -0
- package/dist/router/static.d.ts +9 -0
- package/{router/static.ts → dist/router/static.js} +20 -34
- package/dist/scripts/ensureDist.d.ts +1 -0
- package/dist/scripts/ensureDist.js +80 -0
- package/dist/sync/syncApi.d.ts +3 -0
- package/{sync/syncApi.ts → dist/sync/syncApi.js} +34 -54
- package/dist/sync/syncCache.d.ts +2 -0
- package/{sync/syncCache.ts → dist/sync/syncCache.js} +1 -6
- package/dist/sync/syncDev.d.ts +6 -0
- package/{sync/syncDev.ts → dist/sync/syncDev.js} +29 -62
- package/dist/sync/syncMenu.d.ts +14 -0
- package/{sync/syncMenu.ts → dist/sync/syncMenu.js} +65 -125
- package/dist/sync/syncTable.d.ts +151 -0
- package/{sync/syncTable.ts → dist/sync/syncTable.js} +171 -378
- package/{types → dist/types}/api.d.ts +8 -47
- package/dist/types/api.js +4 -0
- package/{types → dist/types}/befly.d.ts +31 -222
- package/dist/types/befly.js +4 -0
- package/{types → dist/types}/cache.d.ts +7 -15
- package/dist/types/cache.js +4 -0
- package/dist/types/cipher.d.ts +27 -0
- package/dist/types/cipher.js +7 -0
- package/{types → dist/types}/common.d.ts +8 -33
- package/dist/types/common.js +5 -0
- package/{types → dist/types}/context.d.ts +2 -4
- package/dist/types/context.js +4 -0
- package/{types → dist/types}/crypto.d.ts +0 -3
- package/dist/types/crypto.js +4 -0
- package/dist/types/database.d.ts +138 -0
- package/dist/types/database.js +4 -0
- package/dist/types/hook.d.ts +15 -0
- package/dist/types/hook.js +6 -0
- package/dist/types/jwt.d.ts +75 -0
- package/dist/types/jwt.js +4 -0
- package/dist/types/logger.d.ts +47 -0
- package/dist/types/logger.js +6 -0
- package/dist/types/plugin.d.ts +14 -0
- package/dist/types/plugin.js +6 -0
- package/dist/types/redis.d.ts +71 -0
- package/dist/types/redis.js +4 -0
- package/{types/roleApisCache.ts → dist/types/roleApisCache.d.ts} +0 -2
- package/dist/types/roleApisCache.js +8 -0
- package/dist/types/sync.d.ts +92 -0
- package/dist/types/sync.js +4 -0
- package/dist/types/table.d.ts +34 -0
- package/dist/types/table.js +4 -0
- package/dist/types/validate.d.ts +67 -0
- package/dist/types/validate.js +4 -0
- package/dist/utils/arrayKeysToCamel.d.ts +13 -0
- package/{utils/arrayKeysToCamel.ts → dist/utils/arrayKeysToCamel.js} +5 -5
- package/dist/utils/calcPerfTime.d.ts +4 -0
- package/{utils/calcPerfTime.ts → dist/utils/calcPerfTime.js} +3 -3
- package/dist/utils/configTypes.d.ts +1 -0
- package/dist/utils/configTypes.js +1 -0
- package/dist/utils/convertBigIntFields.d.ts +11 -0
- package/{utils/convertBigIntFields.ts → dist/utils/convertBigIntFields.js} +5 -9
- package/dist/utils/cors.d.ts +8 -0
- package/{utils/cors.ts → dist/utils/cors.js} +1 -3
- package/dist/utils/disableMenusGlob.d.ts +13 -0
- package/{utils/disableMenusGlob.ts → dist/utils/disableMenusGlob.js} +9 -29
- package/dist/utils/fieldClear.d.ts +11 -0
- package/{utils/fieldClear.ts → dist/utils/fieldClear.js} +15 -33
- package/dist/utils/genShortId.d.ts +10 -0
- package/{utils/genShortId.ts → dist/utils/genShortId.js} +1 -1
- package/dist/utils/getClientIp.d.ts +6 -0
- package/{utils/getClientIp.ts → dist/utils/getClientIp.js} +1 -7
- package/dist/utils/importDefault.d.ts +1 -0
- package/dist/utils/importDefault.js +29 -0
- package/dist/utils/isDirentDirectory.d.ts +2 -0
- package/{utils/isDirentDirectory.ts → dist/utils/isDirentDirectory.js} +3 -8
- package/dist/utils/keysToCamel.d.ts +10 -0
- package/{utils/keysToCamel.ts → dist/utils/keysToCamel.js} +4 -5
- package/dist/utils/keysToSnake.d.ts +10 -0
- package/{utils/keysToSnake.ts → dist/utils/keysToSnake.js} +4 -5
- package/dist/utils/loadMenuConfigs.d.ts +5 -0
- package/{utils/loadMenuConfigs.ts → dist/utils/loadMenuConfigs.js} +24 -51
- package/dist/utils/pickFields.d.ts +4 -0
- package/{utils/pickFields.ts → dist/utils/pickFields.js} +2 -5
- package/dist/utils/process.d.ts +24 -0
- package/{utils/process.ts → dist/utils/process.js} +2 -18
- package/dist/utils/processFields.d.ts +4 -0
- package/{utils/processFields.ts → dist/utils/processFields.js} +5 -9
- package/dist/utils/regex.d.ts +145 -0
- package/{configs/presetRegexp.ts → dist/utils/regex.js} +8 -31
- package/dist/utils/response.d.ts +20 -0
- package/{utils/response.ts → dist/utils/response.js} +28 -49
- package/dist/utils/scanAddons.d.ts +17 -0
- package/{utils/scanAddons.ts → dist/utils/scanAddons.js} +6 -40
- package/dist/utils/scanConfig.d.ts +26 -0
- package/{utils/scanConfig.ts → dist/utils/scanConfig.js} +22 -59
- package/dist/utils/scanFiles.d.ts +30 -0
- package/{utils/scanFiles.ts → dist/utils/scanFiles.js} +26 -66
- package/dist/utils/scanSources.d.ts +10 -0
- package/dist/utils/scanSources.js +41 -0
- package/dist/utils/sortModules.d.ts +28 -0
- package/{utils/sortModules.ts → dist/utils/sortModules.js} +25 -65
- package/dist/utils/sqlLog.d.ts +14 -0
- package/{utils/sqlLog.ts → dist/utils/sqlLog.js} +2 -14
- package/package.json +14 -28
- package/.gitignore +0 -0
- package/bunfig.toml +0 -3
- package/docs/README.md +0 -98
- package/docs/api/api.md +0 -1921
- package/docs/guide/examples.md +0 -926
- package/docs/guide/quickstart.md +0 -354
- package/docs/hooks/auth.md +0 -38
- package/docs/hooks/cors.md +0 -28
- package/docs/hooks/hook.md +0 -838
- package/docs/hooks/parser.md +0 -19
- package/docs/hooks/rateLimit.md +0 -47
- package/docs/infra/redis.md +0 -628
- package/docs/plugins/cipher.md +0 -61
- package/docs/plugins/database.md +0 -189
- package/docs/plugins/plugin.md +0 -986
- package/docs/reference/addon.md +0 -510
- package/docs/reference/config.md +0 -573
- package/docs/reference/logger.md +0 -495
- package/docs/reference/sync.md +0 -478
- package/docs/reference/table.md +0 -763
- package/docs/reference/validator.md +0 -620
- package/lib/asyncContext.ts +0 -43
- package/plugins/config.ts +0 -13
- package/plugins/jwt.ts +0 -15
- package/router/api.ts +0 -130
- package/tsconfig.json +0 -8
- package/types/database.d.ts +0 -541
- package/types/hook.d.ts +0 -25
- package/types/jwt.d.ts +0 -118
- package/types/logger.d.ts +0 -65
- package/types/plugin.d.ts +0 -19
- package/types/redis.d.ts +0 -83
- package/types/sync.d.ts +0 -398
- package/types/table.d.ts +0 -216
- package/types/validate.d.ts +0 -69
- package/utils/configTypes.ts +0 -3
- package/utils/importDefault.ts +0 -21
- package/utils/scanSources.ts +0 -64
package/README.md
CHANGED
|
@@ -4,356 +4,132 @@
|
|
|
4
4
|
|
|
5
5
|
> 道生一,一生二,二生三,三生万物
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
专为 Bun 运行时设计的 API 框架:插件化、自动同步、内置权限与缓存。
|
|
8
8
|
|
|
9
|
-
##
|
|
9
|
+
## 运行环境
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
- Bun >= 1.0
|
|
12
|
+
- MySQL >= 8.0(或 PostgreSQL / SQLite)
|
|
13
|
+
- Redis >= 6.0(可选,但推荐)
|
|
12
14
|
|
|
13
|
-
|
|
14
|
-
- 🚀 **高性能** - 基于 Bun 运行时,超快的启动和执行速度
|
|
15
|
-
- 🔌 **插件化架构** - 灵活的插件系统,轻松扩展功能
|
|
16
|
-
- 🗄️ **多数据库支持** - MySQL、PostgreSQL、SQLite 统一接口
|
|
17
|
-
- 📝 **自动化表管理** - 基于 JSON 的表定义,自动同步数据库结构
|
|
18
|
-
- 🔐 **内置身份验证** - JWT 认证,角色权限管理
|
|
19
|
-
- 📊 **完整日志系统** - 结构化日志,敏感字段过滤
|
|
15
|
+
## 安装与启动
|
|
20
16
|
|
|
21
|
-
|
|
17
|
+
### 1) 安装
|
|
22
18
|
|
|
23
|
-
|
|
19
|
+
在你的项目里安装:
|
|
24
20
|
|
|
25
21
|
```bash
|
|
26
|
-
# 创建新项目
|
|
27
|
-
mkdir my-api && cd my-api
|
|
28
|
-
|
|
29
|
-
# 安装 Befly
|
|
30
22
|
bun add befly
|
|
31
|
-
|
|
32
|
-
# 初始化项目(即将支持)
|
|
33
|
-
bunx befly init
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
### 最简示例
|
|
37
|
-
|
|
38
|
-
```typescript
|
|
39
|
-
// main.ts
|
|
40
|
-
import { Befly } from "befly";
|
|
41
|
-
|
|
42
|
-
const app = new Befly({
|
|
43
|
-
appName: "My API",
|
|
44
|
-
appPort: 3000
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
await app.start();
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
运行项目:
|
|
51
|
-
|
|
52
|
-
```bash
|
|
53
|
-
bun run main.ts
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
### 创建第一个接口
|
|
57
|
-
|
|
58
|
-
```typescript
|
|
59
|
-
// apis/user/hello.ts
|
|
60
|
-
import type { ApiRoute } from "befly/types/api";
|
|
61
|
-
|
|
62
|
-
export default {
|
|
63
|
-
name: "问候接口",
|
|
64
|
-
auth: false, // 公开接口
|
|
65
|
-
fields: {},
|
|
66
|
-
handler: async (befly, ctx) => {
|
|
67
|
-
return {
|
|
68
|
-
msg: "Hello, Befly!",
|
|
69
|
-
data: {
|
|
70
|
-
timestamp: Date.now()
|
|
71
|
-
}
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
} as ApiRoute;
|
|
75
23
|
```
|
|
76
24
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
fields: {
|
|
92
|
-
id: "用户ID|number|1|999999|null|1|null"
|
|
93
|
-
},
|
|
94
|
-
required: ["id"],
|
|
95
|
-
handler: async (befly: BeflyContext, ctx) => {
|
|
96
|
-
const { id } = ctx.body;
|
|
97
|
-
|
|
98
|
-
// 类型安全的数据库查询
|
|
99
|
-
const user = await befly.db.getOne<User>({
|
|
100
|
-
table: "user",
|
|
101
|
-
where: { id }
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
return { msg: "查询成功", data: user };
|
|
105
|
-
}
|
|
106
|
-
} as ApiRoute;
|
|
25
|
+
### 2) 项目结构(约定)
|
|
26
|
+
|
|
27
|
+
```text
|
|
28
|
+
my-api/
|
|
29
|
+
├── apis/ # 项目 API(扫描 ts/js)
|
|
30
|
+
│ └── user/
|
|
31
|
+
│ └── hello.ts
|
|
32
|
+
├── tables/ # 表定义 JSON(可选)
|
|
33
|
+
│ └── user.json
|
|
34
|
+
├── configs/ # 配置文件(必需)
|
|
35
|
+
│ ├── befly.common.json
|
|
36
|
+
│ ├── befly.development.json
|
|
37
|
+
│ └── befly.production.json
|
|
38
|
+
└── main.ts
|
|
107
39
|
```
|
|
108
40
|
|
|
109
|
-
###
|
|
41
|
+
### 3) 配置文件
|
|
110
42
|
|
|
111
|
-
|
|
112
|
-
// 查询单条
|
|
113
|
-
const user = await befly.db.getOne<User>({
|
|
114
|
-
table: "user",
|
|
115
|
-
where: { id: 1 }
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
// 分页列表
|
|
119
|
-
const result = await befly.db.getList<Product>({
|
|
120
|
-
table: "product",
|
|
121
|
-
where: { category: "electronics" },
|
|
122
|
-
page: 1,
|
|
123
|
-
limit: 10,
|
|
124
|
-
orderBy: ["createdAt#DESC"]
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
// 插入数据
|
|
128
|
-
await befly.db.insData({
|
|
129
|
-
table: "user",
|
|
130
|
-
data: {
|
|
131
|
-
username: "john",
|
|
132
|
-
email: "john@example.com"
|
|
133
|
-
}
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
// 更新数据
|
|
137
|
-
await befly.db.updData({
|
|
138
|
-
table: "user",
|
|
139
|
-
where: { id: 1 },
|
|
140
|
-
data: {
|
|
141
|
-
nickname: "John Doe"
|
|
142
|
-
}
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
// 删除数据
|
|
146
|
-
await befly.db.delData({
|
|
147
|
-
table: "user",
|
|
148
|
-
where: { id: 1 }
|
|
149
|
-
});
|
|
150
|
-
```
|
|
151
|
-
|
|
152
|
-
### 智能表定义
|
|
43
|
+
`configs/befly.development.json` 示例:
|
|
153
44
|
|
|
154
45
|
```json
|
|
155
46
|
{
|
|
156
|
-
"
|
|
157
|
-
"
|
|
158
|
-
"
|
|
159
|
-
|
|
160
|
-
"
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
请参考:`docs/reference/sync.md`(`syncTable` / `syncData` 等同步流程说明)。
|
|
169
|
-
|
|
170
|
-
## 🗄️ 数据库配置
|
|
171
|
-
|
|
172
|
-
统一使用环境变量配置,支持三种数据库:
|
|
173
|
-
|
|
174
|
-
```bash
|
|
175
|
-
# MySQL
|
|
176
|
-
DB_TYPE=mysql
|
|
177
|
-
DB_HOST=127.0.0.1
|
|
178
|
-
DB_PORT=3306
|
|
179
|
-
DB_USER=root
|
|
180
|
-
DB_PASS=password
|
|
181
|
-
DB_NAME=my_database
|
|
182
|
-
|
|
183
|
-
# PostgreSQL
|
|
184
|
-
DB_TYPE=postgresql
|
|
185
|
-
DB_HOST=localhost
|
|
186
|
-
DB_PORT=5432
|
|
187
|
-
DB_USER=postgres
|
|
188
|
-
DB_PASS=password
|
|
189
|
-
DB_NAME=my_database
|
|
190
|
-
|
|
191
|
-
# SQLite
|
|
192
|
-
DB_TYPE=sqlite
|
|
193
|
-
DB_NAME=/path/to/database.sqlite
|
|
194
|
-
# 或使用内存数据库
|
|
195
|
-
DB_NAME=:memory:
|
|
196
|
-
```
|
|
197
|
-
|
|
198
|
-
## ⚙️ 项目配置文件
|
|
199
|
-
|
|
200
|
-
Befly 使用 `befly.config.ts` 作为统一配置文件:
|
|
201
|
-
|
|
202
|
-
```typescript
|
|
203
|
-
// befly.config.ts
|
|
204
|
-
export const beflyConfig = {
|
|
205
|
-
appName: "我的应用",
|
|
206
|
-
appPort: 3000,
|
|
207
|
-
appHost: "0.0.0.0",
|
|
208
|
-
|
|
209
|
-
// 数据库配置(优先使用环境变量)
|
|
210
|
-
db: {
|
|
211
|
-
type: "mysql",
|
|
212
|
-
host: "127.0.0.1",
|
|
213
|
-
port: 3306,
|
|
214
|
-
username: "root",
|
|
215
|
-
password: "password",
|
|
216
|
-
database: "my_database"
|
|
217
|
-
},
|
|
218
|
-
|
|
219
|
-
// Redis 配置
|
|
220
|
-
redis: {
|
|
221
|
-
host: "127.0.0.1",
|
|
222
|
-
port: 6379,
|
|
223
|
-
prefix: "befly"
|
|
47
|
+
"appName": "My API",
|
|
48
|
+
"appPort": 3000,
|
|
49
|
+
"appHost": "127.0.0.1",
|
|
50
|
+
|
|
51
|
+
"db": {
|
|
52
|
+
"type": "mysql",
|
|
53
|
+
"host": "127.0.0.1",
|
|
54
|
+
"port": 3306,
|
|
55
|
+
"username": "root",
|
|
56
|
+
"password": "root",
|
|
57
|
+
"database": "my_api"
|
|
224
58
|
},
|
|
225
59
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
60
|
+
"redis": {
|
|
61
|
+
"host": "127.0.0.1",
|
|
62
|
+
"port": 6379,
|
|
63
|
+
"db": 0,
|
|
64
|
+
"prefix": "my_api"
|
|
230
65
|
},
|
|
231
66
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
}
|
|
67
|
+
"auth": {
|
|
68
|
+
"secret": "change-me",
|
|
69
|
+
"expiresIn": "7d",
|
|
70
|
+
"algorithm": "HS256"
|
|
237
71
|
}
|
|
238
|
-
}
|
|
72
|
+
}
|
|
239
73
|
```
|
|
240
74
|
|
|
241
|
-
> 注意:`redis.prefix` 不要包含
|
|
242
|
-
|
|
243
|
-
### 数据库连接
|
|
244
|
-
|
|
245
|
-
通常你不需要手动连接(框架启动期会完成连接并注入插件实例)。
|
|
75
|
+
> 注意:`redis.prefix` 不要包含 `:`,框架会自动拼接分隔符。
|
|
246
76
|
|
|
247
|
-
|
|
77
|
+
### 4) 启动入口
|
|
248
78
|
|
|
249
79
|
```typescript
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
// 连接 SQL 数据库
|
|
253
|
-
await Connect.connectSql({
|
|
254
|
-
type: "mysql",
|
|
255
|
-
host: "127.0.0.1",
|
|
256
|
-
port: 3306,
|
|
257
|
-
username: "root",
|
|
258
|
-
password: "root",
|
|
259
|
-
database: "befly_demo",
|
|
260
|
-
poolMax: 1
|
|
261
|
-
});
|
|
262
|
-
|
|
263
|
-
// 连接 Redis
|
|
264
|
-
await Connect.connectRedis({
|
|
265
|
-
host: "127.0.0.1",
|
|
266
|
-
port: 6379,
|
|
267
|
-
db: 0,
|
|
268
|
-
prefix: "befly"
|
|
269
|
-
});
|
|
270
|
-
|
|
271
|
-
// 或:同时连接 SQL 和 Redis
|
|
272
|
-
await Connect.connect({
|
|
273
|
-
db: {
|
|
274
|
-
type: "mysql",
|
|
275
|
-
host: "127.0.0.1",
|
|
276
|
-
port: 3306,
|
|
277
|
-
username: "root",
|
|
278
|
-
password: "root",
|
|
279
|
-
database: "befly_demo",
|
|
280
|
-
poolMax: 1
|
|
281
|
-
},
|
|
282
|
-
redis: {
|
|
283
|
-
host: "127.0.0.1",
|
|
284
|
-
port: 6379,
|
|
285
|
-
db: 0,
|
|
286
|
-
prefix: "befly"
|
|
287
|
-
}
|
|
288
|
-
});
|
|
289
|
-
|
|
290
|
-
// 获取连接状态
|
|
291
|
-
const status = Connect.getStatus();
|
|
292
|
-
console.log(status.sql.connected); // true/false
|
|
293
|
-
console.log(status.redis.connected); // true/false
|
|
80
|
+
// main.ts
|
|
81
|
+
import { Befly } from "befly";
|
|
294
82
|
|
|
295
|
-
|
|
296
|
-
await
|
|
83
|
+
const app = new Befly();
|
|
84
|
+
await app.start();
|
|
297
85
|
```
|
|
298
86
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
配置文件名为 `befly.config.ts`,导出名为 `beflyConfig`:
|
|
87
|
+
## 编写第一个 API
|
|
302
88
|
|
|
303
89
|
```typescript
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
90
|
+
// apis/user/hello.ts
|
|
91
|
+
import type { ApiRoute } from "befly/types/api";
|
|
92
|
+
|
|
93
|
+
export default {
|
|
94
|
+
name: "问候接口",
|
|
95
|
+
auth: false,
|
|
96
|
+
handler: async (_befly, _ctx) => {
|
|
97
|
+
return { code: 0, msg: "Hello, Befly!", data: { ts: Date.now() } };
|
|
98
|
+
}
|
|
99
|
+
} as ApiRoute;
|
|
307
100
|
```
|
|
308
101
|
|
|
309
|
-
|
|
102
|
+
启动后访问:
|
|
310
103
|
|
|
311
|
-
|
|
104
|
+
- `GET http://localhost:3000/api/app/user/hello`
|
|
312
105
|
|
|
313
|
-
|
|
314
|
-
- [核心概念](./docs/03-核心概念/)
|
|
315
|
-
- [API 开发](./docs/04-API开发/)
|
|
316
|
-
- [数据库操作](./docs/05-数据库/)
|
|
317
|
-
- [TypeScript 支持](./docs/10-TypeScript/01-TypeScript支持.md)
|
|
106
|
+
## 路由规则(重要)
|
|
318
107
|
|
|
319
|
-
|
|
108
|
+
Befly 会根据“来源 + 文件相对路径”生成 `routePath`:
|
|
320
109
|
|
|
321
|
-
-
|
|
322
|
-
-
|
|
323
|
-
-
|
|
110
|
+
- 项目 API:`apis/**/*.{ts,js}` → `/api/app/...`
|
|
111
|
+
- Addon API:
|
|
112
|
+
- `addons/<addonName>/apis/**/*.{ts,js}` → `/api/addon/<addonName>/...`
|
|
113
|
+
- `node_modules/@befly-addon/<addonName>/apis/**/*.{ts,js}` → `/api/addon/<addonName>/...`
|
|
324
114
|
|
|
325
|
-
|
|
115
|
+
并且:
|
|
326
116
|
|
|
327
|
-
|
|
117
|
+
- 系统内部用于存储/权限判断的 `routePath` **只看 `url.pathname`**(例如 `/api/app/user/login`)。
|
|
118
|
+
- **禁止**把权限写成 `POST /api/...` 或 `POST/api/...`(那只是请求行展示,不参与存储)。
|
|
328
119
|
|
|
329
|
-
|
|
330
|
-
bun run dev
|
|
331
|
-
# 访问: http://localhost:3000
|
|
332
|
-
```
|
|
120
|
+
## TypeScript 类型导入(public types)
|
|
333
121
|
|
|
334
|
-
|
|
122
|
+
只从 `befly/types/*` 子路径引入类型:
|
|
335
123
|
|
|
336
|
-
```
|
|
337
|
-
|
|
338
|
-
|
|
124
|
+
```typescript
|
|
125
|
+
import type { ApiRoute } from "befly/types/api";
|
|
126
|
+
import type { BeflyContext } from "befly/types/befly";
|
|
339
127
|
```
|
|
340
128
|
|
|
341
|
-
##
|
|
342
|
-
|
|
343
|
-
查看 `/tpl` 目录获取完整的示例项目。
|
|
344
|
-
|
|
345
|
-
## 🤝 贡献
|
|
346
|
-
|
|
347
|
-
欢迎提交 Issue 和 Pull Request!
|
|
348
|
-
|
|
349
|
-
## 📄 许可
|
|
350
|
-
|
|
351
|
-
MIT License
|
|
352
|
-
|
|
353
|
-
## 🌟 致谢
|
|
354
|
-
|
|
355
|
-
感谢所有为 Befly 做出贡献的开发者!
|
|
129
|
+
## 文档
|
|
356
130
|
|
|
357
|
-
|
|
131
|
+
更多细节请看 `packages/core/docs/`:
|
|
358
132
|
|
|
359
|
-
|
|
133
|
+
- `docs/guide/quickstart.md`
|
|
134
|
+
- `docs/reference/sync.md`
|
|
135
|
+
- `docs/plugins/plugin.md`
|
|
@@ -1,15 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
* 自动加载 configs 目录下的配置文件并与默认配置合并
|
|
4
|
-
* 支持环境分离:befly.common.json + befly.development/production.json
|
|
5
|
-
*/
|
|
6
|
-
import type { BeflyOptions } from "./types/befly.ts";
|
|
7
|
-
|
|
8
|
-
import { compileDisableMenuGlobRules } from "./utils/disableMenusGlob.ts";
|
|
9
|
-
import { scanConfig } from "./utils/scanConfig.ts";
|
|
10
|
-
|
|
1
|
+
import { compileDisableMenuGlobRules } from "./utils/disableMenusGlob.js";
|
|
2
|
+
import { scanConfig } from "./utils/scanConfig.js";
|
|
11
3
|
/** 默认配置 */
|
|
12
|
-
const defaultOptions
|
|
4
|
+
const defaultOptions = {
|
|
13
5
|
// ========== 核心参数 ==========
|
|
14
6
|
nodeEnv: "development",
|
|
15
7
|
appName: "野蜂飞舞",
|
|
@@ -19,7 +11,6 @@ const defaultOptions: BeflyOptions = {
|
|
|
19
11
|
devPassword: "beflydev123456",
|
|
20
12
|
bodyLimit: 1048576, // 1MB
|
|
21
13
|
tz: "Asia/Shanghai",
|
|
22
|
-
|
|
23
14
|
// ========== 日志配置 ==========
|
|
24
15
|
logger: {
|
|
25
16
|
debug: 1,
|
|
@@ -30,7 +21,6 @@ const defaultOptions: BeflyOptions = {
|
|
|
30
21
|
maxStringLen: 100,
|
|
31
22
|
maxArrayItems: 100
|
|
32
23
|
},
|
|
33
|
-
|
|
34
24
|
// ========== 数据库配置 ==========
|
|
35
25
|
db: {
|
|
36
26
|
type: "mysql",
|
|
@@ -41,7 +31,6 @@ const defaultOptions: BeflyOptions = {
|
|
|
41
31
|
database: "befly_demo",
|
|
42
32
|
poolMax: 10
|
|
43
33
|
},
|
|
44
|
-
|
|
45
34
|
// ========== Redis 配置 ==========
|
|
46
35
|
redis: {
|
|
47
36
|
host: "127.0.0.1",
|
|
@@ -51,14 +40,12 @@ const defaultOptions: BeflyOptions = {
|
|
|
51
40
|
db: 0,
|
|
52
41
|
prefix: "befly_demo"
|
|
53
42
|
},
|
|
54
|
-
|
|
55
43
|
// ========== 认证配置 ==========
|
|
56
44
|
auth: {
|
|
57
45
|
secret: "befly-secret",
|
|
58
46
|
expiresIn: "7d",
|
|
59
47
|
algorithm: "HS256"
|
|
60
48
|
},
|
|
61
|
-
|
|
62
49
|
// ========== CORS 配置 ==========
|
|
63
50
|
cors: {
|
|
64
51
|
origin: "*",
|
|
@@ -68,7 +55,6 @@ const defaultOptions: BeflyOptions = {
|
|
|
68
55
|
maxAge: 86400,
|
|
69
56
|
credentials: "true"
|
|
70
57
|
},
|
|
71
|
-
|
|
72
58
|
// ========== Hook:全局限流 ==========
|
|
73
59
|
rateLimit: {
|
|
74
60
|
enable: 1,
|
|
@@ -79,28 +65,22 @@ const defaultOptions: BeflyOptions = {
|
|
|
79
65
|
skipRoutes: [],
|
|
80
66
|
rules: []
|
|
81
67
|
},
|
|
82
|
-
|
|
83
68
|
// ========== 禁用配置 ==========
|
|
84
69
|
disableHooks: [],
|
|
85
70
|
disablePlugins: [],
|
|
86
71
|
disableMenus: ["**/404", "**/403", "**/500", "**/login"],
|
|
87
|
-
|
|
88
72
|
// ========== Addon 配置 ==========
|
|
89
73
|
addons: {}
|
|
90
74
|
};
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
export async function loadBeflyConfig(): Promise<BeflyOptions> {
|
|
75
|
+
const beflyConfigCache = new Map();
|
|
76
|
+
export async function loadBeflyConfig() {
|
|
95
77
|
const nodeEnv = process.env.NODE_ENV || defaultOptions.nodeEnv || "development";
|
|
96
78
|
const envSuffix = nodeEnv === "production" ? "production" : "development";
|
|
97
|
-
|
|
98
79
|
const cacheKey = nodeEnv;
|
|
99
80
|
const cached = beflyConfigCache.get(cacheKey);
|
|
100
81
|
if (cached) {
|
|
101
82
|
return await cached;
|
|
102
83
|
}
|
|
103
|
-
|
|
104
84
|
const promise = (async () => {
|
|
105
85
|
// 使用 scanConfig 一次性加载并合并所有配置文件
|
|
106
86
|
// 合并顺序:defaultOptions ← befly.common.json ← befly.development/production.json
|
|
@@ -111,30 +91,27 @@ export async function loadBeflyConfig(): Promise<BeflyOptions> {
|
|
|
111
91
|
mode: "merge",
|
|
112
92
|
defaults: defaultOptions
|
|
113
93
|
});
|
|
114
|
-
|
|
115
94
|
// 配置校验:redis.prefix 作为 key 前缀,由 RedisHelper 统一拼接 ":"。
|
|
116
95
|
// 因此 prefix 本身不允许包含 ":",否则会导致 key 结构出现空段或多段分隔(例如 "prefix::key"),
|
|
117
96
|
// 在 RedisInsight 等工具里可能显示 [NO NAME] 空分组,且容易造成 key 管理混乱。
|
|
118
|
-
const redisPrefix =
|
|
97
|
+
const redisPrefix = config?.redis?.prefix;
|
|
119
98
|
if (typeof redisPrefix === "string") {
|
|
120
99
|
const trimmedPrefix = redisPrefix.trim();
|
|
121
100
|
if (trimmedPrefix.includes(":")) {
|
|
122
101
|
throw new Error(`配置错误:redis.prefix 不允许包含 ':'(RedisHelper 会自动拼接分隔符 ':'),请改为不带冒号的前缀,例如 'befly_demo',当前值=${redisPrefix}`);
|
|
123
102
|
}
|
|
124
103
|
}
|
|
125
|
-
|
|
126
104
|
// 预编译 disableMenus 的 Bun.Glob 规则:
|
|
127
105
|
// - 提前暴露配置错误(fail-fast)
|
|
128
106
|
// - 后续 checkMenu 会复用同一进程级缓存
|
|
129
|
-
compileDisableMenuGlobRules(
|
|
130
|
-
|
|
131
|
-
return config as BeflyOptions;
|
|
107
|
+
compileDisableMenuGlobRules(config?.disableMenus);
|
|
108
|
+
return config;
|
|
132
109
|
})();
|
|
133
|
-
|
|
134
110
|
beflyConfigCache.set(cacheKey, promise);
|
|
135
111
|
try {
|
|
136
112
|
return await promise;
|
|
137
|
-
}
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
138
115
|
beflyConfigCache.delete(cacheKey);
|
|
139
116
|
throw error;
|
|
140
117
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function checkApi(apis: any[]): Promise<void>;
|
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
import { isPlainObject } from "es-toolkit/compat";
|
|
2
2
|
import { omit } from "es-toolkit/object";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
export async function checkApi(apis: any[]): Promise<void> {
|
|
3
|
+
import { Logger } from "../lib/logger.js";
|
|
4
|
+
export async function checkApi(apis) {
|
|
7
5
|
let hasError = false;
|
|
8
|
-
|
|
9
6
|
for (const api of apis) {
|
|
10
7
|
try {
|
|
11
8
|
if (typeof api?.name !== "string" || api.name.trim() === "") {
|
|
@@ -13,83 +10,69 @@ export async function checkApi(apis: any[]): Promise<void> {
|
|
|
13
10
|
hasError = true;
|
|
14
11
|
continue;
|
|
15
12
|
}
|
|
16
|
-
|
|
17
13
|
if (typeof api?.handler !== "function") {
|
|
18
14
|
Logger.warn(omit(api, ["handler"]), "接口的 handler 属性必须是函数");
|
|
19
15
|
hasError = true;
|
|
20
16
|
continue;
|
|
21
17
|
}
|
|
22
|
-
|
|
23
18
|
// routePath / routePrefix 由 scanFiles 系统生成:必须是严格的 pathname
|
|
24
19
|
if (typeof api?.routePath !== "string" || api.routePath.trim() === "") {
|
|
25
20
|
Logger.warn(omit(api, ["handler"]), "接口的 routePath 属性必须是非空字符串(由系统生成)");
|
|
26
21
|
hasError = true;
|
|
27
|
-
}
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
28
24
|
const routePath = api.routePath.trim();
|
|
29
|
-
|
|
30
25
|
// 不允许出现 "POST/api/..." 等 method 前缀
|
|
31
26
|
if (/^(GET|POST|PUT|PATCH|DELETE|OPTIONS|HEAD)\b/i.test(routePath)) {
|
|
32
27
|
Logger.warn(omit(api, ["handler"]), "接口的 routePath 不允许包含 method 前缀,应为 url.pathname(例如 /api/app/xxx)");
|
|
33
28
|
hasError = true;
|
|
34
29
|
}
|
|
35
|
-
|
|
36
30
|
if (!routePath.startsWith("/api/")) {
|
|
37
31
|
Logger.warn(omit(api, ["handler"]), "接口的 routePath 必须以 /api/ 开头");
|
|
38
32
|
hasError = true;
|
|
39
33
|
}
|
|
40
|
-
|
|
41
34
|
if (routePath.includes(" ")) {
|
|
42
35
|
Logger.warn(omit(api, ["handler"]), "接口的 routePath 不允许包含空格");
|
|
43
36
|
hasError = true;
|
|
44
37
|
}
|
|
45
|
-
|
|
46
38
|
if (routePath.includes("/api//")) {
|
|
47
39
|
Logger.warn(omit(api, ["handler"]), "接口的 routePath 不允许出现 /api//(重复斜杠)");
|
|
48
40
|
hasError = true;
|
|
49
41
|
}
|
|
50
42
|
}
|
|
51
|
-
|
|
52
43
|
if (typeof api?.routePrefix !== "string" || api.routePrefix.trim() === "") {
|
|
53
44
|
Logger.warn(omit(api, ["handler"]), "接口的 routePrefix 属性必须是非空字符串(由系统生成)");
|
|
54
45
|
hasError = true;
|
|
55
46
|
}
|
|
56
|
-
|
|
57
47
|
if (api.method && !["GET", "POST", "GET,POST", "POST,GET"].includes(String(api.method).toUpperCase())) {
|
|
58
48
|
Logger.warn(omit(api, ["handler"]), "接口的 method 属性必须是有效的 HTTP 方法 (GET, POST, GET,POST, POST,GET)");
|
|
59
49
|
hasError = true;
|
|
60
50
|
}
|
|
61
|
-
|
|
62
51
|
if (api.auth !== undefined && typeof api.auth !== "boolean") {
|
|
63
52
|
Logger.warn(omit(api, ["handler"]), "接口的 auth 属性必须是布尔值 (true=需登录, false=公开)");
|
|
64
53
|
hasError = true;
|
|
65
54
|
}
|
|
66
|
-
|
|
67
55
|
if (api.fields && !isPlainObject(api.fields)) {
|
|
68
56
|
Logger.warn(omit(api, ["handler"]), "接口的 fields 属性必须是对象");
|
|
69
57
|
hasError = true;
|
|
70
58
|
}
|
|
71
|
-
|
|
72
59
|
if (api.required && !Array.isArray(api.required)) {
|
|
73
60
|
Logger.warn(omit(api, ["handler"]), "接口的 required 属性必须是数组");
|
|
74
61
|
hasError = true;
|
|
75
62
|
}
|
|
76
|
-
|
|
77
|
-
if (api.required && api.required.some((reqItem: any) => typeof reqItem !== "string")) {
|
|
63
|
+
if (api.required && api.required.some((reqItem) => typeof reqItem !== "string")) {
|
|
78
64
|
Logger.warn(omit(api, ["handler"]), "接口的 required 属性必须是字符串数组");
|
|
79
65
|
hasError = true;
|
|
80
66
|
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
"接口解析失败"
|
|
88
|
-
);
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
Logger.error({
|
|
70
|
+
err: error,
|
|
71
|
+
item: api
|
|
72
|
+
}, "接口解析失败");
|
|
89
73
|
hasError = true;
|
|
90
74
|
}
|
|
91
75
|
}
|
|
92
|
-
|
|
93
76
|
if (hasError) {
|
|
94
77
|
throw new Error("接口结构检查失败");
|
|
95
78
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function checkHook(hooks: any[]): Promise<void>;
|