befly 2.3.2 → 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.
Files changed (93) hide show
  1. package/apis/health/info.ts +64 -0
  2. package/apis/tool/tokenCheck.ts +51 -0
  3. package/bin/befly.ts +202 -0
  4. package/checks/conflict.ts +408 -0
  5. package/checks/table.ts +284 -0
  6. package/config/env.ts +218 -0
  7. package/config/reserved.ts +96 -0
  8. package/main.ts +101 -0
  9. package/package.json +45 -16
  10. package/plugins/{db.js → db.ts} +25 -12
  11. package/plugins/logger.ts +28 -0
  12. package/plugins/redis.ts +51 -0
  13. package/plugins/tool.ts +34 -0
  14. package/scripts/syncDb/apply.ts +171 -0
  15. package/scripts/syncDb/constants.ts +70 -0
  16. package/scripts/syncDb/ddl.ts +182 -0
  17. package/scripts/syncDb/helpers.ts +172 -0
  18. package/scripts/syncDb/index.ts +215 -0
  19. package/scripts/syncDb/schema.ts +199 -0
  20. package/scripts/syncDb/sqlite.ts +50 -0
  21. package/scripts/syncDb/state.ts +104 -0
  22. package/scripts/syncDb/table.ts +204 -0
  23. package/scripts/syncDb/tableCreate.ts +142 -0
  24. package/scripts/syncDb/tests/constants.test.ts +104 -0
  25. package/scripts/syncDb/tests/ddl.test.ts +134 -0
  26. package/scripts/syncDb/tests/helpers.test.ts +70 -0
  27. package/scripts/syncDb/types.ts +92 -0
  28. package/scripts/syncDb/version.ts +73 -0
  29. package/scripts/syncDb.ts +9 -0
  30. package/scripts/syncDev.ts +112 -0
  31. package/system.ts +149 -0
  32. package/tables/_common.json +21 -0
  33. package/tables/admin.json +10 -0
  34. package/tsconfig.json +58 -0
  35. package/types/api.d.ts +246 -0
  36. package/types/befly.d.ts +234 -0
  37. package/types/common.d.ts +215 -0
  38. package/types/context.ts +167 -0
  39. package/types/crypto.d.ts +23 -0
  40. package/types/database.d.ts +278 -0
  41. package/types/index.d.ts +16 -0
  42. package/types/index.ts +459 -0
  43. package/types/jwt.d.ts +99 -0
  44. package/types/logger.d.ts +43 -0
  45. package/types/plugin.d.ts +109 -0
  46. package/types/redis.d.ts +44 -0
  47. package/types/tool.d.ts +67 -0
  48. package/types/validator.d.ts +45 -0
  49. package/utils/addonHelper.ts +60 -0
  50. package/utils/api.ts +23 -0
  51. package/utils/{colors.js → colors.ts} +79 -21
  52. package/utils/crypto.ts +308 -0
  53. package/utils/datetime.ts +51 -0
  54. package/utils/dbHelper.ts +142 -0
  55. package/utils/errorHandler.ts +68 -0
  56. package/utils/index.ts +46 -0
  57. package/utils/jwt.ts +493 -0
  58. package/utils/logger.ts +284 -0
  59. package/utils/objectHelper.ts +68 -0
  60. package/utils/pluginHelper.ts +62 -0
  61. package/utils/redisHelper.ts +338 -0
  62. package/utils/response.ts +38 -0
  63. package/utils/{sqlBuilder.js → sqlBuilder.ts} +233 -97
  64. package/utils/sqlHelper.ts +447 -0
  65. package/utils/tableHelper.ts +167 -0
  66. package/utils/tool.ts +230 -0
  67. package/utils/typeHelper.ts +101 -0
  68. package/utils/validate.ts +451 -0
  69. package/utils/{xml.js → xml.ts} +100 -74
  70. package/.npmrc +0 -3
  71. package/.prettierignore +0 -2
  72. package/.prettierrc +0 -11
  73. package/apis/health/info.js +0 -49
  74. package/apis/tool/tokenCheck.js +0 -29
  75. package/checks/table.js +0 -221
  76. package/config/env.js +0 -62
  77. package/main.js +0 -579
  78. package/plugins/logger.js +0 -14
  79. package/plugins/redis.js +0 -32
  80. package/plugins/tool.js +0 -8
  81. package/scripts/syncDb.js +0 -603
  82. package/system.js +0 -118
  83. package/tables/common.json +0 -16
  84. package/tables/tool.json +0 -6
  85. package/utils/api.js +0 -27
  86. package/utils/crypto.js +0 -260
  87. package/utils/index.js +0 -387
  88. package/utils/jwt.js +0 -387
  89. package/utils/logger.js +0 -143
  90. package/utils/redisHelper.js +0 -74
  91. package/utils/sqlManager.js +0 -471
  92. package/utils/tool.js +0 -31
  93. package/utils/validate.js +0 -228
@@ -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
+ };