befly 2.3.3 → 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.
Files changed (87) hide show
  1. package/checks/conflict.ts +329 -0
  2. package/checks/table.ts +252 -0
  3. package/config/env.ts +218 -0
  4. package/config/fields.ts +55 -0
  5. package/config/regexAliases.ts +51 -0
  6. package/config/reserved.ts +96 -0
  7. package/main.ts +47 -0
  8. package/package.json +26 -11
  9. package/plugins/db.ts +60 -0
  10. package/plugins/logger.ts +28 -0
  11. package/plugins/redis.ts +47 -0
  12. package/scripts/syncDb/apply.ts +171 -0
  13. package/scripts/syncDb/constants.ts +71 -0
  14. package/scripts/syncDb/ddl.ts +189 -0
  15. package/scripts/syncDb/helpers.ts +173 -0
  16. package/scripts/syncDb/index.ts +203 -0
  17. package/scripts/syncDb/schema.ts +199 -0
  18. package/scripts/syncDb/sqlite.ts +50 -0
  19. package/scripts/syncDb/state.ts +106 -0
  20. package/scripts/syncDb/table.ts +214 -0
  21. package/scripts/syncDb/tableCreate.ts +148 -0
  22. package/scripts/syncDb/tests/constants.test.ts +105 -0
  23. package/scripts/syncDb/tests/ddl.test.ts +134 -0
  24. package/scripts/syncDb/tests/helpers.test.ts +70 -0
  25. package/scripts/syncDb/types.ts +92 -0
  26. package/scripts/syncDb/version.ts +73 -0
  27. package/scripts/syncDb.ts +10 -0
  28. package/tsconfig.json +58 -0
  29. package/types/addon.d.ts +53 -0
  30. package/types/api.d.ts +249 -0
  31. package/types/befly.d.ts +230 -0
  32. package/types/common.d.ts +215 -0
  33. package/types/context.d.ts +7 -0
  34. package/types/crypto.d.ts +23 -0
  35. package/types/database.d.ts +273 -0
  36. package/types/index.d.ts +450 -0
  37. package/types/index.ts +438 -0
  38. package/types/jwt.d.ts +99 -0
  39. package/types/logger.d.ts +43 -0
  40. package/types/plugin.d.ts +109 -0
  41. package/types/redis.d.ts +46 -0
  42. package/types/tool.d.ts +67 -0
  43. package/types/validator.d.ts +43 -0
  44. package/types/validator.ts +43 -0
  45. package/utils/colors.ts +221 -0
  46. package/utils/crypto.ts +308 -0
  47. package/utils/database.ts +348 -0
  48. package/utils/dbHelper.ts +713 -0
  49. package/utils/helper.ts +812 -0
  50. package/utils/index.ts +33 -0
  51. package/utils/jwt.ts +493 -0
  52. package/utils/logger.ts +191 -0
  53. package/utils/redisHelper.ts +321 -0
  54. package/utils/requestContext.ts +167 -0
  55. package/utils/sqlBuilder.ts +611 -0
  56. package/utils/validate.ts +493 -0
  57. package/utils/{xml.js → xml.ts} +100 -74
  58. package/.npmrc +0 -3
  59. package/.prettierignore +0 -2
  60. package/.prettierrc +0 -11
  61. package/apis/health/info.js +0 -49
  62. package/apis/tool/tokenCheck.js +0 -29
  63. package/bin/befly.js +0 -109
  64. package/bunfig.toml +0 -3
  65. package/checks/table.js +0 -206
  66. package/config/env.js +0 -64
  67. package/main.js +0 -579
  68. package/plugins/db.js +0 -46
  69. package/plugins/logger.js +0 -14
  70. package/plugins/redis.js +0 -32
  71. package/plugins/tool.js +0 -8
  72. package/scripts/syncDb.js +0 -752
  73. package/scripts/syncDev.js +0 -96
  74. package/system.js +0 -118
  75. package/tables/common.json +0 -16
  76. package/tables/tool.json +0 -6
  77. package/utils/api.js +0 -27
  78. package/utils/colors.js +0 -83
  79. package/utils/crypto.js +0 -260
  80. package/utils/index.js +0 -334
  81. package/utils/jwt.js +0 -387
  82. package/utils/logger.js +0 -143
  83. package/utils/redisHelper.js +0 -74
  84. package/utils/sqlBuilder.js +0 -498
  85. package/utils/sqlManager.js +0 -471
  86. package/utils/tool.js +0 -31
  87. package/utils/validate.js +0 -226
package/utils/index.js DELETED
@@ -1,334 +0,0 @@
1
- import { fileURLToPath } from 'node:url';
2
- import path from 'node:path';
3
- import { SQL } from 'bun';
4
- import { Env } from '../config/env.js';
5
- import { Logger } from './logger.js';
6
-
7
- export const setCorsOptions = (req) => {
8
- return {
9
- headers: {
10
- 'Access-Control-Allow-Origin': Env.ALLOWED_ORIGIN || req.headers.get('origin') || '*',
11
- 'Access-Control-Allow-Methods': Env.ALLOWED_METHODS || 'GET, POST, PUT, DELETE, OPTIONS',
12
- 'Access-Control-Allow-Headers': Env.ALLOWED_HEADERS || 'Content-Type, Authorization, authorization, token',
13
- 'Access-Control-Expose-Headers': Env.EXPOSE_HEADERS || 'Content-Range, X-Content-Range, Authorization, authorization, token',
14
- 'Access-Control-Max-Age': Env.MAX_AGE || 86400,
15
- 'Access-Control-Allow-Credentials': Env.ALLOW_CREDENTIALS || 'true'
16
- }
17
- };
18
- };
19
-
20
- export const sortPlugins = (plugins) => {
21
- const result = [];
22
- const visited = new Set();
23
- const visiting = new Set();
24
- const pluginMap = Object.fromEntries(plugins.map((p) => [p.pluginName, p]));
25
- let isPass = true;
26
- const visit = (name) => {
27
- if (visited.has(name)) return;
28
- if (visiting.has(name)) {
29
- isPass = false;
30
- return;
31
- }
32
-
33
- const plugin = pluginMap[name];
34
- if (!plugin) return; // 依赖不存在时跳过
35
-
36
- visiting.add(name);
37
- (plugin.after || []).forEach(visit);
38
- visiting.delete(name);
39
- visited.add(name);
40
- result.push(plugin);
41
- };
42
-
43
- plugins.forEach((p) => visit(p.pluginName));
44
- return isPass ? result : false;
45
- };
46
-
47
- /**
48
- * 解析字段规则字符串(以 ⚡ 分隔),并进行最小类型转换:
49
- * - 返回顺序:显示名, 类型, 最小值, 最大值, 默认值, 是否索引, 正则
50
- * - 最小值/最大值/是否索引:当不为字面量字符串 'null' 时转为数字;否则保留原值
51
- * - 默认值:当类型为 number 且默认值不为 'null' 时转为数字;否则保留原值
52
- * - 不做正确性校验(由 checks/table.js 负责)
53
- * - 保留额外段位(>7)以便上层可检测异常长度
54
- *
55
- * @param {string} rule
56
- * @returns {any[]} 一个数组,至少包含前7段(若原始段位不足则按原样长度返回),多余段位将原样附加
57
- */
58
- export const parseRule = (rule) => {
59
- let [fieldName, fieldType, fieldMin, fieldMax, fieldDefault, fieldIndex, fieldRegx] = rule.split('⚡');
60
-
61
- fieldIndex = Number(fieldIndex);
62
- if (fieldMin !== 'null') fieldMin = Number(fieldMin);
63
- if (fieldMax !== 'null') fieldMax = Number(fieldMax);
64
- if (fieldType !== 'number') fieldDefault = Number(fieldDefault);
65
-
66
- return [fieldName, fieldType, fieldMin, fieldMax, fieldDefault, fieldIndex, fieldRegx];
67
- };
68
-
69
- export const formatDate = (date = new Date(), format = 'YYYY-MM-DD HH:mm:ss') => {
70
- const d = new Date(date);
71
- const year = d.getFullYear();
72
- const month = String(d.getMonth() + 1).padStart(2, '0');
73
- const day = String(d.getDate()).padStart(2, '0');
74
- const hour = String(d.getHours()).padStart(2, '0');
75
- const minute = String(d.getMinutes()).padStart(2, '0');
76
- const second = String(d.getSeconds()).padStart(2, '0');
77
-
78
- return format.replace('YYYY', year).replace('MM', month).replace('DD', day).replace('HH', hour).replace('mm', minute).replace('ss', second);
79
- };
80
-
81
- /**
82
- * 计算时间差并返回带单位的字符串
83
- * @param {number} startTime - 开始时间(Bun.nanoseconds()返回值)
84
- * @param {number} endTime - 结束时间(可选,默认为当前时间)
85
- * @returns {string} 时间差(如果小于1秒返回"xx 毫秒",否则返回"xx 秒")
86
- */
87
- export const calcPerfTime = (startTime, endTime = Bun.nanoseconds()) => {
88
- const elapsedMs = (endTime - startTime) / 1_000_000;
89
-
90
- if (elapsedMs < 1000) {
91
- return `${elapsedMs.toFixed(2)} 毫秒`;
92
- } else {
93
- const elapsedSeconds = elapsedMs / 1000;
94
- return `${elapsedSeconds.toFixed(2)} 秒`;
95
- }
96
- };
97
-
98
- // 类型判断
99
- export const isType = (value, type) => {
100
- const actualType = Object.prototype.toString.call(value).slice(8, -1).toLowerCase();
101
- const expectedType = String(type).toLowerCase();
102
-
103
- // 语义类型单独处理,其余走 actualType === expectedType
104
- switch (expectedType) {
105
- case 'function':
106
- // 统一将普通函数、异步函数、生成器函数等都识别为函数
107
- return typeof value === 'function';
108
- case 'nan':
109
- return typeof value === 'number' && Number.isNaN(value);
110
- case 'empty':
111
- return value === '' || value === null || value === undefined;
112
- case 'integer':
113
- return Number.isInteger(value);
114
- case 'float':
115
- return typeof value === 'number' && !Number.isInteger(value) && !Number.isNaN(value);
116
- case 'positive':
117
- return typeof value === 'number' && value > 0;
118
- case 'negative':
119
- return typeof value === 'number' && value < 0;
120
- case 'zero':
121
- return value === 0;
122
- case 'truthy':
123
- return !!value;
124
- case 'falsy':
125
- return !value;
126
- case 'primitive':
127
- return value !== Object(value);
128
- case 'reference':
129
- return value === Object(value);
130
- default:
131
- return actualType === expectedType;
132
- }
133
- };
134
-
135
- export const pickFields = (obj, keys) => {
136
- // 仅对对象或数组进行字段挑选,其他类型返回空对象(保持原有行为)
137
- if (!obj || (!isType(obj, 'object') && !isType(obj, 'array'))) {
138
- return {};
139
- }
140
-
141
- const result = {};
142
-
143
- for (const key of keys) {
144
- if (key in obj) {
145
- result[key] = obj[key];
146
- }
147
- }
148
-
149
- return result;
150
- };
151
-
152
- export const omitFields = (data, excludeKeys = [], excludeValues = []) => {
153
- const shouldDropValue = (v) => excludeValues.some((x) => x === v);
154
-
155
- const cleanObject = (obj) => {
156
- if (!isType(obj, 'object')) return obj;
157
- const result = {};
158
- for (const [k, v] of Object.entries(obj)) {
159
- if (excludeKeys.includes(k)) continue;
160
- if (shouldDropValue(v)) continue;
161
- result[k] = v;
162
- }
163
- return result;
164
- };
165
-
166
- if (isType(data, 'array')) {
167
- return data.filter((item) => !shouldDropValue(item)).map((item) => (isType(item, 'object') ? cleanObject(item) : item));
168
- }
169
-
170
- if (isType(data, 'object')) {
171
- return cleanObject(data);
172
- }
173
-
174
- // 非对象/数组则原样返回(不处理)
175
- return data;
176
- };
177
-
178
- export const isEmptyObject = (obj) => {
179
- // 首先检查是否为对象
180
- if (!isType(obj, 'object')) {
181
- return false;
182
- }
183
-
184
- // 检查是否为空对象
185
- return Object.keys(obj).length === 0;
186
- };
187
-
188
- export const isEmptyArray = (arr) => {
189
- // 首先检查是否为数组
190
- if (!isType(arr, 'array')) {
191
- return false;
192
- }
193
-
194
- // 检查是否为空数组
195
- return arr.length === 0;
196
- };
197
-
198
- // 返回结果
199
- export const RYes = (msg = '', data = {}, other = {}) => {
200
- return {
201
- ...other,
202
- code: 0,
203
- msg: msg,
204
- data: data
205
- };
206
- };
207
-
208
- export const RNo = (msg = '', data = {}, other = {}) => {
209
- return {
210
- ...other,
211
- code: 1,
212
- msg: msg,
213
- data: data
214
- };
215
- };
216
-
217
- export const filename2 = (importMetaUrl) => {
218
- return fileURLToPath(importMetaUrl);
219
- };
220
-
221
- export const dirname2 = (importMetaUrl) => {
222
- return path.dirname(fileURLToPath(importMetaUrl));
223
- };
224
-
225
- // 过滤日志字段的函数
226
- export const filterLogFields = (body, excludeFields = '') => {
227
- // 仅在对象或数组时进行过滤,保持与原 typeof === 'object' 行为一致(数组也会进入)
228
- if (!body || (!isType(body, 'object') && !isType(body, 'array'))) return body;
229
-
230
- // 如果是字符串,按逗号分割并清理空格
231
- const fieldsArray = excludeFields
232
- .split(',')
233
- .map((field) => field.trim())
234
- .filter((field) => field.length > 0);
235
-
236
- // 创建新对象,只包含不在排除列表中的字段
237
- const filtered = {};
238
- for (const [key, value] of Object.entries(body)) {
239
- if (!fieldsArray.includes(key)) {
240
- filtered[key] = value;
241
- }
242
- }
243
- return filtered;
244
- };
245
-
246
- // 将 lowerCamelCase 或单词形式转换为下划线风格(snake_case)
247
- // 例如:userTable -> user_table, testNewFormat -> test_new_format, users -> users, orderV2 -> order_v2
248
- export const toSnakeTableName = (name) => {
249
- if (!name) return name;
250
- return String(name)
251
- .replace(/([a-z0-9])([A-Z])/g, '$1_$2')
252
- .replace(/([A-Z]+)([A-Z][a-z0-9]+)/g, '$1_$2')
253
- .toLowerCase();
254
- };
255
-
256
- /**
257
- * 创建并校验 Bun SQL 客户端
258
- * - 连接成功后返回 SQL 实例,失败会自动 close 并抛出
259
- * @param {object} options 传给 new SQL 的参数(如 { max: 1, bigint: true })
260
- */
261
-
262
- // 组合最终数据库连接串:
263
- // - 基于 DB_* 环境变量构建(DB_TYPE/DB_HOST/DB_PORT/DB_USER/DB_PASS/DB_NAME)
264
- // - sqlite: sqlite:<DB_NAME>(文件路径或 :memory:)
265
- // - postgresql: postgres://[user:pass@]host:port/DB_NAME
266
- // - mysql: mysql://[user:pass@]host:port/DB_NAME
267
- export const buildDatabaseUrl = () => {
268
- const type = Env.DB_TYPE || '';
269
- const host = Env.DB_HOST || '';
270
- const port = Env.DB_PORT;
271
- const user = encodeURIComponent(Env.DB_USER || '');
272
- const pass = encodeURIComponent(Env.DB_PASS || '');
273
- const name = Env.DB_NAME || '';
274
-
275
- if (!type) throw new Error('DB_TYPE 未配置');
276
- if (!name && type !== 'sqlite') throw new Error('DB_NAME 未配置');
277
-
278
- if (type === 'sqlite') {
279
- if (!name) throw new Error('DB_NAME 未配置');
280
- return `sqlite://${name}`;
281
- }
282
-
283
- if (type === 'postgresql' || type === 'postgres') {
284
- if (!host || !port) throw new Error('DB_HOST/DB_PORT 未配置');
285
- const auth = user || pass ? `${user}:${pass}@` : '';
286
- return `postgres://${auth}${host}:${port}/${encodeURIComponent(name)}`;
287
- }
288
-
289
- if (type === 'mysql') {
290
- if (!host || !port) throw new Error('DB_HOST/DB_PORT 未配置');
291
- const auth = user || pass ? `${user}:${pass}@` : '';
292
- return `mysql://${auth}${host}:${port}/${encodeURIComponent(name)}`;
293
- }
294
-
295
- throw new Error(`不支持的 DB_TYPE: ${type}`);
296
- };
297
-
298
- export async function createSqlClient(options = {}) {
299
- const finalUrl = buildDatabaseUrl();
300
- let sql = null;
301
- if (Env.DB_TYPE === 'sqlite') {
302
- sql = new SQL(finalUrl);
303
- } else {
304
- sql = new SQL({
305
- url: finalUrl,
306
- max: options.max ?? 1,
307
- bigint: options.bigint ?? true,
308
- ...options
309
- });
310
- }
311
-
312
- try {
313
- // 连接健康检查:按协议选择
314
- let version = '';
315
- if (Env.DB_TYPE === 'sqlite') {
316
- const v = await sql`SELECT sqlite_version() AS version`;
317
- version = v?.[0]?.version;
318
- } else if (Env.DB_TYPE === 'postgresql' || Env.DB_TYPE === 'postgres') {
319
- const v = await sql`SELECT version() AS version`;
320
- version = v?.[0]?.version;
321
- } else {
322
- const v = await sql`SELECT VERSION() AS version`;
323
- version = v?.[0]?.version;
324
- }
325
- Logger.info(`数据库连接成功,version: ${version}`);
326
- return sql;
327
- } catch (error) {
328
- Logger.error('数据库连接测试失败:', error);
329
- try {
330
- await sql.close();
331
- } catch {}
332
- throw error;
333
- }
334
- }