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,338 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Redis 助手 - TypeScript 版本
|
|
3
|
+
* 提供 Redis 操作的便捷方法
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { RedisClient } from 'bun';
|
|
7
|
+
import { Env } from '../config/env.js';
|
|
8
|
+
import { Logger } from './logger.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Redis 键前缀
|
|
12
|
+
*/
|
|
13
|
+
const prefix = Env.REDIS_KEY_PREFIX ? `${Env.REDIS_KEY_PREFIX}:` : '';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* 构建 Redis 连接 URL
|
|
17
|
+
* @returns Redis 连接 URL
|
|
18
|
+
*/
|
|
19
|
+
function buildRedisUrl(): string {
|
|
20
|
+
const { REDIS_HOST, REDIS_PORT, REDIS_USERNAME, REDIS_PASSWORD, REDIS_DB } = Env;
|
|
21
|
+
|
|
22
|
+
// 构建认证部分
|
|
23
|
+
let auth = '';
|
|
24
|
+
if (REDIS_USERNAME && REDIS_PASSWORD) {
|
|
25
|
+
auth = `${REDIS_USERNAME}:${REDIS_PASSWORD}@`;
|
|
26
|
+
} else if (REDIS_PASSWORD) {
|
|
27
|
+
auth = `:${REDIS_PASSWORD}@`;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// 构建完整 URL
|
|
31
|
+
const url = `redis://${auth}${REDIS_HOST}:${REDIS_PORT}/${REDIS_DB}`;
|
|
32
|
+
|
|
33
|
+
return url;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Redis 客户端实例
|
|
38
|
+
*/
|
|
39
|
+
let redisClient: RedisClient | null = null;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* 初始化 Redis 客户端
|
|
43
|
+
* @returns Redis 客户端实例
|
|
44
|
+
* @throws 如果连接失败
|
|
45
|
+
*/
|
|
46
|
+
export const initRedisClient = async (): Promise<RedisClient> => {
|
|
47
|
+
if (!redisClient) {
|
|
48
|
+
try {
|
|
49
|
+
const url = buildRedisUrl();
|
|
50
|
+
redisClient = new RedisClient(url, {
|
|
51
|
+
// 连接超时(毫秒)
|
|
52
|
+
connectionTimeout: 10000,
|
|
53
|
+
// 空闲超时设为 0,表示永不超时
|
|
54
|
+
idleTimeout: 0,
|
|
55
|
+
// 断开连接时自动重连
|
|
56
|
+
autoReconnect: true,
|
|
57
|
+
// 最大重连次数,0 表示无限重连
|
|
58
|
+
maxRetries: 0,
|
|
59
|
+
// 断开连接时缓存命令
|
|
60
|
+
enableOfflineQueue: true,
|
|
61
|
+
// 自动管道化命令
|
|
62
|
+
enableAutoPipelining: true
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// 测试连接是否成功
|
|
66
|
+
await redisClient.ping();
|
|
67
|
+
Logger.info('Redis 连接成功');
|
|
68
|
+
} catch (error: any) {
|
|
69
|
+
redisClient = null;
|
|
70
|
+
Logger.error({
|
|
71
|
+
msg: 'Redis 连接失败',
|
|
72
|
+
message: error.message,
|
|
73
|
+
code: error.code,
|
|
74
|
+
stack: error.stack
|
|
75
|
+
});
|
|
76
|
+
throw new Error(`Redis 连接失败: ${error.message}`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return redisClient;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* 获取 Redis 客户端
|
|
84
|
+
* @returns Redis 客户端实例
|
|
85
|
+
* @throws 如果连接失败
|
|
86
|
+
*/
|
|
87
|
+
export const getRedisClient = async (): Promise<RedisClient> => {
|
|
88
|
+
if (!redisClient) {
|
|
89
|
+
return await initRedisClient();
|
|
90
|
+
}
|
|
91
|
+
return redisClient;
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* 关闭 Redis 连接
|
|
96
|
+
*/
|
|
97
|
+
export const closeRedisClient = (): void => {
|
|
98
|
+
if (redisClient) {
|
|
99
|
+
redisClient.close();
|
|
100
|
+
redisClient = null;
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Redis 助手对象
|
|
106
|
+
*/
|
|
107
|
+
export const RedisHelper = {
|
|
108
|
+
/**
|
|
109
|
+
* 获取 Redis 客户端实例
|
|
110
|
+
* @returns Redis 客户端
|
|
111
|
+
* @throws 如果连接失败
|
|
112
|
+
*/
|
|
113
|
+
async getRedisClient(): Promise<RedisClient> {
|
|
114
|
+
return await getRedisClient();
|
|
115
|
+
},
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* 设置对象到 Redis
|
|
119
|
+
* @param key - 键名
|
|
120
|
+
* @param obj - 对象
|
|
121
|
+
* @param ttl - 过期时间(秒)
|
|
122
|
+
* @returns 操作结果
|
|
123
|
+
*/
|
|
124
|
+
async setObject<T = any>(key: string, obj: T, ttl: number | null = null): Promise<string | null> {
|
|
125
|
+
try {
|
|
126
|
+
const client = await getRedisClient();
|
|
127
|
+
const data = JSON.stringify(obj);
|
|
128
|
+
const pkey = `${prefix}${key}`;
|
|
129
|
+
|
|
130
|
+
if (ttl) {
|
|
131
|
+
return await client.setEx(pkey, ttl, data);
|
|
132
|
+
}
|
|
133
|
+
return await client.set(pkey, data);
|
|
134
|
+
} catch (error: any) {
|
|
135
|
+
Logger.error({
|
|
136
|
+
msg: 'Redis setObject 错误',
|
|
137
|
+
message: error.message,
|
|
138
|
+
stack: error.stack
|
|
139
|
+
});
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* 从 Redis 获取对象
|
|
146
|
+
* @param key - 键名
|
|
147
|
+
* @returns 对象或 null
|
|
148
|
+
*/
|
|
149
|
+
async getObject<T = any>(key: string): Promise<T | null> {
|
|
150
|
+
try {
|
|
151
|
+
const client = await getRedisClient();
|
|
152
|
+
const pkey = `${prefix}${key}`;
|
|
153
|
+
const data = await client.get(pkey);
|
|
154
|
+
return data ? JSON.parse(data) : null;
|
|
155
|
+
} catch (error: any) {
|
|
156
|
+
Logger.error({
|
|
157
|
+
msg: 'Redis getObject 错误',
|
|
158
|
+
message: error.message,
|
|
159
|
+
stack: error.stack
|
|
160
|
+
});
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* 从 Redis 删除对象
|
|
167
|
+
* @param key - 键名
|
|
168
|
+
*/
|
|
169
|
+
async delObject(key: string): Promise<void> {
|
|
170
|
+
try {
|
|
171
|
+
const client = await getRedisClient();
|
|
172
|
+
const pkey = `${prefix}${key}`;
|
|
173
|
+
await client.del(pkey);
|
|
174
|
+
} catch (error: any) {
|
|
175
|
+
Logger.error({
|
|
176
|
+
msg: 'Redis delObject 错误',
|
|
177
|
+
message: error.message,
|
|
178
|
+
stack: error.stack
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
},
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* 生成基于时间的唯一 ID
|
|
185
|
+
* 格式: Date.now()(13位) + 3位自增 = 16位纯数字
|
|
186
|
+
* 容量: 1000/毫秒 = 1,000,000/秒
|
|
187
|
+
* @returns 唯一 ID (16位纯数字)
|
|
188
|
+
*/
|
|
189
|
+
async genTimeID(): Promise<number> {
|
|
190
|
+
const client = await getRedisClient();
|
|
191
|
+
const timestamp = Date.now();
|
|
192
|
+
const key = `${prefix}time_id_counter:${timestamp}`;
|
|
193
|
+
|
|
194
|
+
const counter = await client.incr(key);
|
|
195
|
+
await client.expire(key, 1);
|
|
196
|
+
|
|
197
|
+
const counterSuffix = (counter % 1000).toString().padStart(3, '0');
|
|
198
|
+
|
|
199
|
+
return Number(`${timestamp}${counterSuffix}`);
|
|
200
|
+
},
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* 批量生成基于时间的唯一 ID
|
|
204
|
+
* 格式: Date.now()(13位) + 3位自增 = 16位纯数字
|
|
205
|
+
* @param count - 需要生成的 ID 数量
|
|
206
|
+
* @returns ID 数组 (16位纯数字)
|
|
207
|
+
*/
|
|
208
|
+
async genTimeIDBatch(count: number): Promise<number[]> {
|
|
209
|
+
if (count <= 0) {
|
|
210
|
+
return [];
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// 限制单次批量生成数量
|
|
214
|
+
const MAX_BATCH_SIZE = 10000;
|
|
215
|
+
if (count > MAX_BATCH_SIZE) {
|
|
216
|
+
throw new Error(`批量大小 ${count} 超过最大限制 ${MAX_BATCH_SIZE}`);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const client = await getRedisClient();
|
|
220
|
+
const timestamp = Date.now();
|
|
221
|
+
const key = `${prefix}time_id_counter:${timestamp}`;
|
|
222
|
+
|
|
223
|
+
// 使用 INCRBY 一次性获取 N 个连续计数
|
|
224
|
+
const startCounter = await client.incrBy(key, count);
|
|
225
|
+
await client.expire(key, 1);
|
|
226
|
+
|
|
227
|
+
// 生成 ID 数组
|
|
228
|
+
const ids: number[] = [];
|
|
229
|
+
for (let i = 0; i < count; i++) {
|
|
230
|
+
const counter = startCounter - count + i + 1; // 计算每个 ID 的计数值
|
|
231
|
+
const counterSuffix = (counter % 1000).toString().padStart(3, '0');
|
|
232
|
+
ids.push(Number(`${timestamp}${counterSuffix}`));
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
return ids;
|
|
236
|
+
},
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* 设置字符串值
|
|
240
|
+
* @param key - 键名
|
|
241
|
+
* @param value - 值
|
|
242
|
+
* @param ttl - 过期时间(秒)
|
|
243
|
+
*/
|
|
244
|
+
async setString(key: string, value: string, ttl: number | null = null): Promise<string | null> {
|
|
245
|
+
try {
|
|
246
|
+
const client = await getRedisClient();
|
|
247
|
+
const pkey = `${prefix}${key}`;
|
|
248
|
+
if (ttl) {
|
|
249
|
+
return await client.setEx(pkey, ttl, value);
|
|
250
|
+
}
|
|
251
|
+
return await client.set(pkey, value);
|
|
252
|
+
} catch (error: any) {
|
|
253
|
+
Logger.error({
|
|
254
|
+
msg: 'Redis setString 错误',
|
|
255
|
+
message: error.message,
|
|
256
|
+
stack: error.stack
|
|
257
|
+
});
|
|
258
|
+
return null;
|
|
259
|
+
}
|
|
260
|
+
},
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* 获取字符串值
|
|
264
|
+
* @param key - 键名
|
|
265
|
+
*/
|
|
266
|
+
async getString(key: string): Promise<string | null> {
|
|
267
|
+
try {
|
|
268
|
+
const client = await getRedisClient();
|
|
269
|
+
const pkey = `${prefix}${key}`;
|
|
270
|
+
return await client.get(pkey);
|
|
271
|
+
} catch (error: any) {
|
|
272
|
+
Logger.error({
|
|
273
|
+
msg: 'Redis getString 错误',
|
|
274
|
+
message: error.message,
|
|
275
|
+
stack: error.stack
|
|
276
|
+
});
|
|
277
|
+
return null;
|
|
278
|
+
}
|
|
279
|
+
},
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* 检查键是否存在
|
|
283
|
+
* @param key - 键名
|
|
284
|
+
*/
|
|
285
|
+
async exists(key: string): Promise<number> {
|
|
286
|
+
try {
|
|
287
|
+
const client = await getRedisClient();
|
|
288
|
+
const pkey = `${prefix}${key}`;
|
|
289
|
+
return await client.exists(pkey);
|
|
290
|
+
} catch (error: any) {
|
|
291
|
+
Logger.error({
|
|
292
|
+
msg: 'Redis exists 错误',
|
|
293
|
+
message: error.message,
|
|
294
|
+
stack: error.stack
|
|
295
|
+
});
|
|
296
|
+
return 0;
|
|
297
|
+
}
|
|
298
|
+
},
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* 设置过期时间
|
|
302
|
+
* @param key - 键名
|
|
303
|
+
* @param seconds - 秒数
|
|
304
|
+
*/
|
|
305
|
+
async expire(key: string, seconds: number): Promise<number> {
|
|
306
|
+
try {
|
|
307
|
+
const client = await getRedisClient();
|
|
308
|
+
const pkey = `${prefix}${key}`;
|
|
309
|
+
return await client.expire(pkey, seconds);
|
|
310
|
+
} catch (error: any) {
|
|
311
|
+
Logger.error({
|
|
312
|
+
msg: 'Redis expire 错误',
|
|
313
|
+
message: error.message,
|
|
314
|
+
stack: error.stack
|
|
315
|
+
});
|
|
316
|
+
return 0;
|
|
317
|
+
}
|
|
318
|
+
},
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* 获取剩余过期时间
|
|
322
|
+
* @param key - 键名
|
|
323
|
+
*/
|
|
324
|
+
async ttl(key: string): Promise<number> {
|
|
325
|
+
try {
|
|
326
|
+
const client = await getRedisClient();
|
|
327
|
+
const pkey = `${prefix}${key}`;
|
|
328
|
+
return await client.ttl(pkey);
|
|
329
|
+
} catch (error: any) {
|
|
330
|
+
Logger.error({
|
|
331
|
+
msg: 'Redis ttl 错误',
|
|
332
|
+
message: error.message,
|
|
333
|
+
stack: error.stack
|
|
334
|
+
});
|
|
335
|
+
return -1;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Befly API 响应工具
|
|
3
|
+
* 提供统一的成功和失败响应格式
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { KeyValue } from '../types/common.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* 成功响应
|
|
10
|
+
* @param msg - 响应消息
|
|
11
|
+
* @param data - 响应数据
|
|
12
|
+
* @param other - 其他字段
|
|
13
|
+
* @returns 成功响应对象 { code: 0, msg, data, ...other }
|
|
14
|
+
*/
|
|
15
|
+
export const Yes = <T = any>(msg: string = '', data: T | {} = {}, other: KeyValue = {}): { code: 0; msg: string; data: T | {} } & KeyValue => {
|
|
16
|
+
return {
|
|
17
|
+
...other,
|
|
18
|
+
code: 0,
|
|
19
|
+
msg: msg,
|
|
20
|
+
data: data
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* 失败响应
|
|
26
|
+
* @param msg - 错误消息
|
|
27
|
+
* @param data - 错误数据
|
|
28
|
+
* @param other - 其他字段
|
|
29
|
+
* @returns 失败响应对象 { code: 1, msg, data, ...other }
|
|
30
|
+
*/
|
|
31
|
+
export const No = <T = any>(msg: string = '', data: T | {} = {}, other: KeyValue = {}): { code: 1; msg: string; data: T | {} } & KeyValue => {
|
|
32
|
+
return {
|
|
33
|
+
...other,
|
|
34
|
+
code: 1,
|
|
35
|
+
msg: msg,
|
|
36
|
+
data: data
|
|
37
|
+
};
|
|
38
|
+
};
|