befly 3.5.7 → 3.7.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/{checks/table.ts → check.ts} +14 -17
- package/lib/addon.ts +77 -0
- package/lib/logger.ts +6 -15
- package/lifecycle/checker.ts +22 -48
- package/lifecycle/lifecycle.ts +7 -5
- package/lifecycle/loader.ts +5 -6
- package/main.ts +12 -1
- package/package.json +3 -14
- package/paths.ts +5 -54
- package/plugins/cache.ts +0 -1
- package/plugins/db.ts +0 -1
- package/plugins/logger.ts +0 -1
- package/plugins/redis.ts +0 -6
- package/util.ts +1 -83
- package/apis/admin/del.ts +0 -35
- package/apis/admin/info.ts +0 -50
- package/apis/admin/ins.ts +0 -61
- package/apis/admin/list.ts +0 -20
- package/apis/admin/roleDetail.ts +0 -35
- package/apis/admin/roleSave.ts +0 -40
- package/apis/admin/upd.ts +0 -51
- package/apis/api/all.ts +0 -37
- package/apis/auth/login.ts +0 -78
- package/apis/auth/logout.ts +0 -23
- package/apis/auth/register.ts +0 -50
- package/apis/auth/sendSmsCode.ts +0 -36
- package/apis/cache/refresh.ts +0 -34
- package/apis/dashboard/addonList.ts +0 -47
- package/apis/dashboard/changelog.ts +0 -37
- package/apis/dashboard/configStatus.ts +0 -54
- package/apis/dashboard/environmentInfo.ts +0 -46
- package/apis/dashboard/performanceMetrics.ts +0 -23
- package/apis/dashboard/permissionStats.ts +0 -31
- package/apis/dashboard/serviceStatus.ts +0 -82
- package/apis/dashboard/systemInfo.ts +0 -26
- package/apis/dashboard/systemOverview.ts +0 -32
- package/apis/dashboard/systemResources.ts +0 -119
- package/apis/dict/all.ts +0 -25
- package/apis/dict/del.ts +0 -19
- package/apis/dict/detail.ts +0 -21
- package/apis/dict/ins.ts +0 -27
- package/apis/dict/list.ts +0 -18
- package/apis/dict/upd.ts +0 -31
- package/apis/menu/all.ts +0 -68
- package/apis/menu/del.ts +0 -37
- package/apis/menu/ins.ts +0 -20
- package/apis/menu/list.ts +0 -21
- package/apis/menu/upd.ts +0 -29
- package/apis/role/apiDetail.ts +0 -30
- package/apis/role/apiSave.ts +0 -41
- package/apis/role/del.ts +0 -44
- package/apis/role/detail.ts +0 -24
- package/apis/role/ins.ts +0 -39
- package/apis/role/list.ts +0 -14
- package/apis/role/menuDetail.ts +0 -30
- package/apis/role/menuSave.ts +0 -38
- package/apis/role/save.ts +0 -44
- package/apis/role/upd.ts +0 -40
- package/bin/index.ts +0 -34
- package/checks/conflict.ts +0 -351
- package/commands/index.ts +0 -73
- package/commands/sync.ts +0 -88
- package/commands/syncApi.ts +0 -316
- package/commands/syncDb/apply.ts +0 -171
- package/commands/syncDb/constants.ts +0 -77
- package/commands/syncDb/ddl.ts +0 -191
- package/commands/syncDb/helpers.ts +0 -173
- package/commands/syncDb/index.ts +0 -217
- package/commands/syncDb/schema.ts +0 -199
- package/commands/syncDb/sqlite.ts +0 -50
- package/commands/syncDb/state.ts +0 -112
- package/commands/syncDb/table.ts +0 -214
- package/commands/syncDb/tableCreate.ts +0 -149
- package/commands/syncDb/types.ts +0 -92
- package/commands/syncDb/version.ts +0 -73
- package/commands/syncDb.ts +0 -34
- package/commands/syncDev.ts +0 -237
- package/commands/syncMenu.ts +0 -349
- package/commands/util.ts +0 -58
- package/entry.ts +0 -9
- package/tables/admin.json +0 -14
- package/tables/api.json +0 -8
- package/tables/dict.json +0 -8
- package/tables/menu.json +0 -8
- package/tables/role.json +0 -8
package/checks/conflict.ts
DELETED
|
@@ -1,351 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 资源冲突检测
|
|
3
|
-
* 在系统启动前检测表名、API 路由、插件名等资源是否存在冲突
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { relative, basename } from 'pathe';
|
|
7
|
-
import { existsSync } from 'node:fs';
|
|
8
|
-
import { Logger } from '../lib/logger.js';
|
|
9
|
-
import { projectPluginDir, coreTableDir, projectTableDir, projectApiDir } from '../paths.js';
|
|
10
|
-
import { scanAddons, getAddonDir, addonDirExists } from '../util.js';
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* 保留名称配置
|
|
14
|
-
*/
|
|
15
|
-
const RESERVED_NAMES = {
|
|
16
|
-
tablePrefix: ['sys_'],
|
|
17
|
-
plugins: ['db', 'logger', 'redis', 'tool'],
|
|
18
|
-
addonNames: ['app', 'api']
|
|
19
|
-
} as const;
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* 资源注册表
|
|
23
|
-
*/
|
|
24
|
-
interface ResourceRegistry {
|
|
25
|
-
tables: Map<string, string>; // 表名 -> 来源
|
|
26
|
-
routes: Map<string, string>; // 路由 -> 来源
|
|
27
|
-
plugins: Map<string, string>; // 插件名 -> 来源
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* 冲突检测结果
|
|
32
|
-
*/
|
|
33
|
-
interface ConflictResult {
|
|
34
|
-
hasConflicts: boolean;
|
|
35
|
-
conflicts: string[];
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* 收集核心插件
|
|
40
|
-
*/
|
|
41
|
-
async function collectCorePlugins(registry: ResourceRegistry): Promise<void> {
|
|
42
|
-
// 检查插件目录是否存在
|
|
43
|
-
if (!existsSync(projectPluginDir)) {
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
try {
|
|
48
|
-
const glob = new Bun.Glob('*.ts');
|
|
49
|
-
for await (const file of glob.scan({
|
|
50
|
-
cwd: projectPluginDir,
|
|
51
|
-
onlyFiles: true,
|
|
52
|
-
absolute: true
|
|
53
|
-
})) {
|
|
54
|
-
const pluginName = basename(file).replace(/\.ts$/, '');
|
|
55
|
-
if (pluginName.startsWith('_')) continue;
|
|
56
|
-
|
|
57
|
-
if (registry.plugins.has(pluginName)) {
|
|
58
|
-
Logger.warn(`核心插件 "${pluginName}" 重复定义`);
|
|
59
|
-
} else {
|
|
60
|
-
registry.plugins.set(pluginName, 'core');
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
} catch (error: any) {
|
|
64
|
-
Logger.error('收集插件时出错:', error);
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* 收集 addon 资源
|
|
70
|
-
*/
|
|
71
|
-
async function collectAddonResources(addonName: string, registry: ResourceRegistry): Promise<string[]> {
|
|
72
|
-
const conflicts: string[] = [];
|
|
73
|
-
|
|
74
|
-
// 检查 addon 名称是否使用保留名称
|
|
75
|
-
if (RESERVED_NAMES.addonNames.includes(addonName.toLowerCase())) {
|
|
76
|
-
conflicts.push(`组件名称 "${addonName}" 使用了保留名称,保留名称包括: ${RESERVED_NAMES.addonNames.join(', ')}`);
|
|
77
|
-
return conflicts;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// 收集 addon 表定义
|
|
81
|
-
if (addonDirExists(addonName, 'tables')) {
|
|
82
|
-
const addonTablesDir = getAddonDir(addonName, 'tables');
|
|
83
|
-
const glob = new Bun.Glob('*.json');
|
|
84
|
-
|
|
85
|
-
for await (const file of glob.scan({
|
|
86
|
-
cwd: addonTablesDir,
|
|
87
|
-
onlyFiles: true,
|
|
88
|
-
absolute: true
|
|
89
|
-
})) {
|
|
90
|
-
const fileName = basename(file, '.json');
|
|
91
|
-
if (fileName.startsWith('_')) continue;
|
|
92
|
-
|
|
93
|
-
try {
|
|
94
|
-
const tableDefine = await Bun.file(file).json();
|
|
95
|
-
const tableName = tableDefine.tableName || `${addonName}_${fileName}`;
|
|
96
|
-
|
|
97
|
-
// 检查是否使用保留前缀
|
|
98
|
-
if (RESERVED_NAMES.tablePrefix.some((prefix) => tableName.startsWith(prefix))) {
|
|
99
|
-
conflicts.push(`组件 ${addonName} 表 "${tableName}" 使用了保留前缀,保留前缀包括: ${RESERVED_NAMES.tablePrefix.join(', ')}`);
|
|
100
|
-
continue;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
// 检查是否与已有表冲突
|
|
104
|
-
if (registry.tables.has(tableName)) {
|
|
105
|
-
conflicts.push(`组件 ${addonName} 表 "${tableName}" 与 ${registry.tables.get(tableName)} 冲突`);
|
|
106
|
-
} else {
|
|
107
|
-
registry.tables.set(tableName, `组件${addonName}`);
|
|
108
|
-
}
|
|
109
|
-
} catch (error: any) {
|
|
110
|
-
// 表定义解析错误会在 table.ts 中处理,这里跳过
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// 收集 addon API 路由
|
|
116
|
-
if (addonDirExists(addonName, 'apis')) {
|
|
117
|
-
const addonApisDir = getAddonDir(addonName, 'apis');
|
|
118
|
-
const glob = new Bun.Glob('**/*.ts');
|
|
119
|
-
|
|
120
|
-
for await (const file of glob.scan({
|
|
121
|
-
cwd: addonApisDir,
|
|
122
|
-
onlyFiles: true,
|
|
123
|
-
absolute: true
|
|
124
|
-
})) {
|
|
125
|
-
const apiPath = relative(addonApisDir, file).replace(/\.ts$/, '');
|
|
126
|
-
if (apiPath.indexOf('_') !== -1) continue;
|
|
127
|
-
|
|
128
|
-
try {
|
|
129
|
-
const api = (await import(file)).default;
|
|
130
|
-
const route = `${(api.method || 'POST').toUpperCase()}/api/${addonName}/${apiPath}`;
|
|
131
|
-
|
|
132
|
-
// 检查是否使用保留路由前缀 /api
|
|
133
|
-
if (route.includes('/api/api/') || route.includes('/api/api')) {
|
|
134
|
-
conflicts.push(`组件 [${addonName}] 路由 "${route}" 使用了保留路径前缀 "/api"`);
|
|
135
|
-
continue;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// 检查是否与已有路由冲突
|
|
139
|
-
if (registry.routes.has(route)) {
|
|
140
|
-
conflicts.push(`组件 [${addonName}] 路由 "${route}" 与 ${registry.routes.get(route)} 冲突`);
|
|
141
|
-
} else {
|
|
142
|
-
registry.routes.set(route, `组件${addonName}`);
|
|
143
|
-
}
|
|
144
|
-
} catch (error: any) {
|
|
145
|
-
// API 加载错误会在 loader.ts 中处理,这里跳过
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
// 收集 addon 插件
|
|
151
|
-
if (addonDirExists(addonName, 'plugins')) {
|
|
152
|
-
const addonPluginsDir = getAddonDir(addonName, 'plugins');
|
|
153
|
-
const glob = new Bun.Glob('*.ts');
|
|
154
|
-
|
|
155
|
-
for await (const file of glob.scan({
|
|
156
|
-
cwd: addonPluginsDir,
|
|
157
|
-
onlyFiles: true,
|
|
158
|
-
absolute: true
|
|
159
|
-
})) {
|
|
160
|
-
const fileName = basename(file).replace(/\.ts$/, '');
|
|
161
|
-
if (fileName.startsWith('_')) continue;
|
|
162
|
-
|
|
163
|
-
// Addon 插件使用点号命名空间
|
|
164
|
-
const pluginName = `${addonName}.${fileName}`;
|
|
165
|
-
|
|
166
|
-
// 检查是否使用保留名称(检测核心插件名或点号前缀是保留名称)
|
|
167
|
-
const isReserved = RESERVED_NAMES.plugins.includes(pluginName) || (pluginName.includes('.') && RESERVED_NAMES.plugins.includes(pluginName.split('.')[0]));
|
|
168
|
-
if (isReserved) {
|
|
169
|
-
conflicts.push(`组件 ${addonName} 插件 "${pluginName}" 使用了保留名称,保留名称包括: ${RESERVED_NAMES.plugins.join(', ')}`);
|
|
170
|
-
continue;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
// 检查是否与已有插件冲突
|
|
174
|
-
if (registry.plugins.has(pluginName)) {
|
|
175
|
-
conflicts.push(`组件 [${addonName}] 插件 "${pluginName}" 与 ${registry.plugins.get(pluginName)} 冲突`);
|
|
176
|
-
} else {
|
|
177
|
-
registry.plugins.set(pluginName, `组件${addonName}`);
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
return conflicts;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
/**
|
|
186
|
-
* 收集用户资源
|
|
187
|
-
*/
|
|
188
|
-
async function collectUserResources(registry: ResourceRegistry): Promise<string[]> {
|
|
189
|
-
const conflicts: string[] = [];
|
|
190
|
-
|
|
191
|
-
// 收集用户表定义
|
|
192
|
-
const userTablesDir = projectTableDir;
|
|
193
|
-
if (existsSync(userTablesDir)) {
|
|
194
|
-
try {
|
|
195
|
-
const glob = new Bun.Glob('*.json');
|
|
196
|
-
for await (const file of glob.scan({
|
|
197
|
-
cwd: userTablesDir,
|
|
198
|
-
onlyFiles: true,
|
|
199
|
-
absolute: true
|
|
200
|
-
})) {
|
|
201
|
-
const fileName = basename(file, '.json');
|
|
202
|
-
if (fileName.startsWith('_')) continue;
|
|
203
|
-
|
|
204
|
-
try {
|
|
205
|
-
const tableDefine = await Bun.file(file).json();
|
|
206
|
-
const tableName = tableDefine.tableName || fileName;
|
|
207
|
-
|
|
208
|
-
// 检查是否使用保留前缀
|
|
209
|
-
if (RESERVED_NAMES.tablePrefix.some((prefix) => tableName.startsWith(prefix))) {
|
|
210
|
-
conflicts.push(`用户表 "${tableName}" 使用了保留前缀,保留前缀包括: ${RESERVED_NAMES.tablePrefix.join(', ')}`);
|
|
211
|
-
continue;
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
// 检查是否与已有表冲突
|
|
215
|
-
if (registry.tables.has(tableName)) {
|
|
216
|
-
conflicts.push(`用户表 "${tableName}" 与 ${registry.tables.get(tableName)} 冲突`);
|
|
217
|
-
} else {
|
|
218
|
-
registry.tables.set(tableName, 'user');
|
|
219
|
-
}
|
|
220
|
-
} catch (error: any) {
|
|
221
|
-
// 表定义解析错误会在 table.ts 中处理,这里跳过
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
} catch (error: any) {
|
|
225
|
-
Logger.error('收集用户表定义时出错:', error);
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
// 收集用户 API 路由
|
|
230
|
-
const userApisDir = projectApiDir;
|
|
231
|
-
if (existsSync(userApisDir)) {
|
|
232
|
-
try {
|
|
233
|
-
const glob = new Bun.Glob('**/*.ts');
|
|
234
|
-
for await (const file of glob.scan({
|
|
235
|
-
cwd: userApisDir,
|
|
236
|
-
onlyFiles: true,
|
|
237
|
-
absolute: true
|
|
238
|
-
})) {
|
|
239
|
-
const apiPath = relative(userApisDir, file).replace(/\.ts$/, '');
|
|
240
|
-
if (apiPath.indexOf('_') !== -1) continue;
|
|
241
|
-
|
|
242
|
-
try {
|
|
243
|
-
const api = (await import(file)).default;
|
|
244
|
-
const route = `${(api.method || 'POST').toUpperCase()}/api/${apiPath}`;
|
|
245
|
-
|
|
246
|
-
// 检查是否使用保留路由前缀 /api
|
|
247
|
-
if (apiPath.startsWith('api/') || apiPath === 'api') {
|
|
248
|
-
conflicts.push(`用户路由 "${route}" 使用了保留路径前缀 "/api"`);
|
|
249
|
-
continue;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
// 检查是否与已有路由冲突
|
|
253
|
-
if (registry.routes.has(route)) {
|
|
254
|
-
conflicts.push(`用户路由 "${route}" 与 ${registry.routes.get(route)} 冲突`);
|
|
255
|
-
} else {
|
|
256
|
-
registry.routes.set(route, 'user');
|
|
257
|
-
}
|
|
258
|
-
} catch (error: any) {
|
|
259
|
-
// API 加载错误会在 loader.ts 中处理,这里跳过
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
} catch (error: any) {
|
|
263
|
-
Logger.error('收集用户 API 路由时出错:', error);
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
// 收集用户插件
|
|
268
|
-
const userPluginsDir = projectPluginDir;
|
|
269
|
-
if (existsSync(userPluginsDir)) {
|
|
270
|
-
try {
|
|
271
|
-
const glob = new Bun.Glob('*.ts');
|
|
272
|
-
for await (const file of glob.scan({
|
|
273
|
-
cwd: userPluginsDir,
|
|
274
|
-
onlyFiles: true,
|
|
275
|
-
absolute: true
|
|
276
|
-
})) {
|
|
277
|
-
const pluginName = basename(file).replace(/\.ts$/, '');
|
|
278
|
-
if (pluginName.startsWith('_')) continue;
|
|
279
|
-
|
|
280
|
-
// 检查是否使用保留名称(检测核心插件名或点号前缀是保留名称)
|
|
281
|
-
const isReserved = RESERVED_NAMES.plugins.includes(pluginName) || (pluginName.includes('.') && RESERVED_NAMES.plugins.includes(pluginName.split('.')[0]));
|
|
282
|
-
if (isReserved) {
|
|
283
|
-
conflicts.push(`用户插件 "${pluginName}" 使用了保留名称,保留名称包括: ${RESERVED_NAMES.plugins.join(', ')}`);
|
|
284
|
-
continue;
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
// 检查是否与已有插件冲突
|
|
288
|
-
if (registry.plugins.has(pluginName)) {
|
|
289
|
-
conflicts.push(`用户插件 "${pluginName}" 与 ${registry.plugins.get(pluginName)} 冲突`);
|
|
290
|
-
} else {
|
|
291
|
-
registry.plugins.set(pluginName, 'user');
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
} catch (error: any) {
|
|
295
|
-
Logger.error('收集用户插件时出错:', error);
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
return conflicts;
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
/**
|
|
303
|
-
* 执行资源冲突检测
|
|
304
|
-
*/
|
|
305
|
-
export default async function checkConflict(): Promise<boolean> {
|
|
306
|
-
try {
|
|
307
|
-
// 初始化资源注册表
|
|
308
|
-
const registry: ResourceRegistry = {
|
|
309
|
-
tables: new Map(),
|
|
310
|
-
routes: new Map(),
|
|
311
|
-
plugins: new Map()
|
|
312
|
-
};
|
|
313
|
-
|
|
314
|
-
const allConflicts: string[] = [];
|
|
315
|
-
|
|
316
|
-
// 1. 收集核心插件
|
|
317
|
-
await collectCorePlugins(registry);
|
|
318
|
-
|
|
319
|
-
// 2. 收集 addon 资源
|
|
320
|
-
const addons = scanAddons();
|
|
321
|
-
for (const addon of addons) {
|
|
322
|
-
const addonConflicts = await collectAddonResources(addon, registry);
|
|
323
|
-
allConflicts.push(...addonConflicts);
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
// 3. 收集用户资源
|
|
327
|
-
const userConflicts = await collectUserResources(registry);
|
|
328
|
-
allConflicts.push(...userConflicts);
|
|
329
|
-
|
|
330
|
-
// 4. 报告冲突
|
|
331
|
-
if (allConflicts.length > 0) {
|
|
332
|
-
Logger.warn('检测到资源冲突:');
|
|
333
|
-
allConflicts.forEach((conflict, index) => {
|
|
334
|
-
Logger.warn(` ${index + 1}. ${conflict}`);
|
|
335
|
-
});
|
|
336
|
-
Logger.warn('请解决以上冲突后再启动服务器');
|
|
337
|
-
return false;
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
// 5. 统计信息
|
|
341
|
-
Logger.info(`资源冲突检测通过 ✓`);
|
|
342
|
-
Logger.info(` - 表: ${registry.tables.size} 个`);
|
|
343
|
-
Logger.info(` - 路由: ${registry.routes.size} 个`);
|
|
344
|
-
Logger.info(` - 插件: ${registry.plugins.size} 个`);
|
|
345
|
-
|
|
346
|
-
return true;
|
|
347
|
-
} catch (error: any) {
|
|
348
|
-
Logger.error('资源冲突检测时发生错误:', error);
|
|
349
|
-
return false;
|
|
350
|
-
}
|
|
351
|
-
}
|
package/commands/index.ts
DELETED
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Sync 命令实现
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { join } from 'pathe';
|
|
6
|
-
import { existsSync } from 'node:fs';
|
|
7
|
-
import { Logger } from '../lib/logger.js';
|
|
8
|
-
import { getProjectRoot } from './util.js';
|
|
9
|
-
|
|
10
|
-
// ========== Sync 命令 ==========
|
|
11
|
-
interface SyncOptions {
|
|
12
|
-
table?: string;
|
|
13
|
-
force: boolean;
|
|
14
|
-
dryRun: boolean;
|
|
15
|
-
drop: boolean;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export async function syncCommand(options: SyncOptions) {
|
|
19
|
-
try {
|
|
20
|
-
const projectRoot = getProjectRoot();
|
|
21
|
-
const syncScript = join(projectRoot, 'node_modules', 'befly', 'scripts', 'syncTable.ts');
|
|
22
|
-
|
|
23
|
-
if (!existsSync(syncScript)) {
|
|
24
|
-
Logger.error('未找到同步脚本,请确保已安装 befly');
|
|
25
|
-
process.exit(1);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
Logger.info('正在同步数据库表...');
|
|
29
|
-
|
|
30
|
-
const args = ['run', syncScript];
|
|
31
|
-
|
|
32
|
-
if (options.table) {
|
|
33
|
-
args.push('--table', options.table);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
if (options.force) {
|
|
37
|
-
args.push('--force');
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
if (options.dryRun) {
|
|
41
|
-
args.push('--dry-run');
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
if (options.drop) {
|
|
45
|
-
args.push('--drop');
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
const proc = Bun.spawn(['bun', ...args], {
|
|
49
|
-
cwd: projectRoot,
|
|
50
|
-
stdout: 'inherit',
|
|
51
|
-
stderr: 'inherit',
|
|
52
|
-
stdin: 'inherit'
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
await proc.exited;
|
|
56
|
-
|
|
57
|
-
if (proc.exitCode === 0) {
|
|
58
|
-
Logger.success('数据库同步完成');
|
|
59
|
-
} else {
|
|
60
|
-
Logger.error('数据库同步失败');
|
|
61
|
-
process.exit(1);
|
|
62
|
-
}
|
|
63
|
-
} catch (error) {
|
|
64
|
-
Logger.error('同步失败:');
|
|
65
|
-
console.error(error);
|
|
66
|
-
process.exit(1);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// ========== 导出同步命令 ==========
|
|
71
|
-
export { syncApiCommand } from './syncApi.js';
|
|
72
|
-
export { syncMenuCommand } from './syncMenu.js';
|
|
73
|
-
export { syncDevCommand } from './syncDev.js';
|
package/commands/sync.ts
DELETED
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Sync 命令 - 一次性执行所有同步操作
|
|
3
|
-
* 按顺序执行:syncDb → syncApi → syncMenu → syncDev
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { Logger } from '../lib/logger.js';
|
|
7
|
-
import { Env } from '../env.js';
|
|
8
|
-
import { syncDbCommand, type SyncDbStats } from './syncDb.js';
|
|
9
|
-
import { syncApiCommand, type SyncApiStats } from './syncApi.js';
|
|
10
|
-
import { syncMenuCommand, type SyncMenuStats } from './syncMenu.js';
|
|
11
|
-
import { syncDevCommand, type SyncDevStats } from './syncDev.js';
|
|
12
|
-
import { existsSync, mkdirSync } from 'node:fs';
|
|
13
|
-
|
|
14
|
-
interface SyncOptions {}
|
|
15
|
-
|
|
16
|
-
export async function syncCommand(options: SyncOptions = {}) {
|
|
17
|
-
try {
|
|
18
|
-
const startTime = Date.now();
|
|
19
|
-
|
|
20
|
-
// 确保 logs 目录存在
|
|
21
|
-
if (!existsSync('./logs')) {
|
|
22
|
-
mkdirSync('./logs', { recursive: true });
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
// 1. 同步数据库表结构
|
|
26
|
-
const dbStats = await syncDbCommand({ dryRun: false });
|
|
27
|
-
|
|
28
|
-
// 2. 同步接口(并缓存)
|
|
29
|
-
const apiStats = await syncApiCommand();
|
|
30
|
-
|
|
31
|
-
// 3. 同步菜单(并缓存)
|
|
32
|
-
const menuStats = await syncMenuCommand();
|
|
33
|
-
|
|
34
|
-
// 4. 同步开发管理员(并缓存角色权限)
|
|
35
|
-
const devStats = await syncDevCommand();
|
|
36
|
-
|
|
37
|
-
// 输出总结
|
|
38
|
-
const totalTime = ((Date.now() - startTime) / 1000).toFixed(2);
|
|
39
|
-
Logger.info(`总耗时: ${totalTime} 秒`);
|
|
40
|
-
|
|
41
|
-
console.log(
|
|
42
|
-
Bun.inspect.table([
|
|
43
|
-
{ 项目: '处理表数', 数量: dbStats.processedTables },
|
|
44
|
-
{ 项目: '创建表', 数量: dbStats.createdTables },
|
|
45
|
-
{ 项目: '修改表', 数量: dbStats.modifiedTables },
|
|
46
|
-
{ 项目: '新增字段', 数量: dbStats.addFields },
|
|
47
|
-
{ 项目: '字段名称变更', 数量: dbStats.nameChanges },
|
|
48
|
-
{ 项目: '字段类型变更', 数量: dbStats.typeChanges },
|
|
49
|
-
{ 项目: '索引新增', 数量: dbStats.indexCreate },
|
|
50
|
-
{ 项目: '索引删除', 数量: dbStats.indexDrop }
|
|
51
|
-
])
|
|
52
|
-
);
|
|
53
|
-
|
|
54
|
-
Logger.info('\n📊 接口同步统计');
|
|
55
|
-
console.log(
|
|
56
|
-
Bun.inspect.table([
|
|
57
|
-
{ 项目: '总接口数', 数量: apiStats.totalApis },
|
|
58
|
-
{ 项目: '新增接口', 数量: apiStats.created },
|
|
59
|
-
{ 项目: '更新接口', 数量: apiStats.updated },
|
|
60
|
-
{ 项目: '删除接口', 数量: apiStats.deleted }
|
|
61
|
-
])
|
|
62
|
-
);
|
|
63
|
-
|
|
64
|
-
Logger.info('\n📊 菜单同步统计');
|
|
65
|
-
console.log(
|
|
66
|
-
Bun.inspect.table([
|
|
67
|
-
{ 项目: '总菜单数', 数量: menuStats.totalMenus },
|
|
68
|
-
{ 项目: '父级菜单', 数量: menuStats.parentMenus },
|
|
69
|
-
{ 项目: '子级菜单', 数量: menuStats.childMenus },
|
|
70
|
-
{ 项目: '新增菜单', 数量: menuStats.created },
|
|
71
|
-
{ 项目: '更新菜单', 数量: menuStats.updated },
|
|
72
|
-
{ 项目: '删除菜单', 数量: menuStats.deleted }
|
|
73
|
-
])
|
|
74
|
-
);
|
|
75
|
-
|
|
76
|
-
Logger.info('\n📊 开发账号同步统计');
|
|
77
|
-
console.log(
|
|
78
|
-
Bun.inspect.table([
|
|
79
|
-
{ 项目: '管理员数量', 数量: devStats.adminCount },
|
|
80
|
-
{ 项目: '角色数量', 数量: devStats.roleCount },
|
|
81
|
-
{ 项目: '缓存角色数', 数量: devStats.cachedRoles }
|
|
82
|
-
])
|
|
83
|
-
);
|
|
84
|
-
} catch (error: any) {
|
|
85
|
-
Logger.error('同步过程中发生错误:', error);
|
|
86
|
-
process.exit(1);
|
|
87
|
-
}
|
|
88
|
-
}
|