befly 2.3.2 → 3.0.0

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 (93) hide show
  1. package/apis/health/info.ts +64 -0
  2. package/apis/tool/tokenCheck.ts +51 -0
  3. package/bin/befly.ts +202 -0
  4. package/checks/conflict.ts +408 -0
  5. package/checks/table.ts +284 -0
  6. package/config/env.ts +218 -0
  7. package/config/reserved.ts +96 -0
  8. package/main.ts +101 -0
  9. package/package.json +45 -16
  10. package/plugins/{db.js → db.ts} +25 -12
  11. package/plugins/logger.ts +28 -0
  12. package/plugins/redis.ts +51 -0
  13. package/plugins/tool.ts +34 -0
  14. package/scripts/syncDb/apply.ts +171 -0
  15. package/scripts/syncDb/constants.ts +70 -0
  16. package/scripts/syncDb/ddl.ts +182 -0
  17. package/scripts/syncDb/helpers.ts +172 -0
  18. package/scripts/syncDb/index.ts +215 -0
  19. package/scripts/syncDb/schema.ts +199 -0
  20. package/scripts/syncDb/sqlite.ts +50 -0
  21. package/scripts/syncDb/state.ts +104 -0
  22. package/scripts/syncDb/table.ts +204 -0
  23. package/scripts/syncDb/tableCreate.ts +142 -0
  24. package/scripts/syncDb/tests/constants.test.ts +104 -0
  25. package/scripts/syncDb/tests/ddl.test.ts +134 -0
  26. package/scripts/syncDb/tests/helpers.test.ts +70 -0
  27. package/scripts/syncDb/types.ts +92 -0
  28. package/scripts/syncDb/version.ts +73 -0
  29. package/scripts/syncDb.ts +9 -0
  30. package/scripts/syncDev.ts +112 -0
  31. package/system.ts +149 -0
  32. package/tables/_common.json +21 -0
  33. package/tables/admin.json +10 -0
  34. package/tsconfig.json +58 -0
  35. package/types/api.d.ts +246 -0
  36. package/types/befly.d.ts +234 -0
  37. package/types/common.d.ts +215 -0
  38. package/types/context.ts +167 -0
  39. package/types/crypto.d.ts +23 -0
  40. package/types/database.d.ts +278 -0
  41. package/types/index.d.ts +16 -0
  42. package/types/index.ts +459 -0
  43. package/types/jwt.d.ts +99 -0
  44. package/types/logger.d.ts +43 -0
  45. package/types/plugin.d.ts +109 -0
  46. package/types/redis.d.ts +44 -0
  47. package/types/tool.d.ts +67 -0
  48. package/types/validator.d.ts +45 -0
  49. package/utils/addonHelper.ts +60 -0
  50. package/utils/api.ts +23 -0
  51. package/utils/{colors.js → colors.ts} +79 -21
  52. package/utils/crypto.ts +308 -0
  53. package/utils/datetime.ts +51 -0
  54. package/utils/dbHelper.ts +142 -0
  55. package/utils/errorHandler.ts +68 -0
  56. package/utils/index.ts +46 -0
  57. package/utils/jwt.ts +493 -0
  58. package/utils/logger.ts +284 -0
  59. package/utils/objectHelper.ts +68 -0
  60. package/utils/pluginHelper.ts +62 -0
  61. package/utils/redisHelper.ts +338 -0
  62. package/utils/response.ts +38 -0
  63. package/utils/{sqlBuilder.js → sqlBuilder.ts} +233 -97
  64. package/utils/sqlHelper.ts +447 -0
  65. package/utils/tableHelper.ts +167 -0
  66. package/utils/tool.ts +230 -0
  67. package/utils/typeHelper.ts +101 -0
  68. package/utils/validate.ts +451 -0
  69. package/utils/{xml.js → xml.ts} +100 -74
  70. package/.npmrc +0 -3
  71. package/.prettierignore +0 -2
  72. package/.prettierrc +0 -11
  73. package/apis/health/info.js +0 -49
  74. package/apis/tool/tokenCheck.js +0 -29
  75. package/checks/table.js +0 -221
  76. package/config/env.js +0 -62
  77. package/main.js +0 -579
  78. package/plugins/logger.js +0 -14
  79. package/plugins/redis.js +0 -32
  80. package/plugins/tool.js +0 -8
  81. package/scripts/syncDb.js +0 -603
  82. package/system.js +0 -118
  83. package/tables/common.json +0 -16
  84. package/tables/tool.json +0 -6
  85. package/utils/api.js +0 -27
  86. package/utils/crypto.js +0 -260
  87. package/utils/index.js +0 -387
  88. package/utils/jwt.js +0 -387
  89. package/utils/logger.js +0 -143
  90. package/utils/redisHelper.js +0 -74
  91. package/utils/sqlManager.js +0 -471
  92. package/utils/tool.js +0 -31
  93. package/utils/validate.js +0 -228
package/config/env.js DELETED
@@ -1,62 +0,0 @@
1
- export const Env = {
2
- // 项目模式
3
- NODE_ENV: process.env.NODE_ENV,
4
- // 应用名称
5
- APP_NAME: process.env.APP_NAME,
6
- // 加密盐
7
- MD5_SALT: process.env.MD5_SALT,
8
- // 监听端口
9
- APP_PORT: Number(process.env.APP_PORT),
10
- // 监听主机
11
- APP_HOST: process.env.APP_HOST,
12
- // 超级管理员密码
13
- DEV_PASSWORD: process.env.DEV_PASSWORD,
14
- // 请求体大小 10M
15
- BODY_LIMIT: Number(process.env.BODY_LIMIT),
16
- // 是否进行参数验证
17
- PARAMS_CHECK: process.env.PARAMS_CHECK,
18
- // 日志等级
19
- LOG_LEVEL: process.env.LOG_LEVEL,
20
- LOG_EXCLUDE_FIELDS: process.env.LOG_EXCLUDE_FIELDS,
21
- LOG_DIR: process.env.LOG_DIR,
22
- LOG_TO_CONSOLE: Number(process.env.LOG_TO_CONSOLE),
23
- LOG_MAX_SIZE: Number(process.env.LOG_MAX_SIZE),
24
- // 时区
25
- TZ: process.env.TZ,
26
- // 数据库配置
27
- MYSQL_ENABLE: Number(process.env.MYSQL_ENABLE),
28
- MYSQL_HOST: process.env.MYSQL_HOST,
29
- MYSQL_PORT: Number(process.env.MYSQL_PORT),
30
- MYSQL_DB: process.env.MYSQL_DB,
31
- MYSQL_USER: process.env.MYSQL_USER,
32
- MYSQL_PASSWORD: process.env.MYSQL_PASSWORD,
33
- MYSQL_DEBUG: Number(process.env.MYSQL_DEBUG),
34
- MYSQL_POOL_MAX: Number(process.env.MYSQL_POOL_MAX),
35
- // Redis配置
36
- REDIS_URL: process.env.REDIS_URL,
37
- REDIS_ENABLE: Number(process.env.REDIS_ENABLE),
38
- REDIS_HOST: process.env.REDIS_HOST,
39
- REDIS_PORT: Number(process.env.REDIS_PORT),
40
- REDIS_USERNAME: process.env.REDIS_USERNAME,
41
- REDIS_PASSWORD: process.env.REDIS_PASSWORD,
42
- REDIS_DB: Number(process.env.REDIS_DB),
43
- REDIS_KEY_PREFIX: process.env.REDIS_KEY_PREFIX,
44
- // JWT配置
45
- JWT_SECRET: process.env.JWT_SECRET,
46
- JWT_EXPIRES_IN: process.env.JWT_EXPIRES_IN,
47
- JWT_ALGORITHM: process.env.JWT_ALGORITHM,
48
- // 邮件配置
49
- MAIL_HOST: process.env.MAIL_HOST,
50
- MAIL_PORT: Number(process.env.MAIL_PORT),
51
- MAIL_POOL: process.env.MAIL_POOL,
52
- MAIL_SECURE: process.env.MAIL_SECURE,
53
- MAIL_USER: process.env.MAIL_USER,
54
- MAIL_PASS: process.env.MAIL_PASS,
55
- MAIL_SENDER: process.env.MAIL_SENDER,
56
- MAIL_ADDRESS: process.env.MAIL_ADDRESS,
57
- // 同步脚本开关(用于 core/scripts/syncDb.js)
58
- SYNC_MERGE_ALTER: process.env.SYNC_MERGE_ALTER,
59
- SYNC_ONLINE_INDEX: process.env.SYNC_ONLINE_INDEX,
60
- SYNC_DISALLOW_SHRINK: process.env.SYNC_DISALLOW_SHRINK,
61
- SYNC_ALLOW_TYPE_CHANGE: process.env.SYNC_ALLOW_TYPE_CHANGE
62
- };
package/main.js DELETED
@@ -1,579 +0,0 @@
1
- import path from 'node:path';
2
- import { Env } from './config/env.js';
3
- import { Api } from './utils/api.js';
4
- import { Logger } from './utils/logger.js';
5
- import { Jwt } from './utils/jwt.js';
6
- import { validator } from './utils/validate.js';
7
- import { Crypto2 } from './utils/crypto.js';
8
- import { Xml } from './utils/xml.js';
9
- import { SyncDb } from './scripts/syncDb.js';
10
- import { __dirchecks, __dirplugins, __dirapis, getProjectDir } from './system.js';
11
- import { isEmptyObject, isType, pickFields, sortPlugins, RYes, RNo, filterLogFields, setCorsOptions, calcPerfTime } from './utils/index.js';
12
-
13
- class Befly {
14
- constructor(options = {}) {
15
- this.apiRoutes = new Map();
16
- this.pluginLists = [];
17
- this.appContext = {};
18
- this.appOptions = options;
19
- }
20
-
21
- async initCheck() {
22
- try {
23
- const checkStartTime = Bun.nanoseconds();
24
-
25
- const glob = new Bun.Glob('*.js');
26
-
27
- // 统计信息
28
- let totalChecks = 0;
29
- let passedChecks = 0;
30
- let failedChecks = 0;
31
-
32
- // 扫描并执行检查函数
33
- for await (const file of glob.scan({
34
- cwd: __dirchecks,
35
- onlyFiles: true,
36
- absolute: true
37
- })) {
38
- const fileName = path.basename(file);
39
- if (fileName.startsWith('_')) continue; // 跳过以下划线开头的文件
40
-
41
- try {
42
- totalChecks++;
43
- const singleCheckStart = Bun.nanoseconds();
44
-
45
- // 导入检查模块
46
- const checkModule = await import(file);
47
-
48
- // 仅允许具名导出(以 check 开头)的检查函数
49
- let checkFn = null;
50
- for (const [exportName, exportValue] of Object.entries(checkModule)) {
51
- if (typeof exportValue === 'function' && /^check/i.test(exportName)) {
52
- checkFn = exportValue;
53
- break;
54
- }
55
- }
56
-
57
- // 执行检查函数
58
- if (typeof checkFn === 'function') {
59
- const checkResult = await checkFn(this.appContext);
60
- const singleCheckTime = calcPerfTime(singleCheckStart);
61
-
62
- if (checkResult === true) {
63
- passedChecks++;
64
- Logger.info(`检查 ${fileName} 通过,耗时: ${singleCheckTime}`);
65
- } else {
66
- Logger.error(`检查未通过: ${fileName},耗时: ${singleCheckTime}`);
67
- failedChecks++;
68
- }
69
- } else {
70
- const singleCheckTime = calcPerfTime(singleCheckStart);
71
- Logger.warn(`文件 ${fileName} 未找到可执行的检查函数(必须具名导出以 check 开头的函数),耗时: ${singleCheckTime}`);
72
- failedChecks++;
73
- }
74
- } catch (error) {
75
- const singleCheckTime = calcPerfTime(singleCheckStart);
76
- Logger.error({
77
- msg: `检查失败 ${fileName},耗时: ${singleCheckTime}`,
78
- error: error.message,
79
- stack: error.stack
80
- });
81
- failedChecks++;
82
- }
83
- }
84
-
85
- const totalCheckTime = calcPerfTime(checkStartTime);
86
-
87
- // 输出检查结果统计
88
- Logger.info(`系统检查完成! 总耗时: ${totalCheckTime},总检查数: ${totalChecks}, 通过: ${passedChecks}, 失败: ${failedChecks}`);
89
-
90
- if (failedChecks > 0) {
91
- process.exit();
92
- } else if (totalChecks > 0) {
93
- Logger.info(`所有系统检查通过!`);
94
- } else {
95
- Logger.info(`未执行任何检查`);
96
- }
97
- } catch (error) {
98
- Logger.error({
99
- msg: '执行系统检查时发生错误',
100
- error: error.message,
101
- stack: error.stack
102
- });
103
- process.exit();
104
- }
105
- }
106
-
107
- async loadPlugins() {
108
- try {
109
- const loadStartTime = Bun.nanoseconds();
110
-
111
- const glob = new Bun.Glob('*.js');
112
- const corePlugins = [];
113
- const userPlugins = [];
114
- const loadedPluginNames = new Set(); // 用于跟踪已加载的插件名称
115
- let hadPluginError = false; // 统一记录插件阶段是否有错误
116
-
117
- // 扫描核心插件目录
118
- const corePluginsScanStart = Bun.nanoseconds();
119
- for await (const file of glob.scan({
120
- cwd: __dirplugins,
121
- onlyFiles: true,
122
- absolute: true
123
- })) {
124
- const fileName = path.basename(file, '.js');
125
- if (fileName.startsWith('_')) continue;
126
-
127
- try {
128
- const importStart = Bun.nanoseconds();
129
- const plugin = await import(file);
130
- const importTime = calcPerfTime(importStart);
131
-
132
- const pluginInstance = plugin.default;
133
- pluginInstance.pluginName = fileName;
134
- corePlugins.push(pluginInstance);
135
- loadedPluginNames.add(fileName); // 记录已加载的核心插件名称
136
-
137
- Logger.info(`核心插件 ${fileName} 导入耗时: ${importTime}`);
138
- } catch (err) {
139
- hadPluginError = true;
140
- Logger.error({
141
- msg: `核心插件 ${fileName} 导入失败`,
142
- error: err.message,
143
- stack: err.stack
144
- });
145
- }
146
- }
147
- const corePluginsScanTime = calcPerfTime(corePluginsScanStart);
148
- Logger.info(`核心插件扫描完成,耗时: ${corePluginsScanTime},共找到 ${corePlugins.length} 个插件`);
149
-
150
- const sortedCorePlugins = sortPlugins(corePlugins);
151
- if (sortedCorePlugins === false) {
152
- Logger.error(`插件依赖关系错误,请检查插件的 after 属性`);
153
- process.exit();
154
- }
155
-
156
- // 初始化核心插件
157
- const corePluginsInitStart = Bun.nanoseconds();
158
- for (const plugin of sortedCorePlugins) {
159
- try {
160
- this.pluginLists.push(plugin);
161
- this.appContext[plugin.pluginName] = typeof plugin?.onInit === 'function' ? await plugin?.onInit(this.appContext) : {};
162
- } catch (error) {
163
- hadPluginError = true;
164
- Logger.warn(`插件 ${plugin.pluginName} 初始化失败: ${error.message}`);
165
- }
166
- }
167
- const corePluginsInitTime = calcPerfTime(corePluginsInitStart);
168
- Logger.info(`核心插件初始化完成,耗时: ${corePluginsInitTime}`);
169
-
170
- // 扫描用户插件目录
171
- const userPluginsScanStart = Bun.nanoseconds();
172
- for await (const file of glob.scan({
173
- cwd: getProjectDir('plugins'),
174
- onlyFiles: true,
175
- absolute: true
176
- })) {
177
- const fileName = path.basename(file, '.js');
178
- if (fileName.startsWith('_')) continue;
179
-
180
- // 检查是否已经加载了同名的核心插件
181
- if (loadedPluginNames.has(fileName)) {
182
- Logger.info(`跳过用户插件 ${fileName},因为同名的核心插件已存在`);
183
- continue;
184
- }
185
-
186
- try {
187
- const importStart = Bun.nanoseconds();
188
- const plugin = await import(file);
189
- const importTime = calcPerfTime(importStart);
190
-
191
- const pluginInstance = plugin.default;
192
- pluginInstance.pluginName = fileName;
193
- userPlugins.push(pluginInstance);
194
-
195
- Logger.info(`用户插件 ${fileName} 导入耗时: ${importTime}`);
196
- } catch (err) {
197
- hadPluginError = true;
198
- Logger.error({
199
- msg: `用户插件 ${fileName} 导入失败`,
200
- error: err.message,
201
- stack: err.stack
202
- });
203
- }
204
- }
205
- const userPluginsScanTime = calcPerfTime(userPluginsScanStart);
206
- Logger.info(`用户插件扫描完成,耗时: ${userPluginsScanTime},共找到 ${userPlugins.length} 个插件`);
207
-
208
- const sortedUserPlugins = sortPlugins(userPlugins);
209
- if (sortedUserPlugins === false) {
210
- Logger.error(`插件依赖关系错误,请检查插件的 after 属性`);
211
- process.exit();
212
- }
213
-
214
- // 初始化用户插件
215
- if (userPlugins.length > 0) {
216
- const userPluginsInitStart = Bun.nanoseconds();
217
- for (const plugin of sortedUserPlugins) {
218
- try {
219
- this.pluginLists.push(plugin);
220
- this.appContext[plugin.pluginName] = typeof plugin?.onInit === 'function' ? await plugin?.onInit(this.appContext) : {};
221
- } catch (error) {
222
- hadPluginError = true;
223
- Logger.warn(`插件 ${plugin.pluginName} 初始化失败: ${error.message}`);
224
- }
225
- }
226
- const userPluginsInitTime = calcPerfTime(userPluginsInitStart);
227
- Logger.info(`用户插件初始化完成,耗时: ${userPluginsInitTime}`);
228
- }
229
-
230
- const totalLoadTime = calcPerfTime(loadStartTime);
231
- const totalPluginCount = sortedCorePlugins.length + sortedUserPlugins.length;
232
- Logger.info(`插件加载完成! 总耗时: ${totalLoadTime},共加载 ${totalPluginCount} 个插件`);
233
-
234
- // 如果任意插件导入或初始化失败,统一退出进程
235
- if (hadPluginError) {
236
- Logger.error('检测到插件导入或初始化失败,进程即将退出');
237
- process.exit(1);
238
- }
239
- } catch (error) {
240
- Logger.error({
241
- msg: '加载插件时发生错误',
242
- error: error.message,
243
- stack: error.stack
244
- });
245
- // 兜底退出,避免服务在插件阶段异常后继续运行
246
- process.exit(1);
247
- }
248
- }
249
- async loadApis(dirName) {
250
- try {
251
- const loadStartTime = Bun.nanoseconds();
252
- const dirDisplayName = dirName === 'core' ? '核心' : '用户';
253
-
254
- const glob = new Bun.Glob('**/*.js');
255
- const apiDir = dirName === 'core' ? __dirapis : getProjectDir('apis');
256
-
257
- let totalApis = 0;
258
- let loadedApis = 0;
259
- let failedApis = 0;
260
-
261
- // 扫描指定目录
262
- for await (const file of glob.scan({
263
- cwd: apiDir,
264
- onlyFiles: true,
265
- absolute: true
266
- })) {
267
- const fileName = path.basename(file, '.js');
268
- const apiPath = path.relative(apiDir, file).replace(/\.js$/, '').replace(/\\/g, '/');
269
- if (apiPath.indexOf('_') !== -1) continue;
270
-
271
- totalApis++;
272
- const singleApiStart = Bun.nanoseconds();
273
-
274
- try {
275
- const api = (await import(file)).default;
276
- if (isType(api.name, 'string') === false || api.name.trim() === '') {
277
- throw new Error(`接口 ${apiPath} 的 name 属性必须是非空字符串`);
278
- }
279
- if (isType(api.auth, 'boolean') === false && isType(api.auth, 'array') === false && isType(api.auth, 'string') === false) {
280
- throw new Error(`接口 ${apiPath} 的 auth 属性必须是布尔值或字符串或字符串数组`);
281
- }
282
- if (isType(api.fields, 'object') === false) {
283
- throw new Error(`接口 ${apiPath} 的 fields 属性必须是对象`);
284
- }
285
- if (isType(api.required, 'array') === false) {
286
- throw new Error(`接口 ${apiPath} 的 required 属性必须是数组`);
287
- }
288
- // 数组的每一项都必须是字符串
289
- if (api.required.some((item) => isType(item, 'string') === false)) {
290
- throw new Error(`接口 ${apiPath} 的 required 属性必须是字符串数组`);
291
- }
292
- if (isType(api.handler, 'function') === false) {
293
- throw new Error(`接口 ${apiPath} 的 handler 属性必须是函数`);
294
- }
295
- api.route = `${api.method.toUpperCase()}/api/${dirName}/${apiPath}`;
296
- this.apiRoutes.set(api.route, api);
297
-
298
- const singleApiTime = calcPerfTime(singleApiStart);
299
- loadedApis++;
300
- // Logger.info(`${dirDisplayName}接口 ${apiPath} 加载成功,耗时: ${singleApiTime}`);
301
- } catch (error) {
302
- const singleApiTime = calcPerfTime(singleApiStart);
303
- failedApis++;
304
- Logger.error({
305
- msg: `${dirDisplayName}接口 ${apiPath} 加载失败,耗时: ${singleApiTime}`,
306
- error: error.message,
307
- stack: error.stack
308
- });
309
- }
310
- }
311
-
312
- const totalLoadTime = calcPerfTime(loadStartTime);
313
- Logger.info(`${dirDisplayName}接口加载完成! 总耗时: ${totalLoadTime},总数: ${totalApis}, 成功: ${loadedApis}, 失败: ${failedApis}`);
314
- } catch (error) {
315
- Logger.error({
316
- msg: '加载接口时发生错误',
317
- error: error.message,
318
- stack: error.stack
319
- });
320
- }
321
- }
322
-
323
- /**
324
- * 启动服务器
325
- */
326
- async listen(callback) {
327
- const serverStartTime = Bun.nanoseconds();
328
- Logger.info('开始启动 Befly 服务器...');
329
-
330
- await this.initCheck();
331
- await this.loadPlugins();
332
- await this.loadApis('core');
333
- await this.loadApis('app');
334
-
335
- const totalStartupTime = calcPerfTime(serverStartTime);
336
- Logger.info(`服务器启动准备完成,总耗时: ${totalStartupTime}`);
337
-
338
- const server = Bun.serve({
339
- port: Env.APP_PORT,
340
- hostname: Env.APP_HOST,
341
- routes: {
342
- '/': async (req) => {
343
- const corsOptions = setCorsOptions(req);
344
- return Response.json(
345
- {
346
- code: 0,
347
- msg: 'Befly 接口服务已启动',
348
- data: {
349
- mode: Env.NODE_ENV
350
- }
351
- },
352
- {
353
- headers: corsOptions.headers
354
- }
355
- );
356
- },
357
- '/api/*': async (req) => {
358
- try {
359
- const corsOptions = setCorsOptions(req);
360
-
361
- // 直接返回options请求
362
- if (req.method === 'OPTIONS') {
363
- return new Response(null, {
364
- status: 204,
365
- headers: corsOptions.headers
366
- });
367
- }
368
-
369
- // 初始化请求数据存储
370
- const ctx = {
371
- headers: Object.fromEntries(req.headers.entries()),
372
- body: {},
373
- user: {}
374
- };
375
-
376
- // 接口处理
377
- const url = new URL(req.url);
378
- const apiPath = `${req.method}${url.pathname}`;
379
-
380
- const api = this.apiRoutes.get(apiPath);
381
-
382
- // 接口不存在
383
- if (!api) {
384
- return Response.json(RNo('接口不存在'), {
385
- headers: corsOptions.headers
386
- });
387
- }
388
-
389
- const authHeader = req.headers.get('authorization');
390
- if (authHeader && authHeader.startsWith('Bearer ')) {
391
- const token = authHeader.substring(7);
392
-
393
- try {
394
- const payload = await Jwt.verify(token);
395
- ctx.user = payload;
396
- } catch (error) {
397
- ctx.user = {};
398
- }
399
- } else {
400
- ctx.user = {};
401
- }
402
- // 配置参数
403
- if (req.method === 'GET') {
404
- if (isEmptyObject(api.fields) === false) {
405
- ctx.body = pickFields(Object.fromEntries(url.searchParams), Object.keys(api.fields));
406
- } else {
407
- ctx.body = Object.fromEntries(url.searchParams);
408
- }
409
- }
410
- if (req.method === 'POST') {
411
- try {
412
- const contentType = req.headers.get('content-type') || '';
413
-
414
- if (contentType.indexOf('json') !== -1) {
415
- ctx.body = await req.json();
416
- } else if (contentType.indexOf('xml') !== -1) {
417
- const textData = await req.text();
418
- const xmlData = Xml.parse(textData);
419
- ctx.body = xmlData?.xml ? xmlData.xml : xmlData;
420
- } else if (contentType.indexOf('form-data') !== -1) {
421
- ctx.body = await req.formData();
422
- } else if (contentType.indexOf('x-www-form-urlencoded') !== -1) {
423
- const text = await req.text();
424
- const formData = new URLSearchParams(text);
425
- ctx.body = Object.fromEntries(formData);
426
- } else {
427
- ctx.body = {};
428
- }
429
- if (isEmptyObject(api.fields) === false) {
430
- ctx.body = pickFields(ctx.body, Object.keys(api.fields));
431
- }
432
- } catch (err) {
433
- Logger.error({
434
- msg: '处理请求参数时发生错误',
435
- error: err.message,
436
- stack: err.stack
437
- });
438
-
439
- return Response.json(RNo('无效的请求参数格式'), {
440
- headers: corsOptions.headers
441
- });
442
- }
443
- }
444
-
445
- // 插件钩子
446
- for await (const plugin of this.pluginLists) {
447
- try {
448
- if (typeof plugin?.onGet === 'function') {
449
- await plugin?.onGet(this.appContext, ctx, req);
450
- }
451
- } catch (error) {
452
- Logger.error({
453
- msg: '插件处理请求时发生错误',
454
- error: error.message,
455
- stack: error.stack
456
- });
457
- }
458
- }
459
-
460
- // 请求记录
461
- Logger.info({
462
- msg: '通用接口日志',
463
- 请求路径: apiPath,
464
- 请求方法: req.method,
465
- 用户信息: ctx.user,
466
- 请求体: filterLogFields(ctx.body, Env.LOG_EXCLUDE_FIELDS)
467
- });
468
-
469
- // 登录验证 auth 有3种值 分别为 true、false、['admin', 'user']
470
- if (api.auth === true && !ctx.user.id) {
471
- return Response.json(RNo('未登录', {}, { login: 'no' }), {
472
- headers: corsOptions.headers
473
- });
474
- }
475
-
476
- // 如果为字符串,则判断是否等于角色类型
477
- if (isType(api.auth, 'string') && api.auth !== ctx.user?.role_type) {
478
- return Response.json(RNo('没有权限'), {
479
- headers: corsOptions.headers
480
- });
481
- }
482
-
483
- // 如果为数组,则判断角色是否在数组中
484
- if (isType(api.auth, 'array') && !api.auth.includes(ctx.user?.role)) {
485
- return Response.json(RNo('没有权限'), {
486
- headers: corsOptions.headers
487
- });
488
- }
489
-
490
- // 参数验证
491
- const validate = validator.validate(ctx.body, api.fields, api.required);
492
- if (validate.code !== 0) {
493
- return Response.json(RNo('无效的请求参数格式', validate.fields), {
494
- headers: corsOptions.headers
495
- });
496
- }
497
-
498
- // 执行函数
499
- const result = await api.handler(this.appContext, ctx, req);
500
-
501
- // 返回数据
502
- if (result && typeof result === 'object' && 'code' in result) {
503
- return Response.json(result, {
504
- headers: corsOptions.headers
505
- });
506
- } else {
507
- return new Response(result, {
508
- headers: corsOptions.headers
509
- });
510
- }
511
- } catch (error) {
512
- Logger.error({
513
- msg: '处理接口请求时发生错误',
514
- error: error.message,
515
- stack: error.stack,
516
- url: req.url
517
- });
518
- return Response.json(RNo('内部服务器错误'), {
519
- headers: corsOptions.headers
520
- });
521
- }
522
- },
523
- '/*': async (req) => {
524
- const corsOptions = setCorsOptions(req);
525
-
526
- // 直接返回options请求
527
- if (req.method === 'OPTIONS') {
528
- return new Response(null, {
529
- status: 204,
530
- headers: corsOptions.headers
531
- });
532
- }
533
-
534
- const url = new URL(req.url);
535
- const filePath = path.join(getProjectDir('public'), url.pathname);
536
-
537
- try {
538
- const file = await Bun.file(filePath);
539
- if (await file.exists()) {
540
- return new Response(file, {
541
- headers: {
542
- 'Content-Type': file.type || 'application/octet-stream',
543
- ...corsOptions.headers
544
- }
545
- });
546
- } else {
547
- return Response.json(RNo('文件未找到'), {
548
- headers: corsOptions.headers
549
- });
550
- }
551
- } catch (error) {
552
- return Response.json(RNo('文件读取失败'), {
553
- headers: corsOptions.headers
554
- });
555
- }
556
- },
557
- ...(this.appOptions.routes || {})
558
- },
559
- error(error) {
560
- Logger.error({
561
- msg: '服务启动时发生错误',
562
- error: error.message,
563
- stack: error.stack
564
- });
565
- return Response.json(RNo('内部服务器错误'));
566
- }
567
- });
568
-
569
- const finalStartupTime = calcPerfTime(serverStartTime);
570
- Logger.info(`Befly 服务器启动成功! 完整启动耗时: ${finalStartupTime}`);
571
- Logger.info(`服务器监听地址: http://${Env.APP_HOST}:${Env.APP_PORT}`);
572
-
573
- if (callback && typeof callback === 'function') {
574
- callback(server);
575
- }
576
- }
577
- }
578
-
579
- export { Befly, Env, Api, Jwt, Crypto2, Logger, RYes, RNo, SyncDb };
package/plugins/logger.js DELETED
@@ -1,14 +0,0 @@
1
- import { Env } from '../config/env.js';
2
- import { Logger } from '../utils/logger.js';
3
-
4
- export default {
5
- after: [],
6
- async onInit(befly) {
7
- try {
8
- return Logger;
9
- } catch (error) {
10
- // 插件内禁止直接退出进程,抛出异常交由主流程统一处理
11
- throw error;
12
- }
13
- }
14
- };
package/plugins/redis.js DELETED
@@ -1,32 +0,0 @@
1
- import { redis } from 'bun';
2
- import { Env } from '../config/env.js';
3
- import { Logger } from '../utils/logger.js';
4
- import { RedisHelper, getRedisClient } from '../utils/redisHelper.js';
5
-
6
- export default {
7
- after: ['_logger'],
8
- async onInit(befly) {
9
- try {
10
- if (Env.REDIS_ENABLE === 1) {
11
- const client = getRedisClient();
12
- if ((await client.ping()) !== 'PONG') {
13
- throw new Error('Redis 连接失败');
14
- }
15
-
16
- // 返回工具对象,向下游以相同 API 暴露
17
- return RedisHelper;
18
- } else {
19
- Logger.warn(`Redis 未启用,跳过初始化`);
20
- return {};
21
- }
22
- } catch (err) {
23
- Logger.error({
24
- msg: 'Redis 初始化失败',
25
- message: err.message,
26
- stack: err.stack
27
- });
28
- // 插件内禁止直接退出进程,抛出异常交由主流程统一处理
29
- throw err;
30
- }
31
- }
32
- };