befly 2.3.3 → 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.
- package/apis/health/info.ts +64 -0
- package/apis/tool/tokenCheck.ts +51 -0
- package/bin/befly.ts +202 -0
- package/checks/conflict.ts +408 -0
- package/checks/{table.js → table.ts} +139 -61
- package/config/env.ts +218 -0
- package/config/reserved.ts +96 -0
- package/main.ts +101 -0
- package/package.json +44 -8
- package/plugins/{db.js → db.ts} +24 -11
- package/plugins/logger.ts +28 -0
- package/plugins/redis.ts +51 -0
- package/plugins/tool.ts +34 -0
- package/scripts/syncDb/apply.ts +171 -0
- package/scripts/syncDb/constants.ts +70 -0
- package/scripts/syncDb/ddl.ts +182 -0
- package/scripts/syncDb/helpers.ts +172 -0
- package/scripts/syncDb/index.ts +215 -0
- package/scripts/syncDb/schema.ts +199 -0
- package/scripts/syncDb/sqlite.ts +50 -0
- package/scripts/syncDb/state.ts +104 -0
- package/scripts/syncDb/table.ts +204 -0
- package/scripts/syncDb/tableCreate.ts +142 -0
- package/scripts/syncDb/tests/constants.test.ts +104 -0
- package/scripts/syncDb/tests/ddl.test.ts +134 -0
- package/scripts/syncDb/tests/helpers.test.ts +70 -0
- package/scripts/syncDb/types.ts +92 -0
- package/scripts/syncDb/version.ts +73 -0
- package/scripts/syncDb.ts +9 -0
- package/scripts/{syncDev.js → syncDev.ts} +41 -25
- package/system.ts +149 -0
- package/tables/_common.json +21 -0
- package/tables/admin.json +10 -0
- package/tsconfig.json +58 -0
- package/types/api.d.ts +246 -0
- package/types/befly.d.ts +234 -0
- package/types/common.d.ts +215 -0
- package/types/context.ts +167 -0
- package/types/crypto.d.ts +23 -0
- package/types/database.d.ts +278 -0
- package/types/index.d.ts +16 -0
- package/types/index.ts +459 -0
- package/types/jwt.d.ts +99 -0
- package/types/logger.d.ts +43 -0
- package/types/plugin.d.ts +109 -0
- package/types/redis.d.ts +44 -0
- package/types/tool.d.ts +67 -0
- package/types/validator.d.ts +45 -0
- package/utils/addonHelper.ts +60 -0
- package/utils/api.ts +23 -0
- package/utils/{colors.js → colors.ts} +79 -21
- package/utils/crypto.ts +308 -0
- package/utils/datetime.ts +51 -0
- package/utils/dbHelper.ts +142 -0
- package/utils/errorHandler.ts +68 -0
- package/utils/index.ts +46 -0
- package/utils/jwt.ts +493 -0
- package/utils/logger.ts +284 -0
- package/utils/objectHelper.ts +68 -0
- package/utils/pluginHelper.ts +62 -0
- package/utils/redisHelper.ts +338 -0
- package/utils/response.ts +38 -0
- package/utils/{sqlBuilder.js → sqlBuilder.ts} +233 -97
- package/utils/sqlHelper.ts +447 -0
- package/utils/tableHelper.ts +167 -0
- package/utils/tool.ts +230 -0
- package/utils/typeHelper.ts +101 -0
- package/utils/validate.ts +451 -0
- package/utils/{xml.js → xml.ts} +100 -74
- package/.npmrc +0 -3
- package/.prettierignore +0 -2
- package/.prettierrc +0 -11
- package/apis/health/info.js +0 -49
- package/apis/tool/tokenCheck.js +0 -29
- package/bin/befly.js +0 -109
- package/config/env.js +0 -64
- package/main.js +0 -579
- package/plugins/logger.js +0 -14
- package/plugins/redis.js +0 -32
- package/plugins/tool.js +0 -8
- package/scripts/syncDb.js +0 -752
- package/system.js +0 -118
- package/tables/common.json +0 -16
- package/tables/tool.json +0 -6
- package/utils/api.js +0 -27
- package/utils/crypto.js +0 -260
- package/utils/index.js +0 -334
- package/utils/jwt.js +0 -387
- package/utils/logger.js +0 -143
- package/utils/redisHelper.js +0 -74
- package/utils/sqlManager.js +0 -471
- package/utils/tool.js +0 -31
- package/utils/validate.js +0 -226
|
@@ -0,0 +1,408 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 资源冲突检测
|
|
3
|
+
* 在系统启动前检测表名、API 路由、插件名等资源是否存在冲突
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
import { Logger } from '../utils/logger.js';
|
|
8
|
+
import { scanAddons, getAddonDir, hasAddonDir } from '../utils/addonHelper.js';
|
|
9
|
+
import { __dirtables, __dirapis, __dirplugins, getProjectDir } from '../system.js';
|
|
10
|
+
import { isReservedTableName, isReservedRoute, isReservedPluginName, isReservedAddonName, getReservedTablePrefixes, getReservedRoutes, getReservedPlugins, getReservedAddonNames } from '../config/reserved.js';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* 资源注册表
|
|
14
|
+
*/
|
|
15
|
+
interface ResourceRegistry {
|
|
16
|
+
tables: Map<string, string>; // 表名 -> 来源
|
|
17
|
+
routes: Map<string, string>; // 路由 -> 来源
|
|
18
|
+
plugins: Map<string, string>; // 插件名 -> 来源
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* 冲突检测结果
|
|
23
|
+
*/
|
|
24
|
+
interface ConflictResult {
|
|
25
|
+
hasConflicts: boolean;
|
|
26
|
+
conflicts: string[];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* 收集核心表定义
|
|
31
|
+
*/
|
|
32
|
+
async function collectCoreTables(registry: ResourceRegistry): Promise<void> {
|
|
33
|
+
try {
|
|
34
|
+
const glob = new Bun.Glob('*.json');
|
|
35
|
+
for await (const file of glob.scan({
|
|
36
|
+
cwd: __dirtables,
|
|
37
|
+
onlyFiles: true,
|
|
38
|
+
absolute: true
|
|
39
|
+
})) {
|
|
40
|
+
const fileName = path.basename(file, '.json');
|
|
41
|
+
if (fileName.startsWith('_')) continue;
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
const tableDefine = await Bun.file(file).json();
|
|
45
|
+
const tableName = tableDefine.tableName || `sys_${fileName}`;
|
|
46
|
+
|
|
47
|
+
if (registry.tables.has(tableName)) {
|
|
48
|
+
Logger.error(`核心表 "${tableName}" 重复定义`);
|
|
49
|
+
} else {
|
|
50
|
+
registry.tables.set(tableName, 'core');
|
|
51
|
+
}
|
|
52
|
+
} catch (error: any) {
|
|
53
|
+
// 表定义解析错误会在 table.ts 中处理,这里跳过
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
} catch (error: any) {
|
|
57
|
+
Logger.error('收集核心表定义时出错:', error);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* 收集核心 API 路由
|
|
63
|
+
*/
|
|
64
|
+
async function collectCoreApis(registry: ResourceRegistry): Promise<void> {
|
|
65
|
+
try {
|
|
66
|
+
const glob = new Bun.Glob('**/*.ts');
|
|
67
|
+
for await (const file of glob.scan({
|
|
68
|
+
cwd: __dirapis,
|
|
69
|
+
onlyFiles: true,
|
|
70
|
+
absolute: true
|
|
71
|
+
})) {
|
|
72
|
+
const apiPath = path
|
|
73
|
+
.relative(__dirapis, file)
|
|
74
|
+
.replace(/\.(js|ts)$/, '')
|
|
75
|
+
.replace(/\\/g, '/');
|
|
76
|
+
if (apiPath.indexOf('_') !== -1) continue;
|
|
77
|
+
|
|
78
|
+
try {
|
|
79
|
+
const api = (await import(file)).default;
|
|
80
|
+
const route = `${(api.method || 'POST').toUpperCase()}/api/${apiPath}`;
|
|
81
|
+
|
|
82
|
+
if (registry.routes.has(route)) {
|
|
83
|
+
Logger.error(`核心路由 "${route}" 重复定义`);
|
|
84
|
+
} else {
|
|
85
|
+
registry.routes.set(route, 'core');
|
|
86
|
+
}
|
|
87
|
+
} catch (error: any) {
|
|
88
|
+
// API 加载错误会在 loader.ts 中处理,这里跳过
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
} catch (error: any) {
|
|
92
|
+
Logger.error('收集核心 API 路由时出错:', error);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* 收集核心插件
|
|
98
|
+
*/
|
|
99
|
+
async function collectCorePlugins(registry: ResourceRegistry): Promise<void> {
|
|
100
|
+
try {
|
|
101
|
+
const glob = new Bun.Glob('*.ts');
|
|
102
|
+
for await (const file of glob.scan({
|
|
103
|
+
cwd: __dirplugins,
|
|
104
|
+
onlyFiles: true,
|
|
105
|
+
absolute: true
|
|
106
|
+
})) {
|
|
107
|
+
const pluginName = path.basename(file).replace(/\.(js|ts)$/, '');
|
|
108
|
+
if (pluginName.startsWith('_')) continue;
|
|
109
|
+
|
|
110
|
+
if (registry.plugins.has(pluginName)) {
|
|
111
|
+
Logger.error(`核心插件 "${pluginName}" 重复定义`);
|
|
112
|
+
} else {
|
|
113
|
+
registry.plugins.set(pluginName, 'core');
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
} catch (error: any) {
|
|
117
|
+
Logger.error('收集核心插件时出错:', error);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* 收集 addon 资源
|
|
123
|
+
*/
|
|
124
|
+
async function collectAddonResources(addonName: string, registry: ResourceRegistry): Promise<string[]> {
|
|
125
|
+
const conflicts: string[] = [];
|
|
126
|
+
|
|
127
|
+
// 检查 addon 名称是否使用保留名称
|
|
128
|
+
if (isReservedAddonName(addonName)) {
|
|
129
|
+
conflicts.push(`Addon 名称 "${addonName}" 使用了保留名称,保留名称包括: ${getReservedAddonNames().join(', ')}`);
|
|
130
|
+
return conflicts;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// 收集 addon 表定义
|
|
134
|
+
if (hasAddonDir(addonName, 'tables')) {
|
|
135
|
+
const addonTablesDir = getAddonDir(addonName, 'tables');
|
|
136
|
+
const glob = new Bun.Glob('*.json');
|
|
137
|
+
|
|
138
|
+
for await (const file of glob.scan({
|
|
139
|
+
cwd: addonTablesDir,
|
|
140
|
+
onlyFiles: true,
|
|
141
|
+
absolute: true
|
|
142
|
+
})) {
|
|
143
|
+
const fileName = path.basename(file, '.json');
|
|
144
|
+
if (fileName.startsWith('_')) continue;
|
|
145
|
+
|
|
146
|
+
try {
|
|
147
|
+
const tableDefine = await Bun.file(file).json();
|
|
148
|
+
const tableName = tableDefine.tableName || `${addonName}_${fileName}`;
|
|
149
|
+
|
|
150
|
+
// 检查是否使用保留前缀
|
|
151
|
+
if (isReservedTableName(tableName)) {
|
|
152
|
+
conflicts.push(`Addon [${addonName}] 表 "${tableName}" 使用了保留前缀,保留前缀包括: ${getReservedTablePrefixes().join(', ')}`);
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// 检查是否与已有表冲突
|
|
157
|
+
if (registry.tables.has(tableName)) {
|
|
158
|
+
conflicts.push(`Addon [${addonName}] 表 "${tableName}" 与 ${registry.tables.get(tableName)} 冲突`);
|
|
159
|
+
} else {
|
|
160
|
+
registry.tables.set(tableName, `addon[${addonName}]`);
|
|
161
|
+
}
|
|
162
|
+
} catch (error: any) {
|
|
163
|
+
// 表定义解析错误会在 table.ts 中处理,这里跳过
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// 收集 addon API 路由
|
|
169
|
+
if (hasAddonDir(addonName, 'apis')) {
|
|
170
|
+
const addonApisDir = getAddonDir(addonName, 'apis');
|
|
171
|
+
const glob = new Bun.Glob('**/*.ts');
|
|
172
|
+
|
|
173
|
+
for await (const file of glob.scan({
|
|
174
|
+
cwd: addonApisDir,
|
|
175
|
+
onlyFiles: true,
|
|
176
|
+
absolute: true
|
|
177
|
+
})) {
|
|
178
|
+
const apiPath = path
|
|
179
|
+
.relative(addonApisDir, file)
|
|
180
|
+
.replace(/\.(js|ts)$/, '')
|
|
181
|
+
.replace(/\\/g, '/');
|
|
182
|
+
if (apiPath.indexOf('_') !== -1) continue;
|
|
183
|
+
|
|
184
|
+
try {
|
|
185
|
+
const api = (await import(file)).default;
|
|
186
|
+
const route = `${(api.method || 'POST').toUpperCase()}/api/${addonName}/${apiPath}`;
|
|
187
|
+
|
|
188
|
+
// 检查是否使用保留路由
|
|
189
|
+
if (isReservedRoute(route)) {
|
|
190
|
+
conflicts.push(`Addon [${addonName}] 路由 "${route}" 使用了保留路径,保留路径包括: ${getReservedRoutes().join(', ')}`);
|
|
191
|
+
continue;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// 检查是否与已有路由冲突
|
|
195
|
+
if (registry.routes.has(route)) {
|
|
196
|
+
conflicts.push(`Addon [${addonName}] 路由 "${route}" 与 ${registry.routes.get(route)} 冲突`);
|
|
197
|
+
} else {
|
|
198
|
+
registry.routes.set(route, `addon[${addonName}]`);
|
|
199
|
+
}
|
|
200
|
+
} catch (error: any) {
|
|
201
|
+
// API 加载错误会在 loader.ts 中处理,这里跳过
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// 收集 addon 插件
|
|
207
|
+
if (hasAddonDir(addonName, 'plugins')) {
|
|
208
|
+
const addonPluginsDir = getAddonDir(addonName, 'plugins');
|
|
209
|
+
const glob = new Bun.Glob('*.ts');
|
|
210
|
+
|
|
211
|
+
for await (const file of glob.scan({
|
|
212
|
+
cwd: addonPluginsDir,
|
|
213
|
+
onlyFiles: true,
|
|
214
|
+
absolute: true
|
|
215
|
+
})) {
|
|
216
|
+
const fileName = path.basename(file).replace(/\.(js|ts)$/, '');
|
|
217
|
+
if (fileName.startsWith('_')) continue;
|
|
218
|
+
|
|
219
|
+
// Addon 插件使用点号命名空间
|
|
220
|
+
const pluginName = `${addonName}.${fileName}`;
|
|
221
|
+
|
|
222
|
+
// 检查是否使用保留名称
|
|
223
|
+
if (isReservedPluginName(pluginName)) {
|
|
224
|
+
conflicts.push(`Addon [${addonName}] 插件 "${pluginName}" 使用了保留名称,保留名称包括: ${getReservedPlugins().join(', ')}`);
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// 检查是否与已有插件冲突
|
|
229
|
+
if (registry.plugins.has(pluginName)) {
|
|
230
|
+
conflicts.push(`Addon [${addonName}] 插件 "${pluginName}" 与 ${registry.plugins.get(pluginName)} 冲突`);
|
|
231
|
+
} else {
|
|
232
|
+
registry.plugins.set(pluginName, `addon[${addonName}]`);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
return conflicts;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* 收集用户资源
|
|
242
|
+
*/
|
|
243
|
+
async function collectUserResources(registry: ResourceRegistry): Promise<string[]> {
|
|
244
|
+
const conflicts: string[] = [];
|
|
245
|
+
|
|
246
|
+
// 收集用户表定义
|
|
247
|
+
const userTablesDir = getProjectDir('tables');
|
|
248
|
+
try {
|
|
249
|
+
const glob = new Bun.Glob('*.json');
|
|
250
|
+
for await (const file of glob.scan({
|
|
251
|
+
cwd: userTablesDir,
|
|
252
|
+
onlyFiles: true,
|
|
253
|
+
absolute: true
|
|
254
|
+
})) {
|
|
255
|
+
const fileName = path.basename(file, '.json');
|
|
256
|
+
if (fileName.startsWith('_')) continue;
|
|
257
|
+
|
|
258
|
+
try {
|
|
259
|
+
const tableDefine = await Bun.file(file).json();
|
|
260
|
+
const tableName = tableDefine.tableName || fileName;
|
|
261
|
+
|
|
262
|
+
// 检查是否使用保留前缀
|
|
263
|
+
if (isReservedTableName(tableName)) {
|
|
264
|
+
conflicts.push(`用户表 "${tableName}" 使用了保留前缀,保留前缀包括: ${getReservedTablePrefixes().join(', ')}`);
|
|
265
|
+
continue;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// 检查是否与已有表冲突
|
|
269
|
+
if (registry.tables.has(tableName)) {
|
|
270
|
+
conflicts.push(`用户表 "${tableName}" 与 ${registry.tables.get(tableName)} 冲突`);
|
|
271
|
+
} else {
|
|
272
|
+
registry.tables.set(tableName, 'user');
|
|
273
|
+
}
|
|
274
|
+
} catch (error: any) {
|
|
275
|
+
// 表定义解析错误会在 table.ts 中处理,这里跳过
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
} catch (error: any) {
|
|
279
|
+
// 用户可能没有 tables 目录,这是正常的
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// 收集用户 API 路由
|
|
283
|
+
const userApisDir = getProjectDir('apis');
|
|
284
|
+
try {
|
|
285
|
+
const glob = new Bun.Glob('**/*.ts');
|
|
286
|
+
for await (const file of glob.scan({
|
|
287
|
+
cwd: userApisDir,
|
|
288
|
+
onlyFiles: true,
|
|
289
|
+
absolute: true
|
|
290
|
+
})) {
|
|
291
|
+
const apiPath = path
|
|
292
|
+
.relative(userApisDir, file)
|
|
293
|
+
.replace(/\.(js|ts)$/, '')
|
|
294
|
+
.replace(/\\/g, '/');
|
|
295
|
+
if (apiPath.indexOf('_') !== -1) continue;
|
|
296
|
+
|
|
297
|
+
try {
|
|
298
|
+
const api = (await import(file)).default;
|
|
299
|
+
const route = `${(api.method || 'POST').toUpperCase()}/api/${apiPath}`;
|
|
300
|
+
|
|
301
|
+
// 检查是否使用保留路由
|
|
302
|
+
if (isReservedRoute(route)) {
|
|
303
|
+
conflicts.push(`用户路由 "${route}" 使用了保留路径,保留路径包括: ${getReservedRoutes().join(', ')}`);
|
|
304
|
+
continue;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// 检查是否与已有路由冲突
|
|
308
|
+
if (registry.routes.has(route)) {
|
|
309
|
+
conflicts.push(`用户路由 "${route}" 与 ${registry.routes.get(route)} 冲突`);
|
|
310
|
+
} else {
|
|
311
|
+
registry.routes.set(route, 'user');
|
|
312
|
+
}
|
|
313
|
+
} catch (error: any) {
|
|
314
|
+
// API 加载错误会在 loader.ts 中处理,这里跳过
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
} catch (error: any) {
|
|
318
|
+
// 用户可能没有 apis 目录,这是正常的
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// 收集用户插件
|
|
322
|
+
const userPluginsDir = getProjectDir('plugins');
|
|
323
|
+
try {
|
|
324
|
+
const glob = new Bun.Glob('*.ts');
|
|
325
|
+
for await (const file of glob.scan({
|
|
326
|
+
cwd: userPluginsDir,
|
|
327
|
+
onlyFiles: true,
|
|
328
|
+
absolute: true
|
|
329
|
+
})) {
|
|
330
|
+
const pluginName = path.basename(file).replace(/\.(js|ts)$/, '');
|
|
331
|
+
if (pluginName.startsWith('_')) continue;
|
|
332
|
+
|
|
333
|
+
// 检查是否使用保留名称
|
|
334
|
+
if (isReservedPluginName(pluginName)) {
|
|
335
|
+
conflicts.push(`用户插件 "${pluginName}" 使用了保留名称,保留名称包括: ${getReservedPlugins().join(', ')}`);
|
|
336
|
+
continue;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// 检查是否与已有插件冲突
|
|
340
|
+
if (registry.plugins.has(pluginName)) {
|
|
341
|
+
conflicts.push(`用户插件 "${pluginName}" 与 ${registry.plugins.get(pluginName)} 冲突`);
|
|
342
|
+
} else {
|
|
343
|
+
registry.plugins.set(pluginName, 'user');
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
} catch (error: any) {
|
|
347
|
+
// 用户可能没有 plugins 目录,这是正常的
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
return conflicts;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* 执行资源冲突检测
|
|
355
|
+
*/
|
|
356
|
+
export default async function checkConflict(): Promise<boolean> {
|
|
357
|
+
try {
|
|
358
|
+
// 初始化资源注册表
|
|
359
|
+
const registry: ResourceRegistry = {
|
|
360
|
+
tables: new Map(),
|
|
361
|
+
routes: new Map(),
|
|
362
|
+
plugins: new Map()
|
|
363
|
+
};
|
|
364
|
+
|
|
365
|
+
const allConflicts: string[] = [];
|
|
366
|
+
|
|
367
|
+
// 1. 收集核心资源
|
|
368
|
+
await collectCoreTables(registry);
|
|
369
|
+
await collectCoreApis(registry);
|
|
370
|
+
await collectCorePlugins(registry);
|
|
371
|
+
|
|
372
|
+
// 2. 收集 addon 资源
|
|
373
|
+
const addons = scanAddons();
|
|
374
|
+
for (const addon of addons) {
|
|
375
|
+
const addonConflicts = await collectAddonResources(addon, registry);
|
|
376
|
+
allConflicts.push(...addonConflicts);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// 3. 收集用户资源
|
|
380
|
+
const userConflicts = await collectUserResources(registry);
|
|
381
|
+
allConflicts.push(...userConflicts);
|
|
382
|
+
|
|
383
|
+
// 4. 报告冲突
|
|
384
|
+
if (allConflicts.length > 0) {
|
|
385
|
+
Logger.error('');
|
|
386
|
+
Logger.error('❌ 检测到资源冲突:');
|
|
387
|
+
Logger.error('');
|
|
388
|
+
allConflicts.forEach((conflict, index) => {
|
|
389
|
+
Logger.error(` ${index + 1}. ${conflict}`);
|
|
390
|
+
});
|
|
391
|
+
Logger.error('');
|
|
392
|
+
Logger.error('请解决以上冲突后再启动服务器');
|
|
393
|
+
Logger.error('');
|
|
394
|
+
return false;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
// 5. 统计信息
|
|
398
|
+
Logger.info(`资源冲突检测通过 ✓`);
|
|
399
|
+
Logger.info(` - 表: ${registry.tables.size} 个`);
|
|
400
|
+
Logger.info(` - 路由: ${registry.routes.size} 个`);
|
|
401
|
+
Logger.info(` - 插件: ${registry.plugins.size} 个`);
|
|
402
|
+
|
|
403
|
+
return true;
|
|
404
|
+
} catch (error: any) {
|
|
405
|
+
Logger.error('资源冲突检测时发生错误:', error);
|
|
406
|
+
return false;
|
|
407
|
+
}
|
|
408
|
+
}
|