befly 1.1.2 → 1.2.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/config/env.js CHANGED
@@ -21,7 +21,7 @@ export const Env = {
21
21
  LOG_TO_CONSOLE: Number(process.env.LOG_TO_CONSOLE),
22
22
  LOG_MAX_SIZE: Number(process.env.LOG_MAX_SIZE),
23
23
  // 时区
24
- TIMEZONE: process.env.TIMEZONE,
24
+ TZ: process.env.TZ,
25
25
  // 数据库配置
26
26
  MYSQL_ENABLE: Number(process.env.MYSQL_ENABLE),
27
27
  MYSQL_HOST: process.env.MYSQL_HOST,
package/main.js CHANGED
@@ -10,6 +10,19 @@ import { Crypto2 } from './utils/crypto.js';
10
10
  import { XMLParser } from './libs/xml/XMLParser.js';
11
11
  import { isEmptyObject, isType, pickFields, sortPlugins, RYes, RNo, filename2, dirname2 } from './utils/util.js';
12
12
 
13
+ const setCorsOptions = (req) => {
14
+ return {
15
+ headers: {
16
+ 'Access-Control-Allow-Origin': Env.ALLOWED_ORIGIN || req.headers.get('origin') || '*',
17
+ 'Access-Control-Allow-Methods': Env.ALLOWED_METHODS || 'GET, POST, PUT, DELETE, OPTIONS',
18
+ 'Access-Control-Allow-Headers': Env.ALLOWED_HEADERS || 'Content-Type, Authorization, authorization, token',
19
+ 'Access-Control-Expose-Headers': Env.EXPOSE_HEADERS || 'Content-Range, X-Content-Range, Authorization, authorization, token',
20
+ 'Access-Control-Max-Age': Env.MAX_AGE || 86400,
21
+ 'Access-Control-Allow-Credentials': Env.ALLOW_CREDENTIALS || 'true'
22
+ }
23
+ };
24
+ };
25
+
13
26
  class Befly {
14
27
  constructor(options = {}) {
15
28
  this.apiRoutes = new Map();
@@ -316,9 +329,13 @@ class Befly {
316
329
  },
317
330
  '/api/*': async (req) => {
318
331
  try {
332
+ const corsOptions = setCorsOptions(req);
319
333
  // 直接返回options请求
320
334
  if (req.method === 'OPTIONS') {
321
- return new Response();
335
+ return new Response(null, {
336
+ status: 204,
337
+ headers: corsOptions.headers
338
+ });
322
339
  }
323
340
  // 初始化请求数据存储
324
341
  const ctx = {
@@ -416,17 +433,23 @@ class Befly {
416
433
 
417
434
  // 登录验证 auth 有3种值 分别为 true、false、['admin', 'user']
418
435
  if (api.auth === true && !ctx.user.id) {
419
- return Response.json(RNo('未登录'));
436
+ return Response.json(RNo('未登录'), {
437
+ headers: corsOptions.headers
438
+ });
420
439
  }
421
440
 
422
441
  if (api.auth && api.auth !== true && ctx.user.role !== api.auth) {
423
- return Response.json(RNo('没有权限'));
442
+ return Response.json(RNo('没有权限'), {
443
+ headers: corsOptions.headers
444
+ });
424
445
  }
425
446
 
426
447
  // 参数验证
427
448
  const validate = validator.validate(ctx.body, api.fields, api.required);
428
449
  if (validate.code !== 0) {
429
- return Response.json(RNo('无效的请求参数格式', validate.fields));
450
+ return Response.json(RNo('无效的请求参数格式', validate.fields), {
451
+ headers: corsOptions.headers
452
+ });
430
453
  }
431
454
 
432
455
  // 执行函数
@@ -434,9 +457,13 @@ class Befly {
434
457
 
435
458
  // 返回数据
436
459
  if (result && typeof result === 'object' && 'code' in result) {
437
- return Response.json(result);
460
+ return Response.json(result, {
461
+ headers: corsOptions.headers
462
+ });
438
463
  } else {
439
- return new Response(result);
464
+ return new Response(result, {
465
+ headers: corsOptions.headers
466
+ });
440
467
  }
441
468
  } catch (error) {
442
469
  Logger.error({
@@ -445,10 +472,13 @@ class Befly {
445
472
  stack: error.stack,
446
473
  url: req.url
447
474
  });
448
- return Response.json(RNo('内部服务器错误'));
475
+ return Response.json(RNo('内部服务器错误'), {
476
+ headers: corsOptions.headers
477
+ });
449
478
  }
450
479
  },
451
480
  '/*': async (req) => {
481
+ const corsOptions = setCorsOptions(req);
452
482
  const url = new URL(req.url);
453
483
  const filePath = path.join(process.cwd(), 'public', url.pathname);
454
484
 
@@ -457,14 +487,19 @@ class Befly {
457
487
  if (await file.exists()) {
458
488
  return new Response(file, {
459
489
  headers: {
460
- 'Content-Type': file.type || 'application/octet-stream'
490
+ 'Content-Type': file.type || 'application/octet-stream',
491
+ ...corsOptions.headers
461
492
  }
462
493
  });
463
494
  } else {
464
- return Response.json(RNo('文件未找到'));
495
+ return Response.json(RNo('文件未找到'), {
496
+ headers: corsOptions.headers
497
+ });
465
498
  }
466
499
  } catch (error) {
467
- return Response.json(RNo('文件读取失败'));
500
+ return Response.json(RNo('文件读取失败'), {
501
+ headers: corsOptions.headers
502
+ });
468
503
  }
469
504
  },
470
505
  ...(this.appOptions.routes || {})
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "befly",
3
- "version": "1.1.2",
3
+ "version": "1.2.0",
4
4
  "description": "Buma - 为 Bun 专属打造的 API 接口框架核心引擎",
5
5
  "type": "module",
6
6
  "private": false,
@@ -49,5 +49,5 @@
49
49
  "README.md",
50
50
  "vitest.config.js"
51
51
  ],
52
- "gitHead": "d1a26096e103683507f21e2932cb0305e034c2d0"
52
+ "gitHead": "58489e0447cd7388d4dbe4d525ebb86ddea343e2"
53
53
  }
package/plugins/db.js CHANGED
@@ -1,6 +1,5 @@
1
1
  import { Env } from '../config/env.js';
2
2
  import { Logger } from '../utils/logger.js';
3
- import { Crud } from '../utils/curd.js';
4
3
 
5
4
  export default {
6
5
  after: ['_redis'],
package/utils/validate.js CHANGED
@@ -134,7 +134,13 @@ export class Validator {
134
134
  }
135
135
 
136
136
  if (spec && spec.trim() !== '') {
137
- return this.validateNumberExpression(value, name, spec, fieldName);
137
+ // 检查是否为枚举格式
138
+ if (this.isEnumSpec(spec)) {
139
+ return this.validateEnum(value, spec, name, fieldName, 'number');
140
+ } else {
141
+ // 原有的表达式验证逻辑
142
+ return this.validateNumberExpression(value, name, spec, fieldName);
143
+ }
138
144
  }
139
145
 
140
146
  return null;
@@ -143,6 +149,46 @@ export class Validator {
143
149
  }
144
150
  }
145
151
 
152
+ /**
153
+ * 判断是否为枚举格式
154
+ */
155
+ isEnumSpec(spec) {
156
+ return spec && spec.startsWith('enum#');
157
+ }
158
+
159
+ /**
160
+ * 验证枚举值
161
+ */
162
+ validateEnum(value, spec, name, fieldName, type) {
163
+ // 解析枚举值 "enum#1,2,3" -> ["1", "2", "3"]
164
+ const enumValues = spec
165
+ .substring(5)
166
+ .split(',')
167
+ .map((v) => v.trim());
168
+
169
+ if (type === 'number') {
170
+ // 数字类型:转换枚举值为数字进行比较
171
+ const numericEnumValues = enumValues.map((v) => parseFloat(v));
172
+ if (!numericEnumValues.includes(value)) {
173
+ return `${name}(${fieldName})必须是以下值之一: ${enumValues.join(', ')}`;
174
+ }
175
+ } else if (type === 'string') {
176
+ // 字符串类型:直接比较
177
+ if (!enumValues.includes(value)) {
178
+ return `${name}(${fieldName})必须是以下值之一: ${enumValues.join(', ')}`;
179
+ }
180
+ } else if (type === 'array') {
181
+ // 数组类型:检查每个元素是否在枚举值中
182
+ for (const item of value) {
183
+ if (!enumValues.includes(String(item))) {
184
+ return `${name}(${fieldName})中的元素"${item}"必须是以下值之一: ${enumValues.join(', ')}`;
185
+ }
186
+ }
187
+ }
188
+
189
+ return null;
190
+ }
191
+
146
192
  /**
147
193
  * 验证数字表达式
148
194
  */
@@ -196,9 +242,15 @@ export class Validator {
196
242
  }
197
243
 
198
244
  if (spec && spec.trim() !== '') {
199
- const regExp = new RegExp(spec);
200
- if (!regExp.test(value)) {
201
- return `${name}(${fieldName})格式不正确`;
245
+ // 检查是否为枚举格式
246
+ if (this.isEnumSpec(spec)) {
247
+ return this.validateEnum(value, spec, name, fieldName, 'string');
248
+ } else {
249
+ // 原有的正则表达式验证逻辑
250
+ const regExp = new RegExp(spec);
251
+ if (!regExp.test(value)) {
252
+ return `${name}(${fieldName})格式不正确`;
253
+ }
202
254
  }
203
255
  }
204
256
 
@@ -226,10 +278,16 @@ export class Validator {
226
278
  }
227
279
 
228
280
  if (spec && spec.trim() !== '') {
229
- const regExp = new RegExp(spec);
230
- for (const item of value) {
231
- if (!regExp.test(String(item))) {
232
- return `${name}(${fieldName})中的元素"${item}"格式不正确`;
281
+ // 检查是否为枚举格式
282
+ if (this.isEnumSpec(spec)) {
283
+ return this.validateEnum(value, spec, name, fieldName, 'array');
284
+ } else {
285
+ // 原有的正则表达式验证逻辑
286
+ const regExp = new RegExp(spec);
287
+ for (const item of value) {
288
+ if (!regExp.test(String(item))) {
289
+ return `${name}(${fieldName})中的元素"${item}"格式不正确`;
290
+ }
233
291
  }
234
292
  }
235
293
  }
package/plugins/cors.js DELETED
@@ -1,15 +0,0 @@
1
- export default {
2
- after: ['_redis', '_db'],
3
- async onGet(befly, ctx, req) {
4
- // 设置 CORS 头部
5
- req.headers.set('Access-Control-Allow-Origin', req.headers.get('origin'));
6
- req.headers.set('Access-Control-Allow-Methods', 'POST,GET,OPTIONS');
7
- req.headers.set('Access-Control-Allow-Headers', 'Content-Type, Authorization');
8
- req.headers.set('Access-Control-Allow-Credentials', 'true');
9
-
10
- // 处理预检请求
11
- if (req.method === 'OPTIONS') {
12
- req.status = 204;
13
- }
14
- }
15
- };