befly 3.2.1 → 3.3.1
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/bin/index.ts +138 -0
- package/checks/conflict.ts +35 -25
- package/checks/table.ts +6 -6
- package/commands/addon.ts +57 -0
- package/commands/build.ts +74 -0
- package/commands/dev.ts +94 -0
- package/commands/index.ts +252 -0
- package/commands/script.ts +303 -0
- package/commands/start.ts +80 -0
- package/commands/syncApi.ts +327 -0
- package/{scripts → commands}/syncDb/apply.ts +2 -2
- package/{scripts → commands}/syncDb/constants.ts +13 -7
- package/{scripts → commands}/syncDb/ddl.ts +7 -5
- package/{scripts → commands}/syncDb/helpers.ts +18 -18
- package/{scripts → commands}/syncDb/index.ts +37 -23
- package/{scripts → commands}/syncDb/sqlite.ts +1 -1
- package/{scripts → commands}/syncDb/state.ts +10 -4
- package/{scripts → commands}/syncDb/table.ts +7 -7
- package/{scripts → commands}/syncDb/tableCreate.ts +7 -6
- package/{scripts → commands}/syncDb/types.ts +5 -5
- package/{scripts → commands}/syncDb/version.ts +1 -1
- package/commands/syncDb.ts +35 -0
- package/commands/syncDev.ts +174 -0
- package/commands/syncMenu.ts +368 -0
- package/config/env.ts +4 -4
- package/config/menu.json +67 -0
- package/{utils/crypto.ts → lib/cipher.ts} +16 -67
- package/lib/database.ts +296 -0
- package/{utils → lib}/dbHelper.ts +102 -56
- package/{utils → lib}/jwt.ts +124 -151
- package/{utils → lib}/logger.ts +47 -24
- package/lib/middleware.ts +271 -0
- package/{utils → lib}/redisHelper.ts +4 -4
- package/{utils/validate.ts → lib/validator.ts} +101 -78
- package/lifecycle/bootstrap.ts +63 -0
- package/lifecycle/checker.ts +165 -0
- package/lifecycle/cluster.ts +241 -0
- package/lifecycle/lifecycle.ts +139 -0
- package/lifecycle/loader.ts +513 -0
- package/main.ts +14 -12
- package/package.json +21 -9
- package/paths.ts +34 -0
- package/plugins/cache.ts +187 -0
- package/plugins/db.ts +4 -4
- package/plugins/logger.ts +1 -1
- package/plugins/redis.ts +4 -4
- package/router/api.ts +155 -0
- package/router/root.ts +53 -0
- package/router/static.ts +76 -0
- package/types/api.d.ts +0 -36
- package/types/befly.d.ts +8 -6
- package/types/common.d.ts +1 -1
- package/types/context.d.ts +3 -3
- package/types/util.d.ts +45 -0
- package/util.ts +299 -0
- package/config/fields.ts +0 -55
- package/config/regexAliases.ts +0 -51
- package/config/reserved.ts +0 -96
- package/scripts/syncDb/tests/constants.test.ts +0 -105
- package/scripts/syncDb/tests/ddl.test.ts +0 -134
- package/scripts/syncDb/tests/helpers.test.ts +0 -70
- package/scripts/syncDb.ts +0 -10
- package/types/index.d.ts +0 -450
- package/types/index.ts +0 -438
- package/types/validator.ts +0 -43
- package/utils/colors.ts +0 -221
- package/utils/database.ts +0 -348
- package/utils/helper.ts +0 -812
- package/utils/index.ts +0 -33
- package/utils/requestContext.ts +0 -167
- /package/{scripts → commands}/syncDb/schema.ts +0 -0
- /package/{utils → lib}/sqlBuilder.ts +0 -0
- /package/{utils → lib}/xml.ts +0 -0
package/utils/database.ts
DELETED
|
@@ -1,348 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 数据库统一管理工具
|
|
3
|
-
* 提供 Redis 和 SQL 连接的统一初始化和管理
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { SQL, RedisClient } from 'bun';
|
|
7
|
-
import { Env } from '../config/env.js';
|
|
8
|
-
import { Logger } from './logger.js';
|
|
9
|
-
import { DbHelper } from './dbHelper.js';
|
|
10
|
-
import { RedisHelper } from './redisHelper.js';
|
|
11
|
-
import type { BeflyContext } from '../types/befly.js';
|
|
12
|
-
import type { SqlClientOptions } from '../types/database.js';
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* 数据库连接实例
|
|
16
|
-
*/
|
|
17
|
-
interface DatabaseConnections {
|
|
18
|
-
redis: RedisClient | null;
|
|
19
|
-
sql: any;
|
|
20
|
-
helper: DbHelper | null;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* 全局连接实例
|
|
25
|
-
*/
|
|
26
|
-
const connections: DatabaseConnections = {
|
|
27
|
-
redis: null,
|
|
28
|
-
sql: null,
|
|
29
|
-
helper: null
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* 构建 Redis 连接 URL
|
|
34
|
-
* @returns Redis 连接 URL
|
|
35
|
-
*/
|
|
36
|
-
export function buildRedisUrl(): string {
|
|
37
|
-
const { REDIS_HOST, REDIS_PORT, REDIS_USERNAME, REDIS_PASSWORD, REDIS_DB } = Env;
|
|
38
|
-
|
|
39
|
-
// 构建认证部分
|
|
40
|
-
let auth = '';
|
|
41
|
-
if (REDIS_USERNAME && REDIS_PASSWORD) {
|
|
42
|
-
auth = `${REDIS_USERNAME}:${REDIS_PASSWORD}@`;
|
|
43
|
-
} else if (REDIS_PASSWORD) {
|
|
44
|
-
auth = `:${REDIS_PASSWORD}@`;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// 构建完整 URL
|
|
48
|
-
const url = `redis://${auth}${REDIS_HOST}:${REDIS_PORT}/${REDIS_DB}`;
|
|
49
|
-
|
|
50
|
-
return url;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* 构建数据库连接字符串
|
|
55
|
-
* 根据环境变量自动构建 SQLite、PostgreSQL 或 MySQL 的连接 URL
|
|
56
|
-
* @returns 数据库连接字符串
|
|
57
|
-
* @throws 如果配置不完整或数据库类型不支持
|
|
58
|
-
*/
|
|
59
|
-
export function buildDatabaseUrl(): string {
|
|
60
|
-
const type = Env.DB_TYPE || '';
|
|
61
|
-
const host = Env.DB_HOST || '';
|
|
62
|
-
const port = Env.DB_PORT;
|
|
63
|
-
const user = encodeURIComponent(Env.DB_USER || '');
|
|
64
|
-
const pass = encodeURIComponent(Env.DB_PASS || '');
|
|
65
|
-
const name = Env.DB_NAME || '';
|
|
66
|
-
|
|
67
|
-
if (!type) throw new Error('DB_TYPE 未配置');
|
|
68
|
-
if (!name && type !== 'sqlite') throw new Error('DB_NAME 未配置');
|
|
69
|
-
|
|
70
|
-
if (type === 'sqlite') {
|
|
71
|
-
// 支持内存数据库
|
|
72
|
-
if (!name || name === ':memory:') {
|
|
73
|
-
return 'sqlite://:memory:';
|
|
74
|
-
}
|
|
75
|
-
// 支持绝对路径(以 / 或盘符开头,如 /path 或 C:\path)
|
|
76
|
-
if (name.startsWith('/') || /^[a-zA-Z]:/.test(name)) {
|
|
77
|
-
return `sqlite://${name}`;
|
|
78
|
-
}
|
|
79
|
-
// 相对路径和普通文件名
|
|
80
|
-
return `sqlite://${name}`;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
if (type === 'postgresql' || type === 'postgres') {
|
|
84
|
-
if (!host || !port) throw new Error('DB_HOST/DB_PORT 未配置');
|
|
85
|
-
const auth = user || pass ? `${user}:${pass}@` : '';
|
|
86
|
-
return `postgres://${auth}${host}:${port}/${encodeURIComponent(name)}`;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
if (type === 'mysql') {
|
|
90
|
-
if (!host || !port) throw new Error('DB_HOST/DB_PORT 未配置');
|
|
91
|
-
const auth = user || pass ? `${user}:${pass}@` : '';
|
|
92
|
-
return `mysql://${auth}${host}:${port}/${encodeURIComponent(name)}`;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
throw new Error(`不支持的 DB_TYPE: ${type}`);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* 创建 Redis 客户端
|
|
100
|
-
* @returns Redis 客户端实例
|
|
101
|
-
* @throws 如果连接失败
|
|
102
|
-
*/
|
|
103
|
-
export async function createRedisClient(): Promise<RedisClient> {
|
|
104
|
-
try {
|
|
105
|
-
const url = buildRedisUrl();
|
|
106
|
-
const redis = new RedisClient(url, {
|
|
107
|
-
// 连接超时(毫秒)
|
|
108
|
-
connectionTimeout: 10000,
|
|
109
|
-
// 空闲超时设为 0,表示永不超时
|
|
110
|
-
idleTimeout: 0,
|
|
111
|
-
// 断开连接时自动重连
|
|
112
|
-
autoReconnect: true,
|
|
113
|
-
// 最大重连次数,0 表示无限重连
|
|
114
|
-
maxRetries: 0,
|
|
115
|
-
// 断开连接时缓存命令
|
|
116
|
-
enableOfflineQueue: true,
|
|
117
|
-
// 自动管道化命令
|
|
118
|
-
enableAutoPipelining: true
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
// 测试连接是否成功
|
|
122
|
-
await redis.ping();
|
|
123
|
-
Logger.info('Redis 连接成功');
|
|
124
|
-
|
|
125
|
-
return redis;
|
|
126
|
-
} catch (error: any) {
|
|
127
|
-
Logger.error('Redis 连接失败', error);
|
|
128
|
-
throw new Error(`Redis 连接失败: ${error.message}`);
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* 创建 SQL 客户端
|
|
134
|
-
* @param options SQL 客户端选项
|
|
135
|
-
* @returns SQL 客户端实例
|
|
136
|
-
* @throws 如果连接失败
|
|
137
|
-
*/
|
|
138
|
-
export async function createSqlClient(options: SqlClientOptions = {}): Promise<any> {
|
|
139
|
-
const finalUrl = buildDatabaseUrl();
|
|
140
|
-
let sql: any = null;
|
|
141
|
-
|
|
142
|
-
if (Env.DB_TYPE === 'sqlite') {
|
|
143
|
-
sql = new SQL(finalUrl);
|
|
144
|
-
} else {
|
|
145
|
-
sql = new SQL({
|
|
146
|
-
url: finalUrl,
|
|
147
|
-
max: options.max ?? 1,
|
|
148
|
-
bigint: false,
|
|
149
|
-
...options
|
|
150
|
-
});
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
try {
|
|
154
|
-
// 连接健康检查 - 添加超时机制
|
|
155
|
-
const timeout = options.connectionTimeout ?? 5000; // 默认5秒超时
|
|
156
|
-
|
|
157
|
-
const healthCheckPromise = (async () => {
|
|
158
|
-
let version = '';
|
|
159
|
-
if (Env.DB_TYPE === 'sqlite') {
|
|
160
|
-
const v = await sql`SELECT sqlite_version() AS version`;
|
|
161
|
-
version = v?.[0]?.version;
|
|
162
|
-
} else if (Env.DB_TYPE === 'postgresql' || Env.DB_TYPE === 'postgres') {
|
|
163
|
-
const v = await sql`SELECT version() AS version`;
|
|
164
|
-
version = v?.[0]?.version;
|
|
165
|
-
} else {
|
|
166
|
-
const v = await sql`SELECT VERSION() AS version`;
|
|
167
|
-
version = v?.[0]?.version;
|
|
168
|
-
}
|
|
169
|
-
return version;
|
|
170
|
-
})();
|
|
171
|
-
|
|
172
|
-
// 创建超时 Promise
|
|
173
|
-
const timeoutPromise = new Promise<never>((_, reject) => {
|
|
174
|
-
setTimeout(() => {
|
|
175
|
-
reject(new Error(`数据库连接超时 (${timeout}ms)`));
|
|
176
|
-
}, timeout);
|
|
177
|
-
});
|
|
178
|
-
|
|
179
|
-
// 使用 Promise.race 实现超时控制
|
|
180
|
-
const version = await Promise.race([healthCheckPromise, timeoutPromise]);
|
|
181
|
-
|
|
182
|
-
Logger.info(`数据库连接成功,version: ${version}`);
|
|
183
|
-
return sql;
|
|
184
|
-
} catch (error: any) {
|
|
185
|
-
Logger.error('数据库连接测试失败', error);
|
|
186
|
-
|
|
187
|
-
// 清理资源
|
|
188
|
-
try {
|
|
189
|
-
await sql.close();
|
|
190
|
-
} catch (cleanupError) {
|
|
191
|
-
// 忽略清理错误
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
throw error;
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
/**
|
|
199
|
-
* 初始化数据库连接(Redis + SQL)
|
|
200
|
-
* @param options SQL 客户端选项
|
|
201
|
-
* @returns 数据库连接实例
|
|
202
|
-
*/
|
|
203
|
-
export async function initDatabase(options: SqlClientOptions = {}): Promise<DatabaseConnections> {
|
|
204
|
-
try {
|
|
205
|
-
// 1. 初始化 Redis
|
|
206
|
-
Logger.info('正在初始化 Redis 连接...');
|
|
207
|
-
connections.redis = await createRedisClient();
|
|
208
|
-
|
|
209
|
-
// 2. 初始化 SQL
|
|
210
|
-
Logger.info('正在初始化 SQL 连接...');
|
|
211
|
-
connections.sql = await createSqlClient(options);
|
|
212
|
-
|
|
213
|
-
// 3. 创建 DbHelper 实例
|
|
214
|
-
const befly: BeflyContext = {
|
|
215
|
-
redis: RedisHelper, // 使用 RedisHelper 对象而不是 RedisClient
|
|
216
|
-
db: null as any,
|
|
217
|
-
tool: null as any,
|
|
218
|
-
logger: null as any
|
|
219
|
-
};
|
|
220
|
-
connections.helper = new DbHelper(befly, connections.sql);
|
|
221
|
-
|
|
222
|
-
Logger.info('数据库连接初始化完成(Redis + SQL + DbHelper)');
|
|
223
|
-
|
|
224
|
-
return {
|
|
225
|
-
redis: connections.redis,
|
|
226
|
-
sql: connections.sql,
|
|
227
|
-
helper: connections.helper
|
|
228
|
-
};
|
|
229
|
-
} catch (error: any) {
|
|
230
|
-
Logger.error('数据库初始化失败', error);
|
|
231
|
-
// 清理已创建的连接
|
|
232
|
-
await closeDatabase();
|
|
233
|
-
throw error;
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
/**
|
|
238
|
-
* 关闭所有数据库连接
|
|
239
|
-
*/
|
|
240
|
-
export async function closeDatabase(): Promise<void> {
|
|
241
|
-
try {
|
|
242
|
-
// 关闭 SQL 连接
|
|
243
|
-
if (connections.sql) {
|
|
244
|
-
try {
|
|
245
|
-
await connections.sql.close();
|
|
246
|
-
Logger.info('SQL 连接已关闭');
|
|
247
|
-
} catch (error: any) {
|
|
248
|
-
Logger.warn('关闭 SQL 连接时出错:', error.message);
|
|
249
|
-
}
|
|
250
|
-
connections.sql = null;
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
// 关闭 Redis 连接
|
|
254
|
-
if (connections.redis) {
|
|
255
|
-
try {
|
|
256
|
-
connections.redis.close();
|
|
257
|
-
Logger.info('Redis 连接已关闭');
|
|
258
|
-
} catch (error: any) {
|
|
259
|
-
Logger.warn('关闭 Redis 连接时出错:', error);
|
|
260
|
-
}
|
|
261
|
-
connections.redis = null;
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
// 清理 DbHelper
|
|
265
|
-
connections.helper = null;
|
|
266
|
-
} catch (error: any) {
|
|
267
|
-
Logger.error('关闭数据库连接时出错', error);
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
/**
|
|
272
|
-
* 获取 Redis 客户端
|
|
273
|
-
* @returns Redis 客户端实例
|
|
274
|
-
*/
|
|
275
|
-
export function getRedis(): RedisClient | null {
|
|
276
|
-
return connections.redis;
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
/**
|
|
280
|
-
* 获取 SQL 客户端
|
|
281
|
-
* @returns SQL 客户端实例
|
|
282
|
-
*/
|
|
283
|
-
export function getSql(): any {
|
|
284
|
-
return connections.sql;
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
/**
|
|
288
|
-
* 获取 DbHelper 实例
|
|
289
|
-
* @returns DbHelper 实例
|
|
290
|
-
*/
|
|
291
|
-
export function getDbHelper(): DbHelper | null {
|
|
292
|
-
return connections.helper;
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
/**
|
|
296
|
-
* 检查数据库连接是否已初始化
|
|
297
|
-
* @returns 是否已初始化
|
|
298
|
-
*/
|
|
299
|
-
export function isDatabaseInitialized(): boolean {
|
|
300
|
-
return connections.redis !== null && connections.sql !== null && connections.helper !== null;
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
/**
|
|
304
|
-
* 仅初始化 SQL 连接(不需要 Redis 时使用)
|
|
305
|
-
* @param options SQL 客户端选项
|
|
306
|
-
* @returns SQL 客户端和 DbHelper 实例
|
|
307
|
-
*/
|
|
308
|
-
export async function initSqlOnly(options: SqlClientOptions = {}): Promise<{ sql: any; helper: DbHelper }> {
|
|
309
|
-
try {
|
|
310
|
-
Logger.info('正在初始化 SQL 连接(不含 Redis)...');
|
|
311
|
-
connections.sql = await createSqlClient(options);
|
|
312
|
-
|
|
313
|
-
// 创建最小化 befly 上下文(不含 redis)
|
|
314
|
-
const befly: BeflyContext = {
|
|
315
|
-
redis: null as any,
|
|
316
|
-
db: null as any,
|
|
317
|
-
tool: null as any,
|
|
318
|
-
logger: null as any
|
|
319
|
-
};
|
|
320
|
-
connections.helper = new DbHelper(befly, connections.sql);
|
|
321
|
-
|
|
322
|
-
Logger.info('SQL 连接初始化完成');
|
|
323
|
-
|
|
324
|
-
return {
|
|
325
|
-
sql: connections.sql,
|
|
326
|
-
helper: connections.helper
|
|
327
|
-
};
|
|
328
|
-
} catch (error: any) {
|
|
329
|
-
Logger.error('SQL 初始化失败', error);
|
|
330
|
-
throw error;
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
/**
|
|
335
|
-
* 仅初始化 Redis 连接(不需要 SQL 时使用)
|
|
336
|
-
* @returns Redis 客户端实例
|
|
337
|
-
*/
|
|
338
|
-
export async function initRedisOnly(): Promise<RedisClient> {
|
|
339
|
-
try {
|
|
340
|
-
Logger.info('正在初始化 Redis 连接(不含 SQL)...');
|
|
341
|
-
connections.redis = await createRedisClient();
|
|
342
|
-
Logger.info('Redis 连接初始化完成');
|
|
343
|
-
return connections.redis;
|
|
344
|
-
} catch (error: any) {
|
|
345
|
-
Logger.error('Redis 初始化失败', error);
|
|
346
|
-
throw error;
|
|
347
|
-
}
|
|
348
|
-
}
|