befly 3.8.25 → 3.8.27
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/config.ts +8 -9
- package/hooks/{rateLimit.ts → _rateLimit.ts} +7 -13
- package/hooks/auth.ts +3 -11
- package/hooks/cors.ts +1 -4
- package/hooks/parser.ts +6 -8
- package/hooks/permission.ts +9 -12
- package/hooks/validator.ts +6 -9
- package/lib/cacheHelper.ts +0 -4
- package/lib/{database.ts → connect.ts} +65 -18
- package/lib/logger.ts +1 -17
- package/lib/redisHelper.ts +6 -5
- package/loader/loadApis.ts +3 -3
- package/loader/loadHooks.ts +15 -41
- package/loader/loadPlugins.ts +10 -16
- package/main.ts +25 -28
- package/package.json +3 -3
- package/plugins/cache.ts +2 -2
- package/plugins/cipher.ts +15 -0
- package/plugins/config.ts +16 -0
- package/plugins/db.ts +7 -17
- package/plugins/jwt.ts +15 -0
- package/plugins/logger.ts +1 -1
- package/plugins/redis.ts +4 -4
- package/plugins/tool.ts +50 -0
- package/router/api.ts +56 -42
- package/router/static.ts +12 -12
- package/sync/syncAll.ts +2 -20
- package/sync/syncApi.ts +7 -7
- package/sync/syncDb/apply.ts +1 -4
- package/sync/syncDb/constants.ts +3 -0
- package/sync/syncDb/ddl.ts +2 -1
- package/sync/syncDb/helpers.ts +5 -117
- package/sync/syncDb/sqlite.ts +1 -3
- package/sync/syncDb/table.ts +8 -142
- package/sync/syncDb/tableCreate.ts +25 -9
- package/sync/syncDb/types.ts +125 -0
- package/sync/syncDb/version.ts +0 -3
- package/sync/syncDb.ts +146 -6
- package/sync/syncDev.ts +19 -15
- package/sync/syncMenu.ts +87 -75
- package/tests/redisHelper.test.ts +15 -16
- package/tests/sync-connection.test.ts +189 -0
- package/tests/syncDb-apply.test.ts +287 -0
- package/tests/syncDb-constants.test.ts +150 -0
- package/tests/syncDb-ddl.test.ts +205 -0
- package/tests/syncDb-helpers.test.ts +112 -0
- package/tests/syncDb-schema.test.ts +178 -0
- package/tests/syncDb-types.test.ts +129 -0
- package/tsconfig.json +2 -2
- package/types/api.d.ts +1 -1
- package/types/befly.d.ts +23 -21
- package/types/common.d.ts +0 -29
- package/types/context.d.ts +8 -6
- package/types/hook.d.ts +3 -4
- package/types/plugin.d.ts +3 -0
- package/hooks/errorHandler.ts +0 -23
- package/hooks/requestId.ts +0 -24
- package/hooks/requestLogger.ts +0 -25
- package/hooks/responseFormatter.ts +0 -64
- package/router/root.ts +0 -56
- package/sync/syncDb/index.ts +0 -164
package/loader/loadPlugins.ts
CHANGED
|
@@ -12,29 +12,24 @@ import { sortModules, scanModules } from '../util.js';
|
|
|
12
12
|
import type { Plugin } from '../types/plugin.js';
|
|
13
13
|
import type { BeflyContext } from '../types/befly.js';
|
|
14
14
|
|
|
15
|
-
export async function loadPlugins(
|
|
16
|
-
//
|
|
17
|
-
pluginLists: Plugin[];
|
|
18
|
-
appContext: BeflyContext;
|
|
19
|
-
pluginsConfig?: Record<string, any>;
|
|
20
|
-
}): Promise<void> {
|
|
15
|
+
export async function loadPlugins(config: Record<string, any> | undefined, plugins: Plugin[], context: BeflyContext): Promise<void> {
|
|
21
16
|
try {
|
|
22
17
|
const allPlugins: Plugin[] = [];
|
|
23
18
|
|
|
24
19
|
// 1. 扫描核心插件
|
|
25
|
-
const corePlugins = await scanModules<Plugin>(corePluginDir, 'core', '插件',
|
|
20
|
+
const corePlugins = await scanModules<Plugin>(corePluginDir, 'core', '插件', config);
|
|
26
21
|
|
|
27
22
|
// 2. 扫描组件插件
|
|
28
23
|
const addonPlugins: Plugin[] = [];
|
|
29
24
|
const addons = scanAddons();
|
|
30
25
|
for (const addon of addons) {
|
|
31
26
|
const dir = getAddonDir(addon, 'plugins');
|
|
32
|
-
const plugins = await scanModules<Plugin>(dir, 'addon', '插件',
|
|
27
|
+
const plugins = await scanModules<Plugin>(dir, 'addon', '插件', config, addon);
|
|
33
28
|
addonPlugins.push(...plugins);
|
|
34
29
|
}
|
|
35
30
|
|
|
36
31
|
// 3. 扫描项目插件
|
|
37
|
-
const appPlugins = await scanModules<Plugin>(projectPluginDir, 'app', '插件',
|
|
32
|
+
const appPlugins = await scanModules<Plugin>(projectPluginDir, 'app', '插件', config);
|
|
38
33
|
|
|
39
34
|
// 4. 合并所有插件
|
|
40
35
|
allPlugins.push(...corePlugins);
|
|
@@ -42,7 +37,7 @@ export async function loadPlugins(befly: {
|
|
|
42
37
|
allPlugins.push(...appPlugins);
|
|
43
38
|
|
|
44
39
|
// 5. 过滤禁用的插件
|
|
45
|
-
const disablePlugins = (
|
|
40
|
+
const disablePlugins = (config as any)?.disablePlugins || [];
|
|
46
41
|
const enabledPlugins = allPlugins.filter((plugin) => !disablePlugins.includes(plugin.name));
|
|
47
42
|
|
|
48
43
|
if (disablePlugins.length > 0) {
|
|
@@ -58,13 +53,12 @@ export async function loadPlugins(befly: {
|
|
|
58
53
|
|
|
59
54
|
for (const plugin of sortedPlugins) {
|
|
60
55
|
try {
|
|
61
|
-
|
|
56
|
+
plugins.push(plugin);
|
|
62
57
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
}
|
|
58
|
+
const pluginInstance = typeof plugin.handler === 'function' ? await plugin.handler(context, config) : {};
|
|
59
|
+
|
|
60
|
+
// 直接挂载到 befly 下
|
|
61
|
+
(context as any)[plugin.name!] = pluginInstance;
|
|
68
62
|
} catch (error: any) {
|
|
69
63
|
Logger.error(`插件 ${plugin.name} 初始化失败`, error);
|
|
70
64
|
process.exit(1);
|
package/main.ts
CHANGED
|
@@ -10,13 +10,12 @@ import { calcPerfTime } from 'befly-util';
|
|
|
10
10
|
import { Logger } from './lib/logger.js';
|
|
11
11
|
import { Cipher } from './lib/cipher.js';
|
|
12
12
|
import { Jwt } from './lib/jwt.js';
|
|
13
|
-
import {
|
|
13
|
+
import { Connect } from './lib/connect.js';
|
|
14
14
|
import { DbHelper } from './lib/dbHelper.js';
|
|
15
15
|
import { RedisHelper } from './lib/redisHelper.js';
|
|
16
16
|
import { loadPlugins } from './loader/loadPlugins.js';
|
|
17
17
|
import { loadHooks } from './loader/loadHooks.js';
|
|
18
18
|
import { loadApis } from './loader/loadApis.js';
|
|
19
|
-
import { rootHandler } from './router/root.js';
|
|
20
19
|
import { apiHandler } from './router/api.js';
|
|
21
20
|
import { staticHandler } from './router/static.js';
|
|
22
21
|
import { checkApp } from './checks/checkApp.js';
|
|
@@ -25,6 +24,7 @@ import { checkApi } from './checks/checkApi.js';
|
|
|
25
24
|
import { syncAllCommand } from './sync/syncAll.js';
|
|
26
25
|
import { coreDir } from './paths.js';
|
|
27
26
|
import { defaultOptions } from './config.js';
|
|
27
|
+
import { isPrimaryProcess, getProcessRole } from './util.js';
|
|
28
28
|
|
|
29
29
|
// ========== 类型导入 ==========
|
|
30
30
|
import type { Server } from 'bun';
|
|
@@ -38,24 +38,22 @@ import type { ApiRoute } from './types/api.js';
|
|
|
38
38
|
*/
|
|
39
39
|
export class Befly {
|
|
40
40
|
/** API 路由映射表 */
|
|
41
|
-
private
|
|
41
|
+
private apis: Map<string, ApiRoute> = new Map();
|
|
42
42
|
|
|
43
43
|
/** 插件列表 */
|
|
44
|
-
private
|
|
44
|
+
private plugins: Plugin[] = [];
|
|
45
45
|
|
|
46
46
|
/** 钩子列表 */
|
|
47
|
-
private
|
|
47
|
+
private hooks: Hook[] = [];
|
|
48
48
|
|
|
49
49
|
/** 应用上下文 */
|
|
50
|
-
public
|
|
50
|
+
public context: BeflyContext = {};
|
|
51
51
|
|
|
52
52
|
/** 最终配置(合并默认值后) */
|
|
53
|
-
public config: BeflyOptions;
|
|
53
|
+
public config: BeflyOptions = { ...defaultOptions };
|
|
54
54
|
|
|
55
55
|
constructor(options: BeflyOptions = {}) {
|
|
56
|
-
|
|
57
|
-
// 合并配置:用户配置 > 默认配置(最多 2 级)
|
|
58
|
-
this.config = { ...defaultOptions };
|
|
56
|
+
// 合并用户配置:用户配置 > 默认配置(最多 2 级)
|
|
59
57
|
for (const key in options) {
|
|
60
58
|
const value = options[key as keyof BeflyOptions];
|
|
61
59
|
if (value !== undefined && value !== null) {
|
|
@@ -82,32 +80,27 @@ export class Befly {
|
|
|
82
80
|
await checkApi();
|
|
83
81
|
|
|
84
82
|
// 4. 加载插件
|
|
85
|
-
await loadPlugins(
|
|
86
|
-
pluginLists: this.pluginLists,
|
|
87
|
-
appContext: this.appContext,
|
|
88
|
-
pluginsConfig: this.config as any
|
|
89
|
-
});
|
|
83
|
+
await loadPlugins(this.config as any, this.plugins, this.context);
|
|
90
84
|
|
|
91
85
|
// 5. 加载钩子
|
|
92
|
-
await loadHooks(
|
|
93
|
-
hookLists: this.hookLists,
|
|
94
|
-
pluginsConfig: this.config as any
|
|
95
|
-
});
|
|
86
|
+
await loadHooks(this.config as any, this.hooks);
|
|
96
87
|
|
|
97
88
|
// 6. 加载所有 API
|
|
98
|
-
await loadApis(this.
|
|
89
|
+
await loadApis(this.apis);
|
|
99
90
|
|
|
100
|
-
// 7. 自动同步 (
|
|
101
|
-
|
|
91
|
+
// 7. 自动同步 (仅主进程执行,避免集群模式下重复执行)
|
|
92
|
+
if (isPrimaryProcess()) {
|
|
93
|
+
await syncAllCommand(this.config);
|
|
94
|
+
}
|
|
102
95
|
|
|
103
96
|
// 8. 启动 HTTP 服务器
|
|
104
97
|
const server = Bun.serve({
|
|
105
98
|
port: this.config.appPort,
|
|
106
99
|
hostname: this.config.appHost,
|
|
107
100
|
routes: {
|
|
108
|
-
'/':
|
|
109
|
-
'/api/*': apiHandler(this.
|
|
110
|
-
'/*': staticHandler(this.config
|
|
101
|
+
'/': () => Response.json({ code: 0, msg: `${this.config.appName} 接口服务已启动` }),
|
|
102
|
+
'/api/*': apiHandler(this.apis, this.hooks, this.context),
|
|
103
|
+
'/*': staticHandler(this.config)
|
|
111
104
|
},
|
|
112
105
|
error: (error: Error) => {
|
|
113
106
|
Logger.error('服务启动时发生错误', error);
|
|
@@ -116,7 +109,11 @@ export class Befly {
|
|
|
116
109
|
});
|
|
117
110
|
|
|
118
111
|
const finalStartupTime = calcPerfTime(serverStartTime);
|
|
119
|
-
|
|
112
|
+
const processRole = getProcessRole();
|
|
113
|
+
const roleLabel = processRole.role === 'primary' ? '主进程' : `工作进程 #${processRole.instanceId}`;
|
|
114
|
+
const envLabel = processRole.env === 'standalone' ? '' : ` [${processRole.env}]`;
|
|
115
|
+
|
|
116
|
+
Logger.info(`${this.config.appName} 启动成功! (${roleLabel}${envLabel})`);
|
|
120
117
|
Logger.info(`服务器启动耗时: ${finalStartupTime}`);
|
|
121
118
|
Logger.info(`服务器监听地址: http://${this.config.appHost}:${this.config.appPort}`);
|
|
122
119
|
|
|
@@ -128,7 +125,7 @@ export class Befly {
|
|
|
128
125
|
|
|
129
126
|
// 关闭数据库连接
|
|
130
127
|
try {
|
|
131
|
-
await
|
|
128
|
+
await Connect.disconnect();
|
|
132
129
|
Logger.info('数据库连接已关闭');
|
|
133
130
|
} catch (error: any) {
|
|
134
131
|
Logger.error('关闭数据库连接时出错:', error);
|
|
@@ -143,7 +140,7 @@ export class Befly {
|
|
|
143
140
|
|
|
144
141
|
return server;
|
|
145
142
|
} catch (error: any) {
|
|
146
|
-
Logger.error('
|
|
143
|
+
Logger.error('项目启动失败', error);
|
|
147
144
|
process.exit(1);
|
|
148
145
|
}
|
|
149
146
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "befly",
|
|
3
|
-
"version": "3.8.
|
|
3
|
+
"version": "3.8.27",
|
|
4
4
|
"description": "Befly - 为 Bun 专属打造的 TypeScript API 接口框架核心引擎",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"private": false,
|
|
@@ -66,12 +66,12 @@
|
|
|
66
66
|
"bun": ">=1.3.0"
|
|
67
67
|
},
|
|
68
68
|
"dependencies": {
|
|
69
|
-
"befly-util": "^1.0.
|
|
69
|
+
"befly-util": "^1.0.6",
|
|
70
70
|
"chalk": "^5.6.2",
|
|
71
71
|
"es-toolkit": "^1.41.0",
|
|
72
72
|
"pathe": "^2.0.3"
|
|
73
73
|
},
|
|
74
|
-
"gitHead": "
|
|
74
|
+
"gitHead": "54c0f11f65a56f7df036fdf386b72329496777b4",
|
|
75
75
|
"devDependencies": {
|
|
76
76
|
"typescript": "^5.9.3"
|
|
77
77
|
}
|
package/plugins/cache.ts
CHANGED
|
@@ -11,8 +11,8 @@ import type { BeflyContext } from '../types/befly.js';
|
|
|
11
11
|
* 缓存插件
|
|
12
12
|
*/
|
|
13
13
|
const cachePlugin: Plugin = {
|
|
14
|
-
after: [
|
|
15
|
-
async
|
|
14
|
+
after: [],
|
|
15
|
+
async handler(befly: BeflyContext): Promise<CacheHelper> {
|
|
16
16
|
return new CacheHelper(befly);
|
|
17
17
|
}
|
|
18
18
|
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cipher 插件
|
|
3
|
+
* 提供加密解密功能
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Cipher } from '../lib/cipher.js';
|
|
7
|
+
|
|
8
|
+
import type { Plugin } from '../types/plugin.js';
|
|
9
|
+
|
|
10
|
+
export default {
|
|
11
|
+
name: 'cipher',
|
|
12
|
+
handler: (context, config) => {
|
|
13
|
+
return Cipher;
|
|
14
|
+
}
|
|
15
|
+
} as Plugin;
|
package/plugins/db.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { Logger } from '../lib/logger.js';
|
|
7
|
-
import {
|
|
7
|
+
import { Connect } from '../lib/connect.js';
|
|
8
8
|
import { DbHelper } from '../lib/dbHelper.js';
|
|
9
9
|
import type { Plugin } from '../types/plugin.js';
|
|
10
10
|
import type { BeflyContext } from '../types/befly.js';
|
|
@@ -13,28 +13,18 @@ import type { BeflyContext } from '../types/befly.js';
|
|
|
13
13
|
* 数据库插件
|
|
14
14
|
*/
|
|
15
15
|
const dbPlugin: Plugin = {
|
|
16
|
-
after: ['
|
|
17
|
-
async
|
|
16
|
+
after: ['logger'],
|
|
17
|
+
async handler(this: Plugin, befly: BeflyContext): Promise<DbHelper> {
|
|
18
18
|
let sql: any = null;
|
|
19
19
|
const config = this.config || {};
|
|
20
20
|
|
|
21
21
|
try {
|
|
22
|
-
|
|
23
|
-
if (config.enable !== 0) {
|
|
24
|
-
// 创建 Bun SQL 客户端(内置连接池),并确保连接验证成功后再继续
|
|
25
|
-
// 从配置读取连接超时配置
|
|
26
|
-
// const connectionTimeout = config.connectionTimeout ? parseInt(config.connectionTimeout) : 30000;
|
|
22
|
+
sql = await Connect.connectSql(config);
|
|
27
23
|
|
|
28
|
-
|
|
24
|
+
// 创建数据库管理器实例,直接传入 sql 对象
|
|
25
|
+
const dbManager = new DbHelper(befly, sql);
|
|
29
26
|
|
|
30
|
-
|
|
31
|
-
const dbManager = new DbHelper(befly, sql);
|
|
32
|
-
|
|
33
|
-
return dbManager;
|
|
34
|
-
} else {
|
|
35
|
-
Logger.warn('数据库未启用,跳过初始化');
|
|
36
|
-
return {};
|
|
37
|
-
}
|
|
27
|
+
return dbManager;
|
|
38
28
|
} catch (error: any) {
|
|
39
29
|
Logger.error('数据库初始化失败', error);
|
|
40
30
|
|
package/plugins/jwt.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JWT 插件
|
|
3
|
+
* 提供 JSON Web Token 签名和验证功能
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Jwt } from '../lib/jwt.js';
|
|
7
|
+
|
|
8
|
+
import type { Plugin } from '../types/plugin.js';
|
|
9
|
+
|
|
10
|
+
export default {
|
|
11
|
+
name: 'jwt',
|
|
12
|
+
handler: (context, config) => {
|
|
13
|
+
return Jwt;
|
|
14
|
+
}
|
|
15
|
+
} as Plugin;
|
package/plugins/logger.ts
CHANGED
|
@@ -12,7 +12,7 @@ import type { BeflyContext } from '../types/befly.js';
|
|
|
12
12
|
*/
|
|
13
13
|
const loggerPlugin: Plugin = {
|
|
14
14
|
after: [],
|
|
15
|
-
async
|
|
15
|
+
async handler(this: Plugin, befly: BeflyContext): Promise<typeof Logger> {
|
|
16
16
|
try {
|
|
17
17
|
// 配置 Logger
|
|
18
18
|
if (this.config) {
|
package/plugins/redis.ts
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { Logger } from '../lib/logger.js';
|
|
7
|
+
import { Connect } from '../lib/connect.js';
|
|
7
8
|
import { RedisHelper } from '../lib/redisHelper.js';
|
|
8
|
-
import { Database } from '../lib/database.js';
|
|
9
9
|
import type { Plugin } from '../types/plugin.js';
|
|
10
10
|
import type { BeflyContext } from '../types/befly.js';
|
|
11
11
|
|
|
@@ -14,14 +14,14 @@ import type { BeflyContext } from '../types/befly.js';
|
|
|
14
14
|
*/
|
|
15
15
|
const redisPlugin: Plugin = {
|
|
16
16
|
after: ['logger'],
|
|
17
|
-
async
|
|
17
|
+
async handler(this: Plugin, befly: BeflyContext): Promise<RedisHelper | Record<string, never>> {
|
|
18
18
|
const config = this.config || {};
|
|
19
19
|
try {
|
|
20
20
|
// 默认启用,除非显式禁用 (这里假设只要配置了 redis 插件就启用,或者检查 enable 字段)
|
|
21
21
|
// 为了兼容性,如果 config 为空,可能意味着使用默认值连接本地 redis
|
|
22
22
|
|
|
23
|
-
// 初始化 Redis 客户端(统一使用
|
|
24
|
-
await
|
|
23
|
+
// 初始化 Redis 客户端(统一使用 connect.ts 的连接管理)
|
|
24
|
+
await Connect.connectRedis(config);
|
|
25
25
|
|
|
26
26
|
// 返回 RedisHelper 实例
|
|
27
27
|
return new RedisHelper(config.prefix);
|
package/plugins/tool.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 工具插件
|
|
3
|
+
* 提供常用的工具函数
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// 类型导入
|
|
7
|
+
import type { Plugin } from '../types/plugin.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* 成功响应
|
|
11
|
+
* @param msg - 消息
|
|
12
|
+
* @param data - 数据(可选)
|
|
13
|
+
* @param other - 其他字段(可选)
|
|
14
|
+
* @returns 成功响应对象
|
|
15
|
+
*/
|
|
16
|
+
export function Yes(msg: string, data: any = {}, other: Record<string, any> = {}) {
|
|
17
|
+
return {
|
|
18
|
+
code: 0,
|
|
19
|
+
msg: msg,
|
|
20
|
+
data: data,
|
|
21
|
+
...other
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* 失败响应
|
|
27
|
+
* @param msg - 消息
|
|
28
|
+
* @param data - 数据(可选)
|
|
29
|
+
* @param other - 其他字段(可选)
|
|
30
|
+
* @returns 失败响应对象
|
|
31
|
+
*/
|
|
32
|
+
export function No(msg: string, data: any = null, other: Record<string, any> = {}) {
|
|
33
|
+
return {
|
|
34
|
+
code: 1,
|
|
35
|
+
msg: msg,
|
|
36
|
+
data: data,
|
|
37
|
+
...other
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const plugin: Plugin = {
|
|
42
|
+
handler: () => {
|
|
43
|
+
return {
|
|
44
|
+
Yes: Yes,
|
|
45
|
+
No: No
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export default plugin;
|
package/router/api.ts
CHANGED
|
@@ -3,8 +3,12 @@
|
|
|
3
3
|
* 处理 /api/* 路径的请求
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
// 外部依赖
|
|
7
|
+
import { genShortId } from 'befly-util';
|
|
8
|
+
|
|
6
9
|
// 相对导入
|
|
7
|
-
import {
|
|
10
|
+
import { FinalResponse } from '../util.js';
|
|
11
|
+
import { Logger } from '../lib/logger.js';
|
|
8
12
|
|
|
9
13
|
// 类型导入
|
|
10
14
|
import type { RequestContext } from '../types/context.js';
|
|
@@ -14,72 +18,82 @@ import type { BeflyContext } from '../types/befly.js';
|
|
|
14
18
|
|
|
15
19
|
/**
|
|
16
20
|
* API处理器工厂函数
|
|
17
|
-
* @param
|
|
18
|
-
* @param
|
|
19
|
-
* @param
|
|
21
|
+
* @param apis - API路由映射表
|
|
22
|
+
* @param hooks - 钩子列表
|
|
23
|
+
* @param context - 应用上下文
|
|
20
24
|
*/
|
|
21
|
-
export function apiHandler(
|
|
22
|
-
// 提取所有钩子的处理函数
|
|
23
|
-
const middleware = hookLists.map((h) => h.handler);
|
|
24
|
-
|
|
25
|
-
// 组合钩子链
|
|
26
|
-
const fn = compose(middleware);
|
|
27
|
-
|
|
25
|
+
export function apiHandler(apis: Map<string, ApiRoute>, hooks: Hook[], context: BeflyContext) {
|
|
28
26
|
return async (req: Request): Promise<Response> => {
|
|
29
|
-
// 1.
|
|
27
|
+
// 1. 生成请求 ID
|
|
28
|
+
const requestId = genShortId();
|
|
29
|
+
|
|
30
|
+
// 2. 创建请求上下文
|
|
30
31
|
const url = new URL(req.url);
|
|
31
32
|
const apiPath = `${req.method}${url.pathname}`;
|
|
33
|
+
const clientIp = req.headers.get('x-forwarded-for')?.split(',')[0]?.trim() || req.headers.get('x-real-ip') || 'unknown';
|
|
32
34
|
|
|
33
35
|
const ctx: RequestContext = {
|
|
34
36
|
body: {},
|
|
35
37
|
user: {},
|
|
36
38
|
req: req,
|
|
37
39
|
now: Date.now(),
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
route: apiPath
|
|
40
|
+
ip: clientIp,
|
|
41
|
+
headers: req.headers,
|
|
42
|
+
route: apiPath,
|
|
43
|
+
requestId: requestId,
|
|
44
|
+
corsHeaders: {
|
|
45
|
+
'X-Request-ID': requestId
|
|
46
|
+
},
|
|
47
|
+
api: apis.get(apiPath),
|
|
48
|
+
response: undefined,
|
|
49
|
+
result: undefined
|
|
41
50
|
};
|
|
42
51
|
|
|
43
|
-
|
|
44
|
-
|
|
52
|
+
try {
|
|
53
|
+
// 4. 串联执行所有钩子
|
|
54
|
+
for (const hook of hooks) {
|
|
55
|
+
await hook.handler(context, ctx);
|
|
45
56
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
57
|
+
// 如果钩子已经设置了 response,停止执行
|
|
58
|
+
if (ctx.response) {
|
|
59
|
+
return ctx.response;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
51
62
|
|
|
52
|
-
|
|
53
|
-
// 错误处理已由 errorHandler 插件接管
|
|
54
|
-
await fn(appContext, ctx, async () => {
|
|
55
|
-
// 核心执行器:执行 API handler
|
|
56
|
-
// 如果没有找到 API 且没有被前面的插件拦截(如 CORS),则返回 404
|
|
63
|
+
// 5. 执行 API handler
|
|
57
64
|
if (!ctx.api) {
|
|
58
|
-
// 只有非 OPTIONS 请求才报 404(OPTIONS 请求通常由 cors 插件处理并返回)
|
|
59
65
|
if (req.method !== 'OPTIONS') {
|
|
60
|
-
ctx.response =
|
|
66
|
+
ctx.response = Response.json(
|
|
67
|
+
{
|
|
68
|
+
code: 1,
|
|
69
|
+
msg: '接口不存在'
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
headers: ctx.corsHeaders
|
|
73
|
+
}
|
|
74
|
+
);
|
|
61
75
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
if (ctx.api.handler) {
|
|
66
|
-
const result = await ctx.api.handler(appContext, ctx, req);
|
|
76
|
+
} else if (ctx.api.handler) {
|
|
77
|
+
const result = await ctx.api.handler(context, ctx);
|
|
67
78
|
|
|
68
79
|
if (result instanceof Response) {
|
|
69
80
|
ctx.response = result;
|
|
70
81
|
} else {
|
|
71
|
-
// 将结果存入 ctx.result,由 responseFormatter 插件统一处理
|
|
72
82
|
ctx.result = result;
|
|
73
83
|
}
|
|
74
84
|
}
|
|
75
|
-
});
|
|
76
85
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
86
|
+
// 6. 返回响应(自动处理 response/result/日志)
|
|
87
|
+
return FinalResponse(ctx);
|
|
88
|
+
} catch (err: any) {
|
|
89
|
+
// 全局错误处理
|
|
90
|
+
const errorPath = ctx.api ? apiPath : req.url;
|
|
91
|
+
Logger.error(`请求错误: ${errorPath}`, err);
|
|
92
|
+
ctx.result = {
|
|
93
|
+
code: 1,
|
|
94
|
+
msg: '内部服务错误'
|
|
95
|
+
};
|
|
96
|
+
return FinalResponse(ctx);
|
|
80
97
|
}
|
|
81
|
-
|
|
82
|
-
// 兜底响应(理论上不应执行到这里,responseFormatter 会处理)
|
|
83
|
-
return JsonResponse(ctx, 'No response generated');
|
|
84
98
|
};
|
|
85
99
|
}
|
package/router/static.ts
CHANGED
|
@@ -12,16 +12,16 @@ import { Logger } from '../lib/logger.js';
|
|
|
12
12
|
import { setCorsOptions } from '../util.js';
|
|
13
13
|
|
|
14
14
|
// 类型导入
|
|
15
|
-
import type {
|
|
15
|
+
import type { BeflyOptions } from '../types/befly.js';
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* 静态文件处理器工厂
|
|
19
|
-
* @param
|
|
19
|
+
* @param config - Befly 配置
|
|
20
20
|
*/
|
|
21
|
-
export function staticHandler(
|
|
21
|
+
export function staticHandler(config: BeflyOptions) {
|
|
22
22
|
return async (req: Request): Promise<Response> => {
|
|
23
23
|
// 设置 CORS 响应头
|
|
24
|
-
const corsHeaders = setCorsOptions(req,
|
|
24
|
+
const corsHeaders = setCorsOptions(req, config.cors);
|
|
25
25
|
|
|
26
26
|
const url = new URL(req.url);
|
|
27
27
|
const filePath = join(projectDir, 'public', url.pathname);
|
|
@@ -45,9 +45,11 @@ export function staticHandler(corsConfig: CorsConfig = {}) {
|
|
|
45
45
|
});
|
|
46
46
|
} else {
|
|
47
47
|
return Response.json(
|
|
48
|
-
{ code: 1, msg: '文件未找到' },
|
|
49
48
|
{
|
|
50
|
-
|
|
49
|
+
code: 1,
|
|
50
|
+
msg: '文件未找到'
|
|
51
|
+
},
|
|
52
|
+
{
|
|
51
53
|
headers: corsHeaders
|
|
52
54
|
}
|
|
53
55
|
);
|
|
@@ -56,14 +58,12 @@ export function staticHandler(corsConfig: CorsConfig = {}) {
|
|
|
56
58
|
// 记录详细的错误日志
|
|
57
59
|
Logger.error('静态文件处理失败', error);
|
|
58
60
|
|
|
59
|
-
// 根据错误类型返回不同的错误信息
|
|
60
|
-
let errorMessage = '文件读取失败';
|
|
61
|
-
let errorDetail = {};
|
|
62
|
-
|
|
63
61
|
return Response.json(
|
|
64
|
-
{ code: 1, msg: errorMessage, data: errorDetail },
|
|
65
62
|
{
|
|
66
|
-
|
|
63
|
+
code: 1,
|
|
64
|
+
msg: '文件读取失败'
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
67
|
headers: corsHeaders
|
|
68
68
|
}
|
|
69
69
|
);
|
package/sync/syncAll.ts
CHANGED
|
@@ -16,41 +16,23 @@ export async function syncAllCommand(config: BeflyOptions, options: SyncOptions
|
|
|
16
16
|
const startTime = Date.now();
|
|
17
17
|
|
|
18
18
|
// 0. 检查项目结构
|
|
19
|
-
|
|
20
|
-
const checkResult = await checkApp();
|
|
21
|
-
if (!checkResult) {
|
|
22
|
-
Logger.error('项目结构检查失败,程序退出');
|
|
23
|
-
throw new Error('项目结构检查失败');
|
|
24
|
-
}
|
|
25
|
-
Logger.debug(`✓ 项目结构检查完成\n`);
|
|
26
|
-
|
|
27
|
-
Logger.debug('开始执行同步任务...\n');
|
|
19
|
+
await checkApp();
|
|
28
20
|
|
|
29
21
|
// 1. 同步数据库表结构
|
|
30
|
-
Logger.debug('📦 正在同步数据库...');
|
|
31
22
|
await syncDbCommand(config, { dryRun: false, force: options.force || false });
|
|
32
|
-
Logger.debug(`✓ 数据库同步完成\n`);
|
|
33
23
|
|
|
34
24
|
// 2. 同步接口(并缓存)
|
|
35
|
-
Logger.debug('🔌 正在同步接口...');
|
|
36
25
|
await syncApiCommand(config);
|
|
37
|
-
Logger.debug(`✓ 接口同步完成\n`);
|
|
38
26
|
|
|
39
27
|
// 3. 同步菜单(并缓存)
|
|
40
|
-
Logger.debug('📋 正在同步菜单...');
|
|
41
28
|
await syncMenuCommand(config);
|
|
42
|
-
Logger.debug(`✓ 菜单同步完成\n`);
|
|
43
29
|
|
|
44
30
|
// 4. 同步开发管理员(并缓存角色权限)
|
|
45
|
-
Logger.debug('👤 正在同步开发账号...');
|
|
46
31
|
await syncDevCommand(config);
|
|
47
|
-
Logger.debug(`✓ 开发账号同步完成\n`);
|
|
48
32
|
|
|
49
33
|
// 输出总结
|
|
50
34
|
const totalTimeSeconds = ((Date.now() - startTime) / 1000).toFixed(2);
|
|
51
|
-
Logger.
|
|
52
|
-
Logger.debug(`✅ 同步完成!总耗时: ${totalTimeSeconds} 秒`);
|
|
53
|
-
Logger.debug('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
|
|
35
|
+
Logger.info(`同步完成 (耗时 ${totalTimeSeconds}s)`);
|
|
54
36
|
} catch (error: any) {
|
|
55
37
|
Logger.error('同步过程中发生错误', error);
|
|
56
38
|
throw error;
|