befly 1.1.3 → 1.2.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/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();
@@ -306,19 +319,28 @@ class Befly {
306
319
  hostname: Env.APP_HOST,
307
320
  routes: {
308
321
  '/': async (req) => {
309
- return Response.json({
310
- code: 0,
311
- msg: 'Befly 接口服务已启动',
312
- data: {
313
- mode: Env.NODE_ENV
322
+ return Response.json(
323
+ {
324
+ code: 0,
325
+ msg: 'Befly 接口服务已启动',
326
+ data: {
327
+ mode: Env.NODE_ENV
328
+ }
329
+ },
330
+ {
331
+ headers: corsOptions.headers
314
332
  }
315
- });
333
+ );
316
334
  },
317
335
  '/api/*': async (req) => {
318
336
  try {
337
+ const corsOptions = setCorsOptions(req);
319
338
  // 直接返回options请求
320
339
  if (req.method === 'OPTIONS') {
321
- return new Response();
340
+ return new Response(null, {
341
+ status: 204,
342
+ headers: corsOptions.headers
343
+ });
322
344
  }
323
345
  // 初始化请求数据存储
324
346
  const ctx = {
@@ -334,7 +356,10 @@ class Befly {
334
356
  const api = this.apiRoutes.get(apiPath);
335
357
 
336
358
  // 接口不存在
337
- if (!api) return Response.json(RNo('接口不存在'));
359
+ if (!api)
360
+ return Response.json(RNo('接口不存在'), {
361
+ headers: corsOptions.headers
362
+ });
338
363
 
339
364
  const authHeader = req.headers.get('authorization');
340
365
  if (authHeader && authHeader.startsWith('Bearer ')) {
@@ -386,7 +411,9 @@ class Befly {
386
411
  stack: err.stack
387
412
  });
388
413
 
389
- return Response.json(RNo('无效的请求参数格式'));
414
+ return Response.json(RNo('无效的请求参数格式'), {
415
+ headers: corsOptions.headers
416
+ });
390
417
  }
391
418
  }
392
419
 
@@ -416,17 +443,23 @@ class Befly {
416
443
 
417
444
  // 登录验证 auth 有3种值 分别为 true、false、['admin', 'user']
418
445
  if (api.auth === true && !ctx.user.id) {
419
- return Response.json(RNo('未登录'));
446
+ return Response.json(RNo('未登录'), {
447
+ headers: corsOptions.headers
448
+ });
420
449
  }
421
450
 
422
451
  if (api.auth && api.auth !== true && ctx.user.role !== api.auth) {
423
- return Response.json(RNo('没有权限'));
452
+ return Response.json(RNo('没有权限'), {
453
+ headers: corsOptions.headers
454
+ });
424
455
  }
425
456
 
426
457
  // 参数验证
427
458
  const validate = validator.validate(ctx.body, api.fields, api.required);
428
459
  if (validate.code !== 0) {
429
- return Response.json(RNo('无效的请求参数格式', validate.fields));
460
+ return Response.json(RNo('无效的请求参数格式', validate.fields), {
461
+ headers: corsOptions.headers
462
+ });
430
463
  }
431
464
 
432
465
  // 执行函数
@@ -434,9 +467,13 @@ class Befly {
434
467
 
435
468
  // 返回数据
436
469
  if (result && typeof result === 'object' && 'code' in result) {
437
- return Response.json(result);
470
+ return Response.json(result, {
471
+ headers: corsOptions.headers
472
+ });
438
473
  } else {
439
- return new Response(result);
474
+ return new Response(result, {
475
+ headers: corsOptions.headers
476
+ });
440
477
  }
441
478
  } catch (error) {
442
479
  Logger.error({
@@ -445,10 +482,13 @@ class Befly {
445
482
  stack: error.stack,
446
483
  url: req.url
447
484
  });
448
- return Response.json(RNo('内部服务器错误'));
485
+ return Response.json(RNo('内部服务器错误'), {
486
+ headers: corsOptions.headers
487
+ });
449
488
  }
450
489
  },
451
490
  '/*': async (req) => {
491
+ const corsOptions = setCorsOptions(req);
452
492
  const url = new URL(req.url);
453
493
  const filePath = path.join(process.cwd(), 'public', url.pathname);
454
494
 
@@ -457,14 +497,19 @@ class Befly {
457
497
  if (await file.exists()) {
458
498
  return new Response(file, {
459
499
  headers: {
460
- 'Content-Type': file.type || 'application/octet-stream'
500
+ 'Content-Type': file.type || 'application/octet-stream',
501
+ ...corsOptions.headers
461
502
  }
462
503
  });
463
504
  } else {
464
- return Response.json(RNo('文件未找到'));
505
+ return Response.json(RNo('文件未找到'), {
506
+ headers: corsOptions.headers
507
+ });
465
508
  }
466
509
  } catch (error) {
467
- return Response.json(RNo('文件读取失败'));
510
+ return Response.json(RNo('文件读取失败'), {
511
+ headers: corsOptions.headers
512
+ });
468
513
  }
469
514
  },
470
515
  ...(this.appOptions.routes || {})
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "befly",
3
- "version": "1.1.3",
3
+ "version": "1.2.1",
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": "d5858fdd308cb3c78fe9c76d490f6531c8cbdbbe"
52
+ "gitHead": "63bf1b29c48afde393f038181900222e6b124fc0"
53
53
  }
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
- };