befly 3.8.19 → 3.8.20
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 +7 -6
- package/bunfig.toml +1 -1
- package/lib/database.ts +28 -25
- package/lib/dbHelper.ts +3 -3
- package/lib/jwt.ts +90 -99
- package/lib/logger.ts +44 -23
- package/lib/redisHelper.ts +19 -22
- package/loader/loadApis.ts +69 -114
- package/loader/loadHooks.ts +65 -0
- package/loader/loadPlugins.ts +50 -219
- package/main.ts +106 -133
- package/package.json +15 -7
- package/paths.ts +20 -0
- package/plugins/cache.ts +1 -3
- package/plugins/db.ts +8 -11
- package/plugins/logger.ts +5 -3
- package/plugins/redis.ts +10 -14
- package/router/api.ts +60 -106
- package/router/root.ts +15 -12
- package/router/static.ts +54 -58
- package/sync/syncAll.ts +58 -0
- package/sync/syncApi.ts +264 -0
- package/sync/syncDb/apply.ts +194 -0
- package/sync/syncDb/constants.ts +76 -0
- package/sync/syncDb/ddl.ts +194 -0
- package/sync/syncDb/helpers.ts +200 -0
- package/sync/syncDb/index.ts +164 -0
- package/sync/syncDb/schema.ts +201 -0
- package/sync/syncDb/sqlite.ts +50 -0
- package/sync/syncDb/table.ts +321 -0
- package/sync/syncDb/tableCreate.ts +146 -0
- package/sync/syncDb/version.ts +72 -0
- package/sync/syncDb.ts +19 -0
- package/sync/syncDev.ts +206 -0
- package/sync/syncMenu.ts +331 -0
- package/tsconfig.json +2 -4
- package/types/api.d.ts +6 -0
- package/types/befly.d.ts +152 -28
- package/types/context.d.ts +29 -3
- package/types/hook.d.ts +35 -0
- package/types/index.ts +14 -1
- package/types/plugin.d.ts +6 -7
- package/types/sync.d.ts +403 -0
- package/check.ts +0 -378
- package/env.ts +0 -106
- package/lib/middleware.ts +0 -275
- package/types/env.ts +0 -65
- package/types/util.d.ts +0 -45
- package/util.ts +0 -257
package/loader/loadPlugins.ts
CHANGED
|
@@ -1,249 +1,80 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 插件加载器
|
|
3
|
-
*
|
|
3
|
+
* 负责扫描和初始化所有插件(核心、组件、项目)
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
import { camelCase } from 'es-toolkit/string';
|
|
6
|
+
import { scanAddons, getAddonDir } from 'befly-util';
|
|
7
|
+
|
|
9
8
|
import { Logger } from '../lib/logger.js';
|
|
10
|
-
import { calcPerfTime } from '../util.js';
|
|
11
9
|
import { corePluginDir, projectPluginDir } from '../paths.js';
|
|
12
|
-
import {
|
|
10
|
+
import { sortModules, scanModules } from '../util.js';
|
|
11
|
+
|
|
13
12
|
import type { Plugin } from '../types/plugin.js';
|
|
14
13
|
import type { BeflyContext } from '../types/befly.js';
|
|
15
14
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
visiting.add(name);
|
|
37
|
-
(plugin.after || []).forEach(visit);
|
|
38
|
-
visiting.delete(name);
|
|
39
|
-
visited.add(name);
|
|
40
|
-
result.push(plugin);
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
plugins.forEach((p) => visit(p.pluginName || p.name));
|
|
44
|
-
return isPass ? result : false;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* 扫描核心插件
|
|
49
|
-
*/
|
|
50
|
-
async function scanCorePlugins(loadedPluginNames: Set<string>): Promise<Plugin[]> {
|
|
51
|
-
const plugins: Plugin[] = [];
|
|
52
|
-
const glob = new Bun.Glob('*.{ts,js}');
|
|
53
|
-
|
|
54
|
-
for await (const file of glob.scan({
|
|
55
|
-
cwd: corePluginDir,
|
|
56
|
-
onlyFiles: true,
|
|
57
|
-
absolute: true
|
|
58
|
-
})) {
|
|
59
|
-
if (file.endsWith('.d.ts')) continue;
|
|
60
|
-
const fileName = basename(file).replace(/\.(ts|js)$/, '');
|
|
61
|
-
if (fileName.startsWith('_')) continue;
|
|
62
|
-
|
|
63
|
-
try {
|
|
64
|
-
const pluginImport = await import(file);
|
|
65
|
-
const plugin = pluginImport.default;
|
|
66
|
-
plugin.pluginName = fileName;
|
|
67
|
-
plugins.push(plugin);
|
|
68
|
-
loadedPluginNames.add(fileName);
|
|
69
|
-
} catch (err: any) {
|
|
70
|
-
Logger.error(`核心插件 ${fileName} 导入失败`, err);
|
|
71
|
-
process.exit(1);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
return plugins;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* 扫描组件插件
|
|
80
|
-
*/
|
|
81
|
-
async function scanAddonPlugins(loadedPluginNames: Set<string>): Promise<Plugin[]> {
|
|
82
|
-
const plugins: Plugin[] = [];
|
|
83
|
-
const glob = new Bun.Glob('*.{ts,js}');
|
|
84
|
-
const addons = scanAddons();
|
|
85
|
-
|
|
86
|
-
for (const addon of addons) {
|
|
87
|
-
if (!addonDirExists(addon, 'plugins')) continue;
|
|
88
|
-
|
|
89
|
-
const addonPluginsDir = getAddonDir(addon, 'plugins');
|
|
90
|
-
for await (const file of glob.scan({
|
|
91
|
-
cwd: addonPluginsDir,
|
|
92
|
-
onlyFiles: true,
|
|
93
|
-
absolute: true
|
|
94
|
-
})) {
|
|
95
|
-
if (file.endsWith('.d.ts')) continue;
|
|
96
|
-
const fileName = basename(file).replace(/\.(ts|js)$/, '');
|
|
97
|
-
if (fileName.startsWith('_')) continue;
|
|
98
|
-
|
|
99
|
-
const addonCamelCase = camelCase(addon);
|
|
100
|
-
const fileNameCamelCase = camelCase(fileName);
|
|
101
|
-
const pluginFullName = `addon${addonCamelCase.charAt(0).toUpperCase() + addonCamelCase.slice(1)}_${fileNameCamelCase}`;
|
|
102
|
-
|
|
103
|
-
if (loadedPluginNames.has(pluginFullName)) {
|
|
104
|
-
continue;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
try {
|
|
108
|
-
const normalizedFilePath = file.replace(/\\/g, '/');
|
|
109
|
-
const pluginImport = await import(normalizedFilePath);
|
|
110
|
-
const plugin = pluginImport.default;
|
|
111
|
-
plugin.pluginName = pluginFullName;
|
|
112
|
-
plugins.push(plugin);
|
|
113
|
-
loadedPluginNames.add(pluginFullName);
|
|
114
|
-
} catch (err: any) {
|
|
115
|
-
Logger.error(`组件${addon} ${fileName} 导入失败`, err);
|
|
116
|
-
process.exit(1);
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
return plugins;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* 扫描用户插件
|
|
126
|
-
*/
|
|
127
|
-
async function scanUserPlugins(loadedPluginNames: Set<string>): Promise<Plugin[]> {
|
|
128
|
-
const plugins: Plugin[] = [];
|
|
129
|
-
|
|
130
|
-
if (!existsSync(projectPluginDir)) {
|
|
131
|
-
return plugins;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
const glob = new Bun.Glob('*.{ts,js}');
|
|
135
|
-
for await (const file of glob.scan({
|
|
136
|
-
cwd: projectPluginDir,
|
|
137
|
-
onlyFiles: true,
|
|
138
|
-
absolute: true
|
|
139
|
-
})) {
|
|
140
|
-
if (file.endsWith('.d.ts')) continue;
|
|
141
|
-
const fileName = basename(file).replace(/\.(ts|js)$/, '');
|
|
142
|
-
if (fileName.startsWith('_')) continue;
|
|
143
|
-
|
|
144
|
-
const fileNameCamelCase = camelCase(fileName);
|
|
145
|
-
const pluginFullName = `app${fileNameCamelCase.charAt(0).toUpperCase() + fileNameCamelCase.slice(1)}`;
|
|
146
|
-
|
|
147
|
-
if (loadedPluginNames.has(pluginFullName)) {
|
|
148
|
-
continue;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
try {
|
|
152
|
-
const normalizedFilePath = file.replace(/\\/g, '/');
|
|
153
|
-
const pluginImport = await import(normalizedFilePath);
|
|
154
|
-
const plugin = pluginImport.default;
|
|
155
|
-
plugin.pluginName = pluginFullName;
|
|
156
|
-
plugins.push(plugin);
|
|
157
|
-
loadedPluginNames.add(pluginFullName);
|
|
158
|
-
} catch (err: any) {
|
|
159
|
-
Logger.error(`用户插件 ${fileName} 导入失败`, err);
|
|
160
|
-
process.exit(1);
|
|
15
|
+
export async function loadPlugins(befly: {
|
|
16
|
+
//
|
|
17
|
+
pluginLists: Plugin[];
|
|
18
|
+
appContext: BeflyContext;
|
|
19
|
+
pluginsConfig?: Record<string, any>;
|
|
20
|
+
}): Promise<void> {
|
|
21
|
+
try {
|
|
22
|
+
const allPlugins: Plugin[] = [];
|
|
23
|
+
|
|
24
|
+
// 1. 扫描核心插件
|
|
25
|
+
const corePlugins = await scanModules<Plugin>(corePluginDir, 'core', '插件', befly.pluginsConfig);
|
|
26
|
+
|
|
27
|
+
// 2. 扫描组件插件
|
|
28
|
+
const addonPlugins: Plugin[] = [];
|
|
29
|
+
const addons = scanAddons();
|
|
30
|
+
for (const addon of addons) {
|
|
31
|
+
const dir = getAddonDir(addon, 'plugins');
|
|
32
|
+
const plugins = await scanModules<Plugin>(dir, 'addon', '插件', befly.pluginsConfig, addon);
|
|
33
|
+
addonPlugins.push(...plugins);
|
|
161
34
|
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
return plugins;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
/**
|
|
168
|
-
* 初始化单个插件
|
|
169
|
-
*/
|
|
170
|
-
async function initPlugin(befly: { pluginLists: Plugin[]; appContext: BeflyContext }, plugin: Plugin): Promise<void> {
|
|
171
|
-
befly.pluginLists.push(plugin);
|
|
172
|
-
|
|
173
|
-
if (typeof plugin?.onInit === 'function') {
|
|
174
|
-
befly.appContext[plugin.pluginName] = await plugin?.onInit(befly.appContext);
|
|
175
|
-
} else {
|
|
176
|
-
befly.appContext[plugin.pluginName] = {};
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
35
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
* @param befly - Befly实例(需要访问 pluginLists 和 appContext)
|
|
183
|
-
*/
|
|
184
|
-
export async function loadPlugins(befly: { pluginLists: Plugin[]; appContext: BeflyContext }): Promise<void> {
|
|
185
|
-
try {
|
|
186
|
-
const loadStartTime = Bun.nanoseconds();
|
|
187
|
-
const loadedPluginNames = new Set<string>();
|
|
36
|
+
// 3. 扫描项目插件
|
|
37
|
+
const appPlugins = await scanModules<Plugin>(projectPluginDir, 'app', '插件', befly.pluginsConfig);
|
|
188
38
|
|
|
189
|
-
//
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
39
|
+
// 4. 合并所有插件
|
|
40
|
+
allPlugins.push(...corePlugins);
|
|
41
|
+
allPlugins.push(...addonPlugins);
|
|
42
|
+
allPlugins.push(...appPlugins);
|
|
193
43
|
|
|
194
|
-
//
|
|
195
|
-
const
|
|
196
|
-
|
|
197
|
-
Logger.error('核心插件依赖关系错误,请检查插件的 after 属性');
|
|
198
|
-
process.exit(1);
|
|
199
|
-
}
|
|
44
|
+
// 5. 过滤禁用的插件
|
|
45
|
+
const disablePlugins = (befly as any).config?.disablePlugins || [];
|
|
46
|
+
const enabledPlugins = allPlugins.filter((plugin) => !disablePlugins.includes(plugin.name));
|
|
200
47
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
Logger.error('组件插件依赖关系错误,请检查插件的 after 属性');
|
|
204
|
-
process.exit(1);
|
|
48
|
+
if (disablePlugins.length > 0) {
|
|
49
|
+
Logger.info(`禁用插件: ${disablePlugins.join(', ')}`);
|
|
205
50
|
}
|
|
206
51
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
52
|
+
// 6. 排序与初始化
|
|
53
|
+
const sortedPlugins = sortModules(enabledPlugins);
|
|
54
|
+
if (sortedPlugins === false) {
|
|
55
|
+
Logger.error('插件依赖关系错误,请检查 after 属性');
|
|
210
56
|
process.exit(1);
|
|
211
57
|
}
|
|
212
58
|
|
|
213
|
-
|
|
214
|
-
// 3.1 初始化核心插件
|
|
215
|
-
for (const plugin of sortedCorePlugins) {
|
|
59
|
+
for (const plugin of sortedPlugins) {
|
|
216
60
|
try {
|
|
217
|
-
|
|
218
|
-
} catch (error: any) {
|
|
219
|
-
Logger.error(`核心插件 ${plugin.pluginName} 初始化失败`, error);
|
|
220
|
-
process.exit(1);
|
|
221
|
-
}
|
|
222
|
-
}
|
|
61
|
+
befly.pluginLists.push(plugin);
|
|
223
62
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
63
|
+
if (typeof plugin.handler === 'function') {
|
|
64
|
+
befly.appContext[plugin.name!] = await plugin.handler(befly.appContext);
|
|
65
|
+
} else {
|
|
66
|
+
befly.appContext[plugin.name!] = {};
|
|
67
|
+
}
|
|
228
68
|
} catch (error: any) {
|
|
229
|
-
Logger.error(
|
|
69
|
+
Logger.error(`插件 ${plugin.name} 初始化失败`, error);
|
|
230
70
|
process.exit(1);
|
|
231
71
|
}
|
|
232
72
|
}
|
|
233
|
-
|
|
234
|
-
// 3.3 初始化用户插件
|
|
235
|
-
for (const plugin of sortedUserPlugins) {
|
|
236
|
-
try {
|
|
237
|
-
await initPlugin(befly, plugin);
|
|
238
|
-
} catch (error: any) {
|
|
239
|
-
Logger.error(`用户插件 ${plugin.pluginName} 初始化失败`, error);
|
|
240
|
-
process.exit(1);
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
const totalLoadTime = calcPerfTime(loadStartTime);
|
|
245
73
|
} catch (error: any) {
|
|
246
74
|
Logger.error('加载插件时发生错误', error);
|
|
247
75
|
process.exit(1);
|
|
248
76
|
}
|
|
249
77
|
}
|
|
78
|
+
|
|
79
|
+
// ==================== 钩子加载逻辑 ====================
|
|
80
|
+
// 已移动到 loadHooks.ts
|
package/main.ts
CHANGED
|
@@ -3,39 +3,34 @@
|
|
|
3
3
|
* 提供简洁的框架接口,核心逻辑已提取到 loader 层
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
// ========== 外部依赖 ==========
|
|
7
|
+
import { calcPerfTime } from 'befly-util';
|
|
7
8
|
|
|
9
|
+
// ========== 相对导入 ==========
|
|
8
10
|
import { Logger } from './lib/logger.js';
|
|
9
11
|
import { Cipher } from './lib/cipher.js';
|
|
10
12
|
import { Jwt } from './lib/jwt.js';
|
|
11
13
|
import { Database } from './lib/database.js';
|
|
14
|
+
import { DbHelper } from './lib/dbHelper.js';
|
|
15
|
+
import { RedisHelper } from './lib/redisHelper.js';
|
|
12
16
|
import { loadPlugins } from './loader/loadPlugins.js';
|
|
17
|
+
import { loadHooks } from './loader/loadHooks.js';
|
|
13
18
|
import { loadApis } from './loader/loadApis.js';
|
|
14
19
|
import { rootHandler } from './router/root.js';
|
|
15
20
|
import { apiHandler } from './router/api.js';
|
|
16
21
|
import { staticHandler } from './router/static.js';
|
|
22
|
+
import { checkApp } from './checks/checkApp.js';
|
|
23
|
+
import { checkTable } from './checks/checkTable.js';
|
|
24
|
+
import { checkApi } from './checks/checkApi.js';
|
|
25
|
+
import { syncAllCommand } from './sync/syncAll.js';
|
|
17
26
|
import { coreDir } from './paths.js';
|
|
18
|
-
import {
|
|
19
|
-
import { RedisHelper } from './lib/redisHelper.js';
|
|
20
|
-
import { checkTable, checkApi, checkApp } from './check.js';
|
|
21
|
-
import {
|
|
22
|
-
//
|
|
23
|
-
Yes,
|
|
24
|
-
No,
|
|
25
|
-
keysToSnake,
|
|
26
|
-
keysToCamel,
|
|
27
|
-
arrayKeysToCamel,
|
|
28
|
-
pickFields,
|
|
29
|
-
fieldClear,
|
|
30
|
-
calcPerfTime,
|
|
31
|
-
scanAddons,
|
|
32
|
-
getAddonDir,
|
|
33
|
-
addonDirExists
|
|
34
|
-
} from './util.js';
|
|
27
|
+
import { defaultOptions } from './config.js';
|
|
35
28
|
|
|
29
|
+
// ========== 类型导入 ==========
|
|
36
30
|
import type { Server } from 'bun';
|
|
37
|
-
import type { BeflyContext } from './types/befly.js';
|
|
31
|
+
import type { BeflyContext, BeflyOptions } from './types/befly.js';
|
|
38
32
|
import type { Plugin } from './types/plugin.js';
|
|
33
|
+
import type { Hook } from './types/hook.js';
|
|
39
34
|
import type { ApiRoute } from './types/api.js';
|
|
40
35
|
/**
|
|
41
36
|
* Befly 框架核心类
|
|
@@ -48,142 +43,120 @@ export class Befly {
|
|
|
48
43
|
/** 插件列表 */
|
|
49
44
|
private pluginLists: Plugin[] = [];
|
|
50
45
|
|
|
46
|
+
/** 钩子列表 */
|
|
47
|
+
private hookLists: Hook[] = [];
|
|
48
|
+
|
|
51
49
|
/** 应用上下文 */
|
|
52
50
|
public appContext: BeflyContext;
|
|
53
51
|
|
|
54
|
-
|
|
52
|
+
/** 最终配置(合并默认值后) */
|
|
53
|
+
public config: BeflyOptions;
|
|
54
|
+
|
|
55
|
+
constructor(options: BeflyOptions = {}) {
|
|
55
56
|
this.appContext = {};
|
|
57
|
+
// 合并配置:用户配置 > 默认配置(最多 2 级)
|
|
58
|
+
this.config = { ...defaultOptions };
|
|
59
|
+
for (const key in options) {
|
|
60
|
+
const value = options[key as keyof BeflyOptions];
|
|
61
|
+
if (value !== undefined && value !== null) {
|
|
62
|
+
if (typeof value === 'object' && !Array.isArray(value)) {
|
|
63
|
+
this.config[key as keyof BeflyOptions] = { ...defaultOptions[key as keyof typeof defaultOptions], ...value } as any;
|
|
64
|
+
} else {
|
|
65
|
+
this.config[key as keyof BeflyOptions] = value as any;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
56
69
|
}
|
|
57
70
|
|
|
58
71
|
/**
|
|
59
72
|
* 启动完整的生命周期流程
|
|
60
73
|
* @returns HTTP 服务器实例
|
|
61
74
|
*/
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
75
|
+
public async start(): Promise<Server> {
|
|
76
|
+
try {
|
|
77
|
+
const serverStartTime = Bun.nanoseconds();
|
|
78
|
+
|
|
79
|
+
// 1. 执行启动检查
|
|
80
|
+
await checkApp();
|
|
81
|
+
await checkTable();
|
|
82
|
+
await checkApi();
|
|
83
|
+
|
|
84
|
+
// 4. 加载插件
|
|
85
|
+
await loadPlugins({
|
|
86
|
+
pluginLists: this.pluginLists,
|
|
87
|
+
appContext: this.appContext,
|
|
88
|
+
pluginsConfig: this.config as any
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
// 5. 加载钩子
|
|
92
|
+
await loadHooks({
|
|
93
|
+
hookLists: this.hookLists,
|
|
94
|
+
pluginsConfig: this.config as any
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
// 6. 加载所有 API
|
|
98
|
+
await loadApis(this.apiRoutes);
|
|
99
|
+
|
|
100
|
+
// 7. 自动同步 (默认开启)
|
|
101
|
+
await syncAllCommand(this.config);
|
|
102
|
+
|
|
103
|
+
// 8. 启动 HTTP 服务器
|
|
104
|
+
const server = Bun.serve({
|
|
105
|
+
port: this.config.appPort,
|
|
106
|
+
hostname: this.config.appHost,
|
|
107
|
+
routes: {
|
|
108
|
+
'/': rootHandler,
|
|
109
|
+
'/api/*': apiHandler(this.apiRoutes, this.hookLists, this.appContext),
|
|
110
|
+
'/*': staticHandler(this.config.cors)
|
|
111
|
+
},
|
|
112
|
+
error: (error: Error) => {
|
|
113
|
+
Logger.error('服务启动时发生错误', error);
|
|
114
|
+
return Response.json({ code: 1, msg: '内部服务器错误' });
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
const finalStartupTime = calcPerfTime(serverStartTime);
|
|
119
|
+
Logger.info(`${this.config.appName} 启动成功! `);
|
|
120
|
+
Logger.info(`服务器启动耗时: ${finalStartupTime}`);
|
|
121
|
+
Logger.info(`服务器监听地址: http://${this.config.appHost}:${this.config.appPort}`);
|
|
122
|
+
|
|
123
|
+
// 9. 注册优雅关闭处理
|
|
124
|
+
const gracefulShutdown = async (signal: string) => {
|
|
125
|
+
// 停止接收新请求
|
|
126
|
+
server.stop(true);
|
|
127
|
+
Logger.info('HTTP 服务器已停止');
|
|
128
|
+
|
|
129
|
+
// 关闭数据库连接
|
|
130
|
+
try {
|
|
131
|
+
await Database.disconnect();
|
|
132
|
+
Logger.info('数据库连接已关闭');
|
|
133
|
+
} catch (error: any) {
|
|
134
|
+
Logger.error('关闭数据库连接时出错:', error);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
Logger.info('服务器已优雅关闭');
|
|
138
|
+
process.exit(0);
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
process.on('SIGTERM', () => gracefulShutdown('SIGTERM'));
|
|
142
|
+
process.on('SIGINT', () => gracefulShutdown('SIGINT'));
|
|
143
|
+
|
|
144
|
+
return server;
|
|
145
|
+
} catch (error: any) {
|
|
146
|
+
Logger.error('Befly 启动失败', error);
|
|
72
147
|
process.exit(1);
|
|
73
148
|
}
|
|
74
|
-
|
|
75
|
-
// 3. 执行表定义检查
|
|
76
|
-
const tableCheckResult = await checkTable();
|
|
77
|
-
if (!tableCheckResult) {
|
|
78
|
-
Logger.error('表定义检查失败,程序退出');
|
|
79
|
-
process.exit(1);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// 4. 执行 API 定义检查
|
|
83
|
-
const apiCheckResult = await checkApi();
|
|
84
|
-
if (!apiCheckResult) {
|
|
85
|
-
Logger.error('API 定义检查失败,程序退出');
|
|
86
|
-
process.exit(1);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// 5. 加载插件
|
|
90
|
-
await loadPlugins({
|
|
91
|
-
pluginLists: this.pluginLists,
|
|
92
|
-
appContext: this.appContext
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
// 4. 启动 HTTP 服务器
|
|
96
|
-
const totalStartupTime = calcPerfTime(serverStartTime);
|
|
97
|
-
|
|
98
|
-
return await this.startServer();
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* 启动 HTTP 服务器
|
|
103
|
-
* @returns HTTP 服务器实例
|
|
104
|
-
*/
|
|
105
|
-
private async startServer(): Promise<Server> {
|
|
106
|
-
const startTime = Bun.nanoseconds();
|
|
107
|
-
|
|
108
|
-
const server = Bun.serve({
|
|
109
|
-
port: Env.APP_PORT,
|
|
110
|
-
hostname: Env.APP_HOST,
|
|
111
|
-
routes: {
|
|
112
|
-
'/': rootHandler,
|
|
113
|
-
'/api/*': apiHandler(this.apiRoutes, this.pluginLists, this.appContext),
|
|
114
|
-
'/*': staticHandler
|
|
115
|
-
},
|
|
116
|
-
error: (error: Error) => {
|
|
117
|
-
Logger.error('服务启动时发生错误', error);
|
|
118
|
-
return Response.json({ code: 1, msg: '内部服务器错误' });
|
|
119
|
-
}
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
const finalStartupTime = calcPerfTime(startTime);
|
|
123
|
-
Logger.info(`${Env.APP_NAME} 启动成功! `);
|
|
124
|
-
Logger.info(`服务器启动耗时: ${finalStartupTime}`);
|
|
125
|
-
Logger.info(`服务器监听地址: http://${Env.APP_HOST}:${Env.APP_PORT}`);
|
|
126
|
-
|
|
127
|
-
return server;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* 启动服务器并注册优雅关闭处理
|
|
132
|
-
*/
|
|
133
|
-
async listen(): Promise<Server> {
|
|
134
|
-
const server = await this.start();
|
|
135
|
-
|
|
136
|
-
const gracefulShutdown = async (signal: string) => {
|
|
137
|
-
// 1. 停止接收新请求
|
|
138
|
-
server.stop(true);
|
|
139
|
-
Logger.info('HTTP 服务器已停止');
|
|
140
|
-
|
|
141
|
-
// 2. 关闭数据库连接
|
|
142
|
-
try {
|
|
143
|
-
await Database.disconnect();
|
|
144
|
-
Logger.info('数据库连接已关闭');
|
|
145
|
-
} catch (error: any) {
|
|
146
|
-
Logger.err('关闭数据库连接时出错:', error);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
Logger.info('服务器已优雅关闭');
|
|
150
|
-
process.exit(0);
|
|
151
|
-
};
|
|
152
|
-
|
|
153
|
-
process.on('SIGTERM', () => gracefulShutdown('SIGTERM'));
|
|
154
|
-
process.on('SIGINT', () => gracefulShutdown('SIGINT'));
|
|
155
|
-
|
|
156
|
-
return server;
|
|
157
149
|
}
|
|
158
150
|
}
|
|
159
151
|
|
|
160
152
|
// 核心类和工具导出
|
|
161
153
|
export {
|
|
162
154
|
// 配置
|
|
163
|
-
Env,
|
|
164
155
|
Logger,
|
|
165
156
|
Cipher,
|
|
166
157
|
Jwt,
|
|
167
|
-
Yes,
|
|
168
|
-
No,
|
|
169
158
|
Database,
|
|
170
159
|
DbHelper,
|
|
171
160
|
RedisHelper,
|
|
172
|
-
coreDir
|
|
173
|
-
checkTable,
|
|
174
|
-
checkApi,
|
|
175
|
-
checkApp
|
|
176
|
-
};
|
|
177
|
-
|
|
178
|
-
// 工具函数命名空间导出
|
|
179
|
-
export const utils = {
|
|
180
|
-
keysToSnake: keysToSnake,
|
|
181
|
-
keysToCamel: keysToCamel,
|
|
182
|
-
arrayKeysToCamel: arrayKeysToCamel,
|
|
183
|
-
pickFields: pickFields,
|
|
184
|
-
fieldClear: fieldClear,
|
|
185
|
-
calcPerfTime: calcPerfTime,
|
|
186
|
-
scanAddons: scanAddons,
|
|
187
|
-
getAddonDir: getAddonDir,
|
|
188
|
-
addonDirExists: addonDirExists
|
|
161
|
+
coreDir
|
|
189
162
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "befly",
|
|
3
|
-
"version": "3.8.
|
|
3
|
+
"version": "3.8.20",
|
|
4
4
|
"description": "Befly - 为 Bun 专属打造的 TypeScript API 接口框架核心引擎",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"private": false,
|
|
@@ -9,14 +9,15 @@
|
|
|
9
9
|
"registry": "https://registry.npmjs.org"
|
|
10
10
|
},
|
|
11
11
|
"main": "main.ts",
|
|
12
|
-
"types": "
|
|
12
|
+
"types": "main.ts",
|
|
13
13
|
"exports": {
|
|
14
14
|
".": {
|
|
15
|
+
"types": "./main.ts",
|
|
15
16
|
"default": "./main.ts"
|
|
16
17
|
},
|
|
17
18
|
"./types": {
|
|
18
|
-
"types": "./types/index.
|
|
19
|
-
"default": "./types/index.
|
|
19
|
+
"types": "./types/index.ts",
|
|
20
|
+
"default": "./types/index.ts"
|
|
20
21
|
}
|
|
21
22
|
},
|
|
22
23
|
"scripts": {
|
|
@@ -38,6 +39,8 @@
|
|
|
38
39
|
"homepage": "https://chensuiyi.me",
|
|
39
40
|
"license": "Apache-2.0",
|
|
40
41
|
"files": [
|
|
42
|
+
"sync/",
|
|
43
|
+
"config/",
|
|
41
44
|
"lib/",
|
|
42
45
|
"loader/",
|
|
43
46
|
"plugins/",
|
|
@@ -51,9 +54,9 @@
|
|
|
51
54
|
"LICENSE",
|
|
52
55
|
"main.ts",
|
|
53
56
|
"check.ts",
|
|
54
|
-
"env.ts",
|
|
55
57
|
"paths.ts",
|
|
56
|
-
"
|
|
58
|
+
"response.ts",
|
|
59
|
+
"dist/",
|
|
57
60
|
"package.json",
|
|
58
61
|
"README.md",
|
|
59
62
|
"LICENSE"
|
|
@@ -62,8 +65,13 @@
|
|
|
62
65
|
"bun": ">=1.3.0"
|
|
63
66
|
},
|
|
64
67
|
"dependencies": {
|
|
68
|
+
"befly-util": "0.1.2",
|
|
69
|
+
"chalk": "^5.6.2",
|
|
65
70
|
"es-toolkit": "^1.41.0",
|
|
66
71
|
"pathe": "^2.0.3"
|
|
67
72
|
},
|
|
68
|
-
"gitHead": "
|
|
73
|
+
"gitHead": "e43aefa3abeda7a35669778793d39b23b4180c4e",
|
|
74
|
+
"devDependencies": {
|
|
75
|
+
"typescript": "^5.9.3"
|
|
76
|
+
}
|
|
69
77
|
}
|
package/paths.ts
CHANGED
|
@@ -39,6 +39,16 @@ export const coreCheckDir = join(__dirname, 'checks');
|
|
|
39
39
|
*/
|
|
40
40
|
export const corePluginDir = join(__dirname, 'plugins');
|
|
41
41
|
|
|
42
|
+
/**
|
|
43
|
+
* Core 框架钩子目录
|
|
44
|
+
* @description packages/core/hooks/
|
|
45
|
+
* @usage 存放内置钩子(auth, cors, parser 等)
|
|
46
|
+
*/
|
|
47
|
+
export const coreHookDir = join(__dirname, 'hooks');
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Core 框架 API 目录
|
|
51
|
+
|
|
42
52
|
/**
|
|
43
53
|
* Core 框架 API 目录
|
|
44
54
|
* @description packages/core/apis/
|
|
@@ -76,6 +86,16 @@ export const projectCheckDir = join(projectDir, 'checks');
|
|
|
76
86
|
*/
|
|
77
87
|
export const projectPluginDir = join(projectDir, 'plugins');
|
|
78
88
|
|
|
89
|
+
/**
|
|
90
|
+
* 项目钩子目录
|
|
91
|
+
* @description {projectDir}/hooks/
|
|
92
|
+
* @usage 存放用户自定义钩子
|
|
93
|
+
*/
|
|
94
|
+
export const projectHookDir = join(projectDir, 'hooks');
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* 项目 API 目录
|
|
98
|
+
|
|
79
99
|
/**
|
|
80
100
|
* 项目 API 目录
|
|
81
101
|
* @description {projectDir}/apis/
|