befly 3.0.0 → 3.0.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/checks/conflict.ts +35 -114
- package/checks/table.ts +31 -63
- package/config/env.ts +3 -3
- package/config/fields.ts +55 -0
- package/config/regexAliases.ts +51 -0
- package/config/reserved.ts +1 -1
- package/main.ts +17 -71
- package/package.json +7 -28
- package/plugins/db.ts +11 -10
- package/plugins/redis.ts +5 -9
- package/scripts/syncDb/apply.ts +3 -3
- package/scripts/syncDb/constants.ts +2 -1
- package/scripts/syncDb/ddl.ts +15 -8
- package/scripts/syncDb/helpers.ts +3 -2
- package/scripts/syncDb/index.ts +23 -35
- package/scripts/syncDb/state.ts +8 -6
- package/scripts/syncDb/table.ts +32 -22
- package/scripts/syncDb/tableCreate.ts +9 -3
- package/scripts/syncDb/tests/constants.test.ts +2 -1
- package/scripts/syncDb.ts +10 -9
- package/types/addon.d.ts +53 -0
- package/types/api.d.ts +17 -14
- package/types/befly.d.ts +2 -6
- package/types/context.d.ts +7 -0
- package/types/database.d.ts +9 -14
- package/types/index.d.ts +442 -8
- package/types/index.ts +35 -56
- package/types/redis.d.ts +2 -0
- package/types/validator.d.ts +0 -2
- package/types/validator.ts +43 -0
- package/utils/colors.ts +117 -37
- package/utils/database.ts +348 -0
- package/utils/dbHelper.ts +687 -116
- package/utils/helper.ts +812 -0
- package/utils/index.ts +10 -23
- package/utils/logger.ts +78 -171
- package/utils/redisHelper.ts +135 -152
- package/{types/context.ts → utils/requestContext.ts} +3 -3
- package/utils/sqlBuilder.ts +142 -165
- package/utils/validate.ts +51 -9
- package/apis/health/info.ts +0 -64
- package/apis/tool/tokenCheck.ts +0 -51
- package/bin/befly.ts +0 -202
- package/bunfig.toml +0 -3
- package/plugins/tool.ts +0 -34
- package/scripts/syncDev.ts +0 -112
- package/system.ts +0 -149
- package/tables/_common.json +0 -21
- package/tables/admin.json +0 -10
- package/utils/addonHelper.ts +0 -60
- package/utils/api.ts +0 -23
- package/utils/datetime.ts +0 -51
- package/utils/errorHandler.ts +0 -68
- package/utils/objectHelper.ts +0 -68
- package/utils/pluginHelper.ts +0 -62
- package/utils/response.ts +0 -38
- package/utils/sqlHelper.ts +0 -447
- package/utils/tableHelper.ts +0 -167
- package/utils/tool.ts +0 -230
- package/utils/typeHelper.ts +0 -101
package/utils/index.ts
CHANGED
|
@@ -5,42 +5,29 @@
|
|
|
5
5
|
* 保持向后兼容性,支持从 utils/index.js 导入所有工具函数
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
// ==========
|
|
9
|
-
export { Yes, No } from './
|
|
8
|
+
// ========== 通用辅助工具(helpers.ts)==========
|
|
9
|
+
export { Yes, No, isDebug, isType, isEmptyObject, isEmptyArray, pickFields, formatDate, calcPerfTime, cleanData, toSnakeCase, toCamelCase, keysToSnake, keysToCamel } from './helper.js';
|
|
10
|
+
export type { DataCleanOptions } from './helper.js';
|
|
10
11
|
|
|
11
|
-
// ==========
|
|
12
|
-
export {
|
|
13
|
-
|
|
14
|
-
// ========== 对象操作工具 ==========
|
|
15
|
-
export { pickFields, omitFields } from './objectHelper.js';
|
|
16
|
-
|
|
17
|
-
// ========== 日期时间工具 ==========
|
|
18
|
-
export { formatDate, calcPerfTime } from './datetime.js';
|
|
12
|
+
// ========== 框架工具 ==========
|
|
13
|
+
export { scanAddons, getAddonDir, addonDirExists, sortPlugins, parseRule } from './helper.js';
|
|
19
14
|
|
|
20
15
|
// ========== 数据库工具 ==========
|
|
21
|
-
export {
|
|
22
|
-
|
|
23
|
-
// ========== 表定义工具 ==========
|
|
24
|
-
export { parseRule, loadTableFields, pickTableFields } from './tableHelper.js';
|
|
25
|
-
|
|
26
|
-
// ========== 插件系统工具 ==========
|
|
27
|
-
export { sortPlugins } from './pluginHelper.js';
|
|
16
|
+
export { buildDatabaseUrl, createSqlClient, buildRedisUrl, createRedisClient, initDatabase, closeDatabase, initSqlOnly, initRedisOnly, getRedis, getSql, getDbHelper, isDatabaseInitialized } from './database.js';
|
|
28
17
|
|
|
29
|
-
//
|
|
18
|
+
// 导出其他大型模块
|
|
30
19
|
export { Colors } from './colors.js';
|
|
31
20
|
export { Logger } from './logger.js';
|
|
32
21
|
export { Validator } from './validate.js';
|
|
33
22
|
export { SqlBuilder } from './sqlBuilder.js';
|
|
34
|
-
export {
|
|
35
|
-
export { RedisHelper
|
|
23
|
+
export { DbHelper } from './dbHelper.js';
|
|
24
|
+
export { RedisHelper } from './redisHelper.js';
|
|
36
25
|
export { Jwt } from './jwt.js';
|
|
37
26
|
export { Crypto2 } from './crypto.js';
|
|
38
27
|
export { Xml } from './xml.js';
|
|
39
|
-
export { Api } from './api.js';
|
|
40
|
-
export { Tool } from './tool.js';
|
|
41
28
|
|
|
42
29
|
// 类型导出
|
|
43
|
-
export type { QueryOptions, InsertOptions, UpdateOptions, DeleteOptions, ListResult, TransactionCallback } from './
|
|
30
|
+
export type { QueryOptions, InsertOptions, UpdateOptions, DeleteOptions, ListResult, TransactionCallback } from './dbHelper.js';
|
|
44
31
|
export type { JwtPayload } from './jwt.js';
|
|
45
32
|
export type { EncodingType, HashAlgorithm } from './crypto.js';
|
|
46
33
|
export type { XmlParseOptions } from './xml.js';
|
package/utils/logger.ts
CHANGED
|
@@ -6,19 +6,10 @@
|
|
|
6
6
|
import path from 'path';
|
|
7
7
|
import { appendFile, stat } from 'node:fs/promises';
|
|
8
8
|
import { formatDate } from './index.js';
|
|
9
|
+
import { Colors } from './colors.js';
|
|
9
10
|
import { Env } from '../config/env.js';
|
|
10
11
|
import type { LogLevel } from '../types/common.js';
|
|
11
12
|
|
|
12
|
-
/**
|
|
13
|
-
* 日志级别映射
|
|
14
|
-
*/
|
|
15
|
-
interface LogLevels {
|
|
16
|
-
error: number;
|
|
17
|
-
warn: number;
|
|
18
|
-
info: number;
|
|
19
|
-
debug: number;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
13
|
/**
|
|
23
14
|
* 日志消息类型
|
|
24
15
|
*/
|
|
@@ -28,17 +19,6 @@ type LogMessage = string | number | boolean | null | undefined | Record<string,
|
|
|
28
19
|
* 日志器类
|
|
29
20
|
*/
|
|
30
21
|
export class Logger {
|
|
31
|
-
/** 当前日志级别 */
|
|
32
|
-
static level: LogLevel = (Env.LOG_LEVEL as LogLevel) || 'info';
|
|
33
|
-
|
|
34
|
-
/** 日志级别权重 */
|
|
35
|
-
static readonly levels: LogLevels = {
|
|
36
|
-
error: 0,
|
|
37
|
-
warn: 1,
|
|
38
|
-
info: 2,
|
|
39
|
-
debug: 3
|
|
40
|
-
};
|
|
41
|
-
|
|
42
22
|
/** 日志目录 */
|
|
43
23
|
static logDir: string = Env.LOG_DIR || 'logs';
|
|
44
24
|
|
|
@@ -49,55 +29,44 @@ export class Logger {
|
|
|
49
29
|
private static currentFiles: Map<string, string> = new Map();
|
|
50
30
|
|
|
51
31
|
/**
|
|
52
|
-
*
|
|
32
|
+
* 记录日志
|
|
53
33
|
* @param level - 日志级别
|
|
54
34
|
* @param message - 日志消息
|
|
55
|
-
* @returns 格式化后的日志字符串
|
|
56
35
|
*/
|
|
57
|
-
static
|
|
58
|
-
|
|
59
|
-
|
|
36
|
+
static async log(level: LogLevel, message: LogMessage): Promise<void> {
|
|
37
|
+
// debug 日志特殊处理:仅当 LOG_DEBUG=1 时才记录
|
|
38
|
+
if (level === 'debug' && Env.LOG_DEBUG !== 1) return;
|
|
60
39
|
|
|
61
|
-
|
|
40
|
+
// 格式化消息
|
|
41
|
+
const timestamp = formatDate();
|
|
42
|
+
const colorMap = {
|
|
43
|
+
info: Colors.greenBright,
|
|
44
|
+
debug: Colors.cyanBright,
|
|
45
|
+
warn: Colors.yellowBright,
|
|
46
|
+
error: Colors.redBright
|
|
47
|
+
};
|
|
62
48
|
|
|
63
|
-
//
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
}
|
|
49
|
+
// 处理消息内容
|
|
50
|
+
let content = '';
|
|
51
|
+
if (typeof message === 'object' && message !== null && Object.keys(message).length > 0) {
|
|
52
|
+
content = JSON.stringify(message, null, 0).replace(/\\"/g, '"');
|
|
68
53
|
} else {
|
|
69
|
-
|
|
54
|
+
content = String(message);
|
|
70
55
|
}
|
|
71
56
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* 检查是否应该记录该级别的日志
|
|
77
|
-
* @param level - 日志级别
|
|
78
|
-
* @returns 是否应该记录
|
|
79
|
-
*/
|
|
80
|
-
private static shouldLog(level: LogLevel): boolean {
|
|
81
|
-
return this.levels[level] <= this.levels[this.level];
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* 记录日志
|
|
86
|
-
* @param level - 日志级别
|
|
87
|
-
* @param message - 日志消息
|
|
88
|
-
*/
|
|
89
|
-
static async log(level: LogLevel, message: LogMessage): Promise<void> {
|
|
90
|
-
// 检查日志级别
|
|
91
|
-
if (!this.shouldLog(level)) return;
|
|
92
|
-
|
|
93
|
-
const formattedMessage = this.formatMessage(level, message);
|
|
57
|
+
// 带颜色的控制台消息
|
|
58
|
+
const coloredLevelStr = colorMap[level](level.toUpperCase().padStart(5));
|
|
59
|
+
const coloredMessage = `[${timestamp}] ${coloredLevelStr} - ${content}`;
|
|
94
60
|
|
|
95
61
|
// 控制台输出
|
|
96
62
|
if (Env.LOG_TO_CONSOLE === 1) {
|
|
97
|
-
console.log(
|
|
63
|
+
console.log(coloredMessage);
|
|
98
64
|
}
|
|
99
65
|
|
|
100
|
-
|
|
66
|
+
// 文件输出(去除 ANSI 颜色代码)
|
|
67
|
+
const plainLevelStr = level.toUpperCase().padStart(5);
|
|
68
|
+
const plainMessage = `[${timestamp}] ${plainLevelStr} - ${content}`;
|
|
69
|
+
await this.writeToFile(plainMessage, level);
|
|
101
70
|
}
|
|
102
71
|
|
|
103
72
|
/**
|
|
@@ -107,14 +76,8 @@ export class Logger {
|
|
|
107
76
|
*/
|
|
108
77
|
static async writeToFile(message: string, level: LogLevel = 'info'): Promise<void> {
|
|
109
78
|
try {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
// debug 日志使用单独的文件名
|
|
113
|
-
if (level === 'debug') {
|
|
114
|
-
prefix = 'debug';
|
|
115
|
-
} else {
|
|
116
|
-
prefix = new Date().toISOString().split('T')[0];
|
|
117
|
-
}
|
|
79
|
+
// 确定文件前缀
|
|
80
|
+
const prefix = level === 'debug' ? 'debug' : new Date().toISOString().split('T')[0];
|
|
118
81
|
|
|
119
82
|
// 检查缓存的当前文件是否仍然可用
|
|
120
83
|
let currentLogFile = this.currentFiles.get(prefix);
|
|
@@ -122,74 +85,76 @@ export class Logger {
|
|
|
122
85
|
if (currentLogFile) {
|
|
123
86
|
try {
|
|
124
87
|
const stats = await stat(currentLogFile);
|
|
125
|
-
// 如果文件超过最大大小,清除缓存
|
|
126
88
|
if (stats.size >= this.maxFileSize) {
|
|
127
89
|
this.currentFiles.delete(prefix);
|
|
128
90
|
currentLogFile = undefined;
|
|
129
91
|
}
|
|
130
|
-
} catch
|
|
131
|
-
// 文件不存在或无法访问,清除缓存
|
|
92
|
+
} catch {
|
|
132
93
|
this.currentFiles.delete(prefix);
|
|
133
94
|
currentLogFile = undefined;
|
|
134
95
|
}
|
|
135
96
|
}
|
|
136
97
|
|
|
137
|
-
//
|
|
98
|
+
// 查找或创建新文件
|
|
138
99
|
if (!currentLogFile) {
|
|
139
|
-
|
|
100
|
+
const glob = new Bun.Glob(`${prefix}.*.log`);
|
|
101
|
+
const files = await Array.fromAsync(glob.scan(this.logDir));
|
|
102
|
+
|
|
103
|
+
// 按索引排序并查找可用文件
|
|
104
|
+
const getIndex = (f: string) => parseInt(f.match(/\.(\d+)\.log$/)?.[1] || '0');
|
|
105
|
+
files.sort((a, b) => getIndex(a) - getIndex(b));
|
|
106
|
+
|
|
107
|
+
let foundFile = false;
|
|
108
|
+
for (let i = files.length - 1; i >= 0; i--) {
|
|
109
|
+
const filePath = path.join(this.logDir, files[i]);
|
|
110
|
+
try {
|
|
111
|
+
const stats = await stat(filePath);
|
|
112
|
+
if (stats.size < this.maxFileSize) {
|
|
113
|
+
currentLogFile = filePath;
|
|
114
|
+
foundFile = true;
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
117
|
+
} catch {
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// 没有可用文件,创建新文件
|
|
123
|
+
if (!foundFile) {
|
|
124
|
+
const maxIndex = files.length > 0 ? Math.max(...files.map(getIndex)) : -1;
|
|
125
|
+
currentLogFile = path.join(this.logDir, `${prefix}.${maxIndex + 1}.log`);
|
|
126
|
+
}
|
|
127
|
+
|
|
140
128
|
this.currentFiles.set(prefix, currentLogFile);
|
|
141
129
|
}
|
|
142
130
|
|
|
143
|
-
// 使用 Node.js 的 appendFile 进行文件追加
|
|
144
131
|
await appendFile(currentLogFile, message + '\n', 'utf8');
|
|
145
132
|
} catch (error: any) {
|
|
146
|
-
console.error('写入日志文件失败:', error
|
|
133
|
+
console.error('写入日志文件失败:', error?.message || error);
|
|
147
134
|
}
|
|
148
135
|
}
|
|
149
136
|
|
|
150
137
|
/**
|
|
151
|
-
*
|
|
152
|
-
* @param
|
|
153
|
-
* @
|
|
138
|
+
* 记录错误日志
|
|
139
|
+
* @param name - 错误名称/位置
|
|
140
|
+
* @param error - 错误对象或消息
|
|
154
141
|
*/
|
|
155
|
-
static async
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
// 按文件名排序
|
|
160
|
-
files.sort((a, b) => {
|
|
161
|
-
const aNum = parseInt(a.match(/\.(\d+)\.log$/)?.[1] || '0');
|
|
162
|
-
const bNum = parseInt(b.match(/\.(\d+)\.log$/)?.[1] || '0');
|
|
163
|
-
return aNum - bNum;
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
// 从最后一个文件开始检查
|
|
167
|
-
for (let i = files.length - 1; i >= 0; i--) {
|
|
168
|
-
const filePath = path.join(this.logDir, files[i]);
|
|
169
|
-
try {
|
|
170
|
-
const stats = await stat(filePath);
|
|
171
|
-
if (stats.size < this.maxFileSize) {
|
|
172
|
-
return filePath;
|
|
173
|
-
}
|
|
174
|
-
} catch (error) {
|
|
175
|
-
// 文件不存在或无法访问,跳过
|
|
176
|
-
continue;
|
|
177
|
-
}
|
|
142
|
+
static async error(name: string, error?: any): Promise<void> {
|
|
143
|
+
if (!error) {
|
|
144
|
+
return this.log('error', name);
|
|
178
145
|
}
|
|
179
146
|
|
|
180
|
-
//
|
|
181
|
-
const
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
147
|
+
// 构建错误消息
|
|
148
|
+
const parts = [name];
|
|
149
|
+
if (error?.message || error?.stack) {
|
|
150
|
+
if (error.message) parts.push(error.message);
|
|
151
|
+
if (error.stack) parts.push('\n' + error.stack);
|
|
152
|
+
} else {
|
|
153
|
+
const errorStr = typeof error === 'object' ? JSON.stringify(error) : String(error);
|
|
154
|
+
parts.push(errorStr);
|
|
155
|
+
}
|
|
186
156
|
|
|
187
|
-
|
|
188
|
-
* 记录错误日志
|
|
189
|
-
* @param message - 日志消息
|
|
190
|
-
*/
|
|
191
|
-
static async error(message: LogMessage): Promise<void> {
|
|
192
|
-
await this.log('error', message);
|
|
157
|
+
await this.log('error', parts.join(' - '));
|
|
193
158
|
}
|
|
194
159
|
|
|
195
160
|
/**
|
|
@@ -209,52 +174,12 @@ export class Logger {
|
|
|
209
174
|
}
|
|
210
175
|
|
|
211
176
|
/**
|
|
212
|
-
*
|
|
177
|
+
* 记录调试日志
|
|
178
|
+
* 受 LOG_DEBUG 环境变量控制,仅当 LOG_DEBUG=1 时才记录
|
|
213
179
|
* @param message - 日志消息
|
|
214
180
|
*/
|
|
215
181
|
static async debug(message: LogMessage): Promise<void> {
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
// 控制台输出
|
|
219
|
-
if (Env.LOG_TO_CONSOLE === 1) {
|
|
220
|
-
console.log(formattedMessage);
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
await this.writeToFile(formattedMessage, 'debug');
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
/**
|
|
227
|
-
* 设置日志级别
|
|
228
|
-
* @param level - 新的日志级别
|
|
229
|
-
*/
|
|
230
|
-
static setLevel(level: LogLevel): void {
|
|
231
|
-
this.level = level;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
/**
|
|
235
|
-
* 获取当前日志级别
|
|
236
|
-
* @returns 当前日志级别
|
|
237
|
-
*/
|
|
238
|
-
static getLevel(): LogLevel {
|
|
239
|
-
return this.level;
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
/**
|
|
243
|
-
* 设置日志目录
|
|
244
|
-
* @param dir - 新的日志目录
|
|
245
|
-
*/
|
|
246
|
-
static setLogDir(dir: string): void {
|
|
247
|
-
this.logDir = dir;
|
|
248
|
-
// 清除文件缓存
|
|
249
|
-
this.currentFiles.clear();
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
/**
|
|
253
|
-
* 设置最大文件大小
|
|
254
|
-
* @param size - 文件大小(字节)
|
|
255
|
-
*/
|
|
256
|
-
static setMaxFileSize(size: number): void {
|
|
257
|
-
this.maxFileSize = size;
|
|
182
|
+
await this.log('debug', message);
|
|
258
183
|
}
|
|
259
184
|
|
|
260
185
|
/**
|
|
@@ -263,22 +188,4 @@ export class Logger {
|
|
|
263
188
|
static clearCache(): void {
|
|
264
189
|
this.currentFiles.clear();
|
|
265
190
|
}
|
|
266
|
-
|
|
267
|
-
/**
|
|
268
|
-
* 获取日志文件统计信息
|
|
269
|
-
* @returns 日志文件统计
|
|
270
|
-
*/
|
|
271
|
-
static getStats(): {
|
|
272
|
-
level: LogLevel;
|
|
273
|
-
logDir: string;
|
|
274
|
-
maxFileSize: number;
|
|
275
|
-
cachedFiles: number;
|
|
276
|
-
} {
|
|
277
|
-
return {
|
|
278
|
-
level: this.level,
|
|
279
|
-
logDir: this.logDir,
|
|
280
|
-
maxFileSize: this.maxFileSize,
|
|
281
|
-
cachedFiles: this.currentFiles.size
|
|
282
|
-
};
|
|
283
|
-
}
|
|
284
191
|
}
|