befly 3.16.11 → 3.17.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.
- package/README.md +6 -0
- package/apis/admin/cacheRefresh.js +122 -0
- package/apis/admin/del.js +34 -0
- package/apis/admin/detail.js +23 -0
- package/apis/admin/ins.js +69 -0
- package/apis/admin/list.js +28 -0
- package/apis/admin/upd.js +95 -0
- package/apis/api/all.js +24 -0
- package/apis/api/list.js +31 -0
- package/apis/auth/login.js +123 -0
- package/apis/auth/sendSmsCode.js +24 -0
- package/apis/dashboard/configStatus.js +39 -0
- package/apis/dashboard/environmentInfo.js +43 -0
- package/apis/dashboard/performanceMetrics.js +20 -0
- package/apis/dashboard/permissionStats.js +27 -0
- package/apis/dashboard/serviceStatus.js +75 -0
- package/apis/dashboard/systemInfo.js +19 -0
- package/apis/dashboard/systemOverview.js +30 -0
- package/apis/dashboard/systemResources.js +106 -0
- package/apis/dict/all.js +23 -0
- package/apis/dict/del.js +16 -0
- package/apis/dict/detail.js +27 -0
- package/apis/dict/ins.js +51 -0
- package/apis/dict/items.js +30 -0
- package/apis/dict/list.js +36 -0
- package/apis/dict/upd.js +74 -0
- package/apis/dictType/all.js +16 -0
- package/apis/dictType/del.js +38 -0
- package/apis/dictType/detail.js +20 -0
- package/apis/dictType/ins.js +37 -0
- package/apis/dictType/list.js +26 -0
- package/apis/dictType/upd.js +51 -0
- package/apis/email/config.js +25 -0
- package/apis/email/logList.js +23 -0
- package/apis/email/send.js +66 -0
- package/apis/email/verify.js +21 -0
- package/apis/loginLog/list.js +23 -0
- package/apis/menu/all.js +41 -0
- package/apis/menu/list.js +25 -0
- package/apis/operateLog/list.js +23 -0
- package/apis/role/all.js +21 -0
- package/apis/role/apiSave.js +43 -0
- package/apis/role/apis.js +22 -0
- package/apis/role/del.js +49 -0
- package/apis/role/detail.js +32 -0
- package/apis/role/ins.js +46 -0
- package/apis/role/list.js +27 -0
- package/apis/role/menuSave.js +42 -0
- package/apis/role/menus.js +22 -0
- package/apis/role/save.js +40 -0
- package/apis/role/upd.js +50 -0
- package/apis/sysConfig/all.js +16 -0
- package/apis/sysConfig/del.js +36 -0
- package/apis/sysConfig/get.js +49 -0
- package/apis/sysConfig/ins.js +50 -0
- package/apis/sysConfig/list.js +24 -0
- package/apis/sysConfig/upd.js +62 -0
- package/checks/api.js +55 -0
- package/checks/config.js +107 -0
- package/checks/hook.js +38 -0
- package/checks/menu.js +58 -0
- package/checks/plugin.js +38 -0
- package/checks/table.js +78 -0
- package/configs/beflyConfig.json +61 -0
- package/configs/beflyMenus.json +85 -0
- package/configs/constConfig.js +34 -0
- package/configs/regexpAlias.json +55 -0
- package/hooks/auth.js +34 -0
- package/hooks/cors.js +39 -0
- package/hooks/parser.js +90 -0
- package/hooks/permission.js +71 -0
- package/hooks/validator.js +43 -0
- package/index.js +326 -0
- package/lib/cacheHelper.js +483 -0
- package/lib/cacheKeys.js +42 -0
- package/lib/connect.js +120 -0
- package/lib/dbHelper/builders.js +698 -0
- package/lib/dbHelper/context.js +131 -0
- package/lib/dbHelper/dataOps.js +505 -0
- package/lib/dbHelper/execute.js +65 -0
- package/lib/dbHelper/index.js +27 -0
- package/lib/dbHelper/transaction.js +43 -0
- package/lib/dbHelper/util.js +58 -0
- package/lib/dbHelper/validate.js +549 -0
- package/lib/emailHelper.js +110 -0
- package/lib/logger.js +604 -0
- package/lib/redisHelper.js +684 -0
- package/lib/sqlBuilder/batch.js +113 -0
- package/lib/sqlBuilder/check.js +150 -0
- package/lib/sqlBuilder/compiler.js +347 -0
- package/lib/sqlBuilder/errors.js +60 -0
- package/lib/sqlBuilder/index.js +218 -0
- package/lib/sqlBuilder/parser.js +296 -0
- package/lib/sqlBuilder/util.js +260 -0
- package/lib/validator.js +303 -0
- package/package.json +20 -12
- package/paths.js +112 -0
- package/plugins/cache.js +16 -0
- package/plugins/config.js +11 -0
- package/plugins/email.js +27 -0
- package/plugins/logger.js +20 -0
- package/plugins/mysql.js +36 -0
- package/plugins/redis.js +34 -0
- package/plugins/tool.js +155 -0
- package/router/api.js +140 -0
- package/router/static.js +71 -0
- package/scripts/syncDb/context.js +99 -0
- package/scripts/syncDb/diff.js +133 -0
- package/scripts/syncDb/index.js +70 -0
- package/scripts/syncDb/query.js +26 -0
- package/scripts/syncDb/report.js +190 -0
- package/scripts/syncDb/transform.js +111 -0
- package/sql/admin.sql +18 -0
- package/sql/api.sql +12 -0
- package/sql/dict.sql +13 -0
- package/sql/dictType.sql +12 -0
- package/sql/emailLog.sql +20 -0
- package/sql/loginLog.sql +25 -0
- package/sql/menu.sql +12 -0
- package/sql/operateLog.sql +22 -0
- package/sql/role.sql +14 -0
- package/sql/sysConfig.sql +16 -0
- package/sync/api.js +93 -0
- package/sync/cache.js +13 -0
- package/sync/dev.js +171 -0
- package/sync/menu.js +99 -0
- package/tables/admin.json +56 -0
- package/tables/api.json +26 -0
- package/tables/dict.json +30 -0
- package/tables/dictType.json +24 -0
- package/tables/emailLog.json +61 -0
- package/tables/loginLog.json +86 -0
- package/tables/menu.json +24 -0
- package/tables/operateLog.json +68 -0
- package/tables/role.json +32 -0
- package/tables/sysConfig.json +43 -0
- package/utils/calcPerfTime.js +13 -0
- package/utils/cors.js +17 -0
- package/utils/deepMerge.js +78 -0
- package/utils/fieldClear.js +65 -0
- package/utils/formatYmdHms.js +23 -0
- package/utils/formatZodIssues.js +109 -0
- package/utils/getClientIp.js +47 -0
- package/utils/importDefault.js +51 -0
- package/utils/is.js +462 -0
- package/utils/loggerUtils.js +185 -0
- package/utils/processInfo.js +39 -0
- package/utils/regexpUtil.js +52 -0
- package/utils/response.js +114 -0
- package/utils/scanFiles.js +124 -0
- package/utils/scanSources.js +68 -0
- package/utils/sortModules.js +75 -0
- package/utils/toSessionTtlSeconds.js +14 -0
- package/utils/util.js +374 -0
- package/befly.js +0 -12769
- package/befly.min.js +0 -47
package/index.js
ADDED
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Befly 框架主入口文件
|
|
3
|
+
* 提供简洁的框架接口,核心逻辑已提取到 loader 层
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// 配置
|
|
7
|
+
import beflyConfig from "./configs/beflyConfig.json";
|
|
8
|
+
import beflyMenus from "./configs/beflyMenus.json";
|
|
9
|
+
|
|
10
|
+
// 检查
|
|
11
|
+
import { checkApi } from "./checks/api.js";
|
|
12
|
+
import { checkConfig } from "./checks/config.js";
|
|
13
|
+
import { checkHook } from "./checks/hook.js";
|
|
14
|
+
import { checkMenu } from "./checks/menu.js";
|
|
15
|
+
import { checkPlugin } from "./checks/plugin.js";
|
|
16
|
+
import { checkTable } from "./checks/table.js";
|
|
17
|
+
// ========== 相对导入(项目内部文件) ==========
|
|
18
|
+
// 基础设施
|
|
19
|
+
import { Connect } from "./lib/connect.js";
|
|
20
|
+
import { Logger } from "./lib/logger.js";
|
|
21
|
+
// 路由处理
|
|
22
|
+
import { apiHandler } from "./router/api.js";
|
|
23
|
+
import { staticHandler } from "./router/static.js";
|
|
24
|
+
// 同步
|
|
25
|
+
import { syncApi } from "./sync/api.js";
|
|
26
|
+
import { syncCache } from "./sync/cache.js";
|
|
27
|
+
import { syncDev } from "./sync/dev.js";
|
|
28
|
+
import { syncMenu } from "./sync/menu.js";
|
|
29
|
+
import { syncDbApply, syncDbCheck } from "./scripts/syncDb/index.js";
|
|
30
|
+
// 工具
|
|
31
|
+
import { calcPerfTime } from "./utils/calcPerfTime.js";
|
|
32
|
+
import { scanSources } from "./utils/scanSources.js";
|
|
33
|
+
import { isPrimaryProcess } from "./utils/is.js";
|
|
34
|
+
import { deepMerge } from "./utils/deepMerge.js";
|
|
35
|
+
import { omit } from "./utils/util.js";
|
|
36
|
+
import { sortModules } from "./utils/sortModules.js";
|
|
37
|
+
|
|
38
|
+
function prefixMenuPaths(menus, prefix) {
|
|
39
|
+
const output = [];
|
|
40
|
+
for (const menu of menus) {
|
|
41
|
+
const nextPath = menu.path === "/" ? `/${prefix}` : `/${prefix}${menu.path}`;
|
|
42
|
+
const nextMenu = {
|
|
43
|
+
name: menu.name,
|
|
44
|
+
path: nextPath,
|
|
45
|
+
sort: menu.sort
|
|
46
|
+
};
|
|
47
|
+
if (Array.isArray(menu.children)) {
|
|
48
|
+
const children = [];
|
|
49
|
+
for (const child of menu.children) {
|
|
50
|
+
children.push({
|
|
51
|
+
name: child.name,
|
|
52
|
+
path: child.path,
|
|
53
|
+
sort: child.sort
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
nextMenu.children = children;
|
|
57
|
+
}
|
|
58
|
+
output.push(nextMenu);
|
|
59
|
+
}
|
|
60
|
+
return output;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async function ensureSyncPrerequisites(ctx) {
|
|
64
|
+
const missingCtxKeys = [];
|
|
65
|
+
if (!ctx.redis) missingCtxKeys.push("ctx.redis");
|
|
66
|
+
if (!ctx.mysql) missingCtxKeys.push("ctx.mysql");
|
|
67
|
+
if (!ctx.cache) missingCtxKeys.push("ctx.cache");
|
|
68
|
+
if (missingCtxKeys.length > 0) {
|
|
69
|
+
throw new Error(`启动失败:${missingCtxKeys.join("、")} 未初始化`, {
|
|
70
|
+
cause: null,
|
|
71
|
+
code: "runtime",
|
|
72
|
+
subsystem: "sync",
|
|
73
|
+
operation: "ctxCheck",
|
|
74
|
+
missingCtxKeys: missingCtxKeys
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const requiredTables = ["beflyApi", "beflyMenu", "beflyAdmin", "beflyRole"];
|
|
79
|
+
const missingTables = [];
|
|
80
|
+
|
|
81
|
+
for (const table of requiredTables) {
|
|
82
|
+
const tableExistsResult = await ctx.mysql.tableExists(table);
|
|
83
|
+
if (!tableExistsResult.data) {
|
|
84
|
+
missingTables.push(table);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (missingTables.length > 0) {
|
|
89
|
+
throw new Error(`同步依赖表缺失: ${missingTables.join("、")}`, {
|
|
90
|
+
cause: null,
|
|
91
|
+
code: "runtime",
|
|
92
|
+
subsystem: "sync",
|
|
93
|
+
operation: "tableExists",
|
|
94
|
+
missingTables: missingTables
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Befly 框架核心类
|
|
101
|
+
* 职责:管理应用上下文和生命周期
|
|
102
|
+
*/
|
|
103
|
+
export class Befly {
|
|
104
|
+
/** 应用上下文 */
|
|
105
|
+
context = {
|
|
106
|
+
env: {},
|
|
107
|
+
config: {}
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
// 菜单配置
|
|
111
|
+
menus = [];
|
|
112
|
+
|
|
113
|
+
/** 插件列表 */
|
|
114
|
+
plugins = [];
|
|
115
|
+
|
|
116
|
+
/** 钩子列表 */
|
|
117
|
+
hooks = [];
|
|
118
|
+
|
|
119
|
+
/** API 路由映射表 */
|
|
120
|
+
apis = {};
|
|
121
|
+
|
|
122
|
+
/** create 阶段扫描得到的 API 列表 */
|
|
123
|
+
scanApis = [];
|
|
124
|
+
|
|
125
|
+
/** create 阶段扫描得到的插件列表 */
|
|
126
|
+
scanPlugins = [];
|
|
127
|
+
|
|
128
|
+
/** create 阶段扫描得到的钩子列表 */
|
|
129
|
+
scanHooks = [];
|
|
130
|
+
|
|
131
|
+
/** 是否由工厂创建 */
|
|
132
|
+
createdByFactory = false;
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* A 方案:异步工厂,先做预检,失败则不返回实例。
|
|
136
|
+
*/
|
|
137
|
+
static async create(env = {}, config = {}, menus = []) {
|
|
138
|
+
const instance = new Befly();
|
|
139
|
+
instance.context.env = env;
|
|
140
|
+
instance.context.config = deepMerge(beflyConfig, config);
|
|
141
|
+
instance.menus = deepMerge(prefixMenuPaths(beflyMenus, "core"), prefixMenuPaths(menus, "app"));
|
|
142
|
+
instance.createdByFactory = true;
|
|
143
|
+
|
|
144
|
+
const configHasError = await checkConfig(instance.context.config);
|
|
145
|
+
const { apis, tables, plugins, hooks } = await scanSources();
|
|
146
|
+
|
|
147
|
+
const apiHasError = await checkApi(apis);
|
|
148
|
+
const tableHasError = await checkTable(tables);
|
|
149
|
+
const pluginHasError = await checkPlugin(plugins);
|
|
150
|
+
const hookHasError = await checkHook(hooks);
|
|
151
|
+
const menuHasError = await checkMenu(instance.menus);
|
|
152
|
+
|
|
153
|
+
if (configHasError || apiHasError || tableHasError || pluginHasError || hookHasError || menuHasError) {
|
|
154
|
+
throw new Error("检查失败:存在配置/结构问题", {
|
|
155
|
+
cause: null,
|
|
156
|
+
code: "policy",
|
|
157
|
+
subsystem: "checks",
|
|
158
|
+
operation: "checkAll"
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
instance.scanApis = apis;
|
|
163
|
+
instance.scanPlugins = plugins;
|
|
164
|
+
instance.scanHooks = hooks;
|
|
165
|
+
|
|
166
|
+
return instance;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* 启动完整的生命周期流程
|
|
171
|
+
* @returns HTTP 服务器实例
|
|
172
|
+
*/
|
|
173
|
+
async start() {
|
|
174
|
+
try {
|
|
175
|
+
if (!this.createdByFactory) {
|
|
176
|
+
throw new Error("请使用 Befly.create(...) 创建实例后再调用 start", {
|
|
177
|
+
cause: null,
|
|
178
|
+
code: "policy",
|
|
179
|
+
subsystem: "start",
|
|
180
|
+
operation: "factoryGuard"
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const serverStartTime = Bun.nanoseconds();
|
|
185
|
+
if (!Array.isArray(this.scanApis) || !Array.isArray(this.scanPlugins) || !Array.isArray(this.scanHooks)) {
|
|
186
|
+
throw new Error("请使用 Befly.create(...) 完成预检后再调用 start", {
|
|
187
|
+
cause: null,
|
|
188
|
+
code: "policy",
|
|
189
|
+
subsystem: "start",
|
|
190
|
+
operation: "preflightGuard"
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// 1. 启动期建立基础连接(SQL + Redis)
|
|
195
|
+
// 说明:连接职责收敛到启动期单点;插件只消费已连接实例(Connect.getSql/getRedis)。
|
|
196
|
+
await Connect.connectRedis(this.context.config.redis);
|
|
197
|
+
await Connect.connectMysql(this.context.config.mysql);
|
|
198
|
+
|
|
199
|
+
// 2. 加载插件
|
|
200
|
+
const sortedPlugins = sortModules(this.scanPlugins);
|
|
201
|
+
if (sortedPlugins) {
|
|
202
|
+
for (const item of sortedPlugins) {
|
|
203
|
+
const pluginInstance = await item.handler(this.context);
|
|
204
|
+
this.context[item.fileName] = pluginInstance;
|
|
205
|
+
}
|
|
206
|
+
} else {
|
|
207
|
+
throw new Error("插件依赖关系错误", {
|
|
208
|
+
cause: null,
|
|
209
|
+
code: "policy"
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
await ensureSyncPrerequisites(this.context);
|
|
213
|
+
|
|
214
|
+
// 5. 自动同步(PM2 cluster:主进程执行,其它进程等待同步完成)
|
|
215
|
+
if (isPrimaryProcess(this.context.env)) {
|
|
216
|
+
await syncApi(this.context, this.scanApis);
|
|
217
|
+
await syncMenu(this.context, this.menus);
|
|
218
|
+
await syncDev(this.context);
|
|
219
|
+
await syncCache(this.context);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// 3. 加载钩子
|
|
223
|
+
const sortedHooks = sortModules(this.scanHooks);
|
|
224
|
+
if (sortedHooks) {
|
|
225
|
+
this.hooks = sortedHooks;
|
|
226
|
+
} else {
|
|
227
|
+
throw new Error("钩子依赖关系错误", {
|
|
228
|
+
cause: null,
|
|
229
|
+
code: "policy"
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// 4. 加载所有 API
|
|
234
|
+
for (const api of this.scanApis) {
|
|
235
|
+
this.apis[api.apiPath] = api;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// 6. 启动 HTTP服务器
|
|
239
|
+
const apiFetch = apiHandler(this.apis, this.hooks, this.context);
|
|
240
|
+
const staticFetch = staticHandler(this.context.config.cors);
|
|
241
|
+
|
|
242
|
+
const server = Bun.serve({
|
|
243
|
+
port: this.context.config.appPort || 3000,
|
|
244
|
+
hostname: this.context.config.appHost || "0.0.0.0",
|
|
245
|
+
// 开发模式下启用详细错误信息
|
|
246
|
+
development: this.context.env.RUN_MODE === "development",
|
|
247
|
+
// 空闲连接超时时间(秒),防止恶意连接占用资源
|
|
248
|
+
fetch: async (req, server) => {
|
|
249
|
+
const url = new URL(req.url);
|
|
250
|
+
|
|
251
|
+
if (url.pathname === "/") {
|
|
252
|
+
return Response.json({ code: 0, msg: `${this.context.config.appName} 接口服务已启动` });
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if (url.pathname.startsWith("/api/")) {
|
|
256
|
+
return apiFetch(req, server);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
return staticFetch(req);
|
|
260
|
+
},
|
|
261
|
+
error: (error) => {
|
|
262
|
+
Logger.error("服务启动时发生错误", error);
|
|
263
|
+
// 开发模式下返回详细错误信息
|
|
264
|
+
if (this.context.env.RUN_MODE === "development") {
|
|
265
|
+
return Response.json(
|
|
266
|
+
{
|
|
267
|
+
code: 1,
|
|
268
|
+
msg: "内部服务器错误",
|
|
269
|
+
error: error.message,
|
|
270
|
+
stack: error.stack
|
|
271
|
+
},
|
|
272
|
+
{ status: 200 }
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
return Response.json({ code: 1, msg: "内部服务器错误" }, { status: 200 });
|
|
276
|
+
}
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
const finalStartupTime = calcPerfTime(serverStartTime);
|
|
280
|
+
|
|
281
|
+
Logger.info(`${this.context.config.appName} 启动成功!`);
|
|
282
|
+
Logger.info(`服务器启动耗时: ${finalStartupTime}`);
|
|
283
|
+
Logger.info(`服务器监听地址: ${server.url}`);
|
|
284
|
+
console.log(`${this.context.config.appName} 启动成功!`);
|
|
285
|
+
console.log(`服务器启动耗时: ${finalStartupTime}`);
|
|
286
|
+
console.log(`服务器监听地址: ${server.url}`);
|
|
287
|
+
// 注意:作为库代码,这里不注册 SIGINT/SIGTERM 处理器,也不调用 process.exit。
|
|
288
|
+
// 宿主应用应自行处理信号并决定退出策略(包括是否调用 server.stop / Connect.disconnect / Logger.flush)。
|
|
289
|
+
return server;
|
|
290
|
+
} catch (error) {
|
|
291
|
+
// 注意:这里不能直接 process.exit(1)
|
|
292
|
+
// - Logger 是异步缓冲写入,exit 会导致日志来不及 flush(实际项目里表现为“完全没打印”)
|
|
293
|
+
// - 作为库代码,也不应该强行终止宿主进程
|
|
294
|
+
|
|
295
|
+
// 注意:测试要求 start() 失败时必须 Logger.error 一次,所以这里始终调用 error。
|
|
296
|
+
Logger.error("项目启动失败", error, null, false);
|
|
297
|
+
|
|
298
|
+
// 尽力把错误日志落盘/输出后再把异常抛给上层。
|
|
299
|
+
try {
|
|
300
|
+
await Logger.flush();
|
|
301
|
+
await Logger.shutdown();
|
|
302
|
+
} catch {
|
|
303
|
+
// ignore
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
throw new Error("运行时错误", {
|
|
307
|
+
cause: error,
|
|
308
|
+
code: error?.code || "runtime",
|
|
309
|
+
subsystem: "start",
|
|
310
|
+
operation: "start"
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
async function dbCheck(config = {}) {
|
|
317
|
+
const mergedConfig = deepMerge(beflyConfig, config);
|
|
318
|
+
return syncDbCheck(mergedConfig.mysql);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
async function dbApply(config = {}) {
|
|
322
|
+
const mergedConfig = deepMerge(beflyConfig, config);
|
|
323
|
+
return syncDbApply(mergedConfig.mysql);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
export { dbApply, dbCheck, Logger };
|