midway-fatcms 0.0.1-beta.82 → 0.0.2

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 (66) hide show
  1. package/README.md +401 -2
  2. package/dist/controller/gateway/DocGatewayController.js +7 -5
  3. package/dist/interface.d.ts +1 -0
  4. package/dist/libs/crud-pro/models/Transaction.js +1 -1
  5. package/dist/libs/crud-pro/models/TransactionMySQL.js +1 -1
  6. package/dist/libs/crud-pro/utils/MixinUtils.js +19 -19
  7. package/dist/libs/utils/errorToString.d.ts +18 -0
  8. package/dist/libs/utils/errorToString.js +26 -4
  9. package/dist/libs/utils/fatcms-request.d.ts +15 -0
  10. package/dist/libs/utils/fatcms-request.js +58 -1
  11. package/dist/libs/utils/format-url.d.ts +16 -1
  12. package/dist/libs/utils/format-url.js +19 -2
  13. package/dist/libs/utils/ordernum-utils.d.ts +20 -0
  14. package/dist/libs/utils/ordernum-utils.js +25 -3
  15. package/dist/middleware/forbidden.middleware.d.ts +65 -1
  16. package/dist/middleware/forbidden.middleware.js +273 -20
  17. package/dist/middleware/global.middleware.d.ts +30 -0
  18. package/dist/middleware/global.middleware.js +41 -4
  19. package/dist/middleware/permission.middleware.d.ts +70 -0
  20. package/dist/middleware/permission.middleware.js +73 -2
  21. package/dist/middleware/rediscache.middleware.d.ts +26 -1
  22. package/dist/middleware/rediscache.middleware.js +41 -4
  23. package/dist/middleware/redislock.middleware.d.ts +28 -6
  24. package/dist/middleware/redislock.middleware.js +40 -6
  25. package/dist/middleware/tx.middleware.d.ts +52 -6
  26. package/dist/middleware/tx.middleware.js +79 -36
  27. package/dist/service/VisitStatService.js +8 -5
  28. package/dist/service/anyapi/AnyApiService.js +1 -1
  29. package/dist/service/base/ApiBaseService.d.ts +10 -3
  30. package/dist/service/base/ApiBaseService.js +18 -8
  31. package/dist/service/base/cache/DiskCache.js +8 -1
  32. package/dist/service/curd/fixSoftDelete.js +14 -4
  33. package/dist/service/proxyapi/ProxyApiService.js +10 -7
  34. package/dist/views/404_app.html +99 -22
  35. package/dist/views/404_workbench.html +95 -22
  36. package/package.json +1 -1
  37. package/src/controller/gateway/DocGatewayController.ts +7 -5
  38. package/src/interface.ts +1 -0
  39. package/src/libs/crud-pro/models/Transaction.ts +1 -1
  40. package/src/libs/crud-pro/models/TransactionMySQL.ts +1 -1
  41. package/src/libs/crud-pro/utils/MixinUtils.ts +20 -19
  42. package/src/libs/utils/errorToString.ts +27 -5
  43. package/src/libs/utils/fatcms-request.ts +62 -0
  44. package/src/libs/utils/format-url.ts +26 -12
  45. package/src/libs/utils/ordernum-utils.ts +25 -3
  46. package/src/middleware/forbidden.middleware.ts +288 -20
  47. package/src/middleware/global.middleware.ts +43 -4
  48. package/src/middleware/permission.middleware.ts +73 -2
  49. package/src/middleware/rediscache.middleware.ts +41 -5
  50. package/src/middleware/redislock.middleware.ts +44 -10
  51. package/src/middleware/tx.middleware.ts +82 -24
  52. package/src/models/userSession.ts +2 -0
  53. package/src/service/VisitStatService.ts +8 -5
  54. package/src/service/anyapi/AnyApiSandboxService.ts +1 -1
  55. package/src/service/anyapi/AnyApiService.ts +1 -1
  56. package/src/service/base/ApiBaseService.ts +19 -8
  57. package/src/service/base/BaseService.ts +2 -2
  58. package/src/service/base/cache/DiskCache.ts +17 -9
  59. package/src/service/crudstd/CrudStdActionService.ts +2 -3
  60. package/src/service/crudstd/CrudStdService.ts +16 -16
  61. package/src/service/curd/CrudProQuick.ts +1 -1
  62. package/src/service/curd/CurdMixByDictService.ts +1 -1
  63. package/src/service/curd/fixSoftDelete.ts +20 -6
  64. package/src/service/proxyapi/ProxyApiService.ts +11 -8
  65. package/src/views/404_app.html +99 -22
  66. package/src/views/404_workbench.html +95 -22
package/README.md CHANGED
@@ -1,8 +1,407 @@
1
- #midway-fatcms
1
+ # Midway FatCMS
2
2
 
3
+ **企业级低代码/零代码开发平台**
3
4
 
5
+ 基于 Midway.js 框架的多租户 SaaS 低代码平台,提供 API 网关、CRUD-Pro 引擎、工作流、文档管理等企业级功能。
4
6
 
5
- * 加密字段:
7
+ ## 项目简介
8
+
9
+ Midway FatCMS 是一个功能完整的企业级低代码开发平台,旨在通过配置化的方式快速构建企业应用系统。系统核心特性包括:
10
+
11
+ - **零代码 CRUD 开发**:通过配置自动生成 CRUD 接口
12
+ - **动态 API 管理**:支持 SQL 查询、沙箱脚本、自定义代码三种 API 类型
13
+ - **API 网关与代理**:反向代理、负载均衡、流量控制
14
+ - **多租户架构**:基于域名的租户隔离,支持 SaaS 部署
15
+ - **工作流引擎**:灵活的业务流程管理
16
+ - **文档管理系统**:支持文档库、版本控制、权限管理
17
+ - **文件服务**:OSS 对接、上传下载、预览管理
18
+
19
+ ## 核心功能
20
+
21
+ ### 1. CRUD-Pro 引擎
22
+
23
+ **智能 CRUD 生成引擎**,提供配置化的数据库操作能力:
24
+
25
+ - **标准化 CRUD**:基于表配置自动生成增删改查接口
26
+ - **复杂查询支持**:分页、排序、筛选、关联查询
27
+ - **字段级权限**:支持字段可见性、只读、必填等控制
28
+ - **数据验证**:内置 20+ 种验证器(邮箱、手机号、日期等)
29
+ - **关联关系**:自动处理一对一、一对多、多对多关系
30
+ - **软删除**:支持逻辑删除和物理删除
31
+ - **审计日志**:自动记录创建人、修改人、时间戳
32
+ - **多数据库支持**:MySQL、PostgreSQL、SQL Server
33
+
34
+ **应用场景**:
35
+ - 快速搭建后台管理系统
36
+ - 数据管理平台
37
+ - 报表查询系统
38
+ - 业务配置中心
39
+
40
+ ### 2. 动态 API 管理(Any API)
41
+
42
+ **三种 API 创建方式**,满足不同复杂度需求:
43
+
44
+ #### 2.1 SQL 查询 API
45
+ - 直接编写 SQL,自动转换为 REST API
46
+ - 支持参数化查询、防 SQL 注入
47
+ - 适用于复杂报表、数据分析
48
+
49
+ #### 2.2 沙箱脚本 API
50
+ - 使用 Node.js VM 沙箱执行自定义代码
51
+ - 提供安全的运行环境
52
+ - 内置工具库(axios、lodash、moment 等)
53
+ - 访问数据库、Redis、OSS 等资源
54
+
55
+ #### 2.3 自定义代码 API
56
+ - 完全自定义的业务逻辑
57
+ - 类似 Serverless Function
58
+ - 热更新,无需重启服务
59
+
60
+ **安全控制**:
61
+ - 权限验证(登录、角色、功能点)
62
+ - 限流控制(令牌桶算法)
63
+ - 服务降级开关
64
+ - 超时控制
65
+ - IP 黑白名单
66
+
67
+ **应用场景**:
68
+ - 快速开发业务接口
69
+ - 数据聚合 API
70
+ - 第三方集成
71
+ - 微服务编排
72
+
73
+ ### 3. API 网关与代理
74
+
75
+ **企业级 API 网关**,提供完整的代理转发能力:
76
+
77
+ #### 负载均衡策略
78
+ - 轮询(Round Robin)
79
+ - 加权轮询(Weighted Round Robin)
80
+ - 随机(Random)
81
+ - 加权随机(Weighted Random)
82
+ - IP Hash(同一 IP 固定路由)
83
+ - 用户 Hash(同一用户固定路由)
84
+ - 会话 Hash(同一会话固定路由)
85
+
86
+ #### 流量控制
87
+ - 请求限流
88
+ - 超时控制
89
+ - 重试机制
90
+ - 熔断降级
91
+
92
+ #### 安全特性
93
+ - 真实 IP 透传
94
+ - 用户上下文传递
95
+ - Host/Origin 修改
96
+ - 请求/响应头控制
97
+
98
+ **应用场景**:
99
+ - 微服务网关
100
+ - 灰度发布
101
+ - A/B 测试
102
+ - 流量分发
103
+ - 服务聚合
104
+
105
+ ### 4. 多租户架构
106
+
107
+ **工作台(Workbench)系统**,实现完整的多租户隔离:
108
+
109
+ #### 租户隔离机制
110
+ - **域名识别**:根据请求域名自动识别租户
111
+ - **数据隔离**:租户数据完全隔离
112
+ - **权限隔离**:用户、角色、权限独立管理
113
+ - **资源隔离**:文件、配置、应用独立
114
+
115
+ #### 灵活配置
116
+ - 支持主域名 + 2 个备用域名
117
+ - 通配符域名支持
118
+ - 租户模板机制
119
+ - 个性化配置
120
+
121
+ **应用场景**:
122
+ - SaaS 平台
123
+ - 多品牌运营
124
+ - 集团企业
125
+ - 白标产品
126
+
127
+ ### 5. 工作流引擎
128
+
129
+ **灵活的业务流程管理**:
130
+
131
+ - 流程定义与配置
132
+ - 流程实例管理
133
+ - 任务分配与审批
134
+ - 流程状态跟踪
135
+ - 流转记录查询
136
+
137
+ **应用场景**:
138
+ - 审批流程
139
+ - 业务流转
140
+ - 状态机管理
141
+
142
+ ### 6. 文档管理系统
143
+
144
+ **企业级文档管理**:
145
+
146
+ - **文档库管理**:支持多个文档库
147
+ - **目录结构**:树形目录组织
148
+ - **版本控制**:文档版本管理
149
+ - **访问统计**:PV/UV 统计(IP 去重)
150
+ - **权限控制**:文档级权限管理
151
+
152
+ **应用场景**:
153
+ - API 文档
154
+ - 产品文档
155
+ - 知识库
156
+ - 帮助中心
157
+
158
+ ### 7. 文件服务
159
+
160
+ **完整的文件管理能力**:
161
+
162
+ - **上传下载**:支持单文件、多文件上传
163
+ - **存储管理**:OSS 对接(公有桶、私有桶)
164
+ - **文件预览**:图片、PDF、Office 文档
165
+ - **访问控制**:临时链接、权限验证
166
+ - **分类管理**:文件分类、标签
167
+
168
+ ### 8. 系统管理
169
+
170
+ #### 用户权限管理
171
+ - 用户账号管理
172
+ - 角色管理
173
+ - 功能点管理
174
+ - 权限分配
175
+
176
+ #### 应用管理
177
+ - 应用列表
178
+ - 应用页面管理
179
+ - 菜单管理
180
+ - 低代码模板
181
+
182
+ #### 系统配置
183
+ - 数据字典
184
+ - 系统参数
185
+ - 枚举管理
186
+ - 定时任务
187
+
188
+ #### 监控运维
189
+ - 访问统计
190
+ - 操作日志
191
+ - 系统信息
192
+ - 部署管理
193
+
194
+ ## 技术架构
195
+
196
+ ### 技术栈
197
+
198
+ **后端框架**
199
+ - Midway.js 3.x(企业级 Node.js 框架)
200
+ - Koa(Web 框架)
201
+ - TypeScript(类型安全)
202
+
203
+ **数据存储**
204
+ - MySQL 2(主数据库)
205
+ - PostgreSQL 8(可选)
206
+ - SQL Server 11(可选)
207
+ - Redis(缓存、锁、会话)
208
+
209
+ **文件存储**
210
+ - 阿里云 OSS
211
+ - 兼容 S3 协议的对象存储
212
+
213
+ **工具库**
214
+ - Lodash(工具函数)
215
+ - Moment(日期处理)
216
+ - Axios(HTTP 客户端)
217
+ - EJS(模板引擎)
218
+ - Nodemailer(邮件发送)
219
+ - LRU Cache(内存缓存)
220
+
221
+ ### 核心中间件
222
+
223
+ #### 全局中间件
224
+ - **用户会话管理**:登录状态、权限信息
225
+ - **多租户隔离**:域名识别、数据隔离
226
+ - **事务管理**:自动事务控制
227
+ - **日志追踪**:请求级日志
228
+ - **响应格式化**:统一响应格式
229
+ - **调试信息注入**:开发环境调试
230
+ - **真实 IP 获取**:代理环境下获取真实 IP
231
+
232
+ #### 权限中间件
233
+ - **登录检查**(checkLogin)
234
+ - **角色检查**(checkRole)
235
+ - **权限检查**(checkPermission)
236
+
237
+ #### 安全中间件
238
+ - **黑名单拦截**(Forbidden):防止路径遍历、敏感文件访问
239
+ - **Redis 分布式锁**(RedisLock):防止并发冲突
240
+ - **Redis 缓存**(RedisCache):接口级缓存
241
+
242
+ #### 事务中间件
243
+ - **自动事务**(Transaction):开启、提交、回滚
244
+
245
+ ### 安全特性
246
+
247
+ #### 访问控制
248
+ - JWT Token 认证
249
+ - 会话管理
250
+ - 超级管理员机制
251
+ - 细粒度权限控制
252
+
253
+ #### 攻击防护
254
+ - SQL 注入防护
255
+ - XSS 防护
256
+ - CSRF Token
257
+ - 路径遍历防护
258
+ - User-Agent 黑名单(扫描工具检测)
259
+ - IP 限流
260
+
261
+ #### 数据安全
262
+ - AES-128-CBC 加密
263
+ - 非对称加密支持
264
+ - 敏感字段加密存储
265
+ - 密码加盐存储
266
+
267
+ ## 应用场景
268
+
269
+ ### 1. 企业后台系统
270
+ - 快速搭建管理后台
271
+ - 业务系统配置中心
272
+ - 内部工具平台
273
+
274
+ ### 2. SaaS 平台
275
+ - 多租户应用
276
+ - 白标产品
277
+ - 行业解决方案
278
+
279
+ ### 3. API 平台
280
+ - API 管理与网关
281
+ - 数据开放平台
282
+ - 微服务聚合
283
+
284
+ ### 4. 低代码平台
285
+ - 业务系统快速开发
286
+ - 表单驱动应用
287
+ - 数据分析平台
288
+
289
+ ### 5. 内容管理系统
290
+ - 文档管理
291
+ - 知识库
292
+ - 内容发布平台
293
+
294
+ ## 快速开始
295
+
296
+ ### 环境要求
297
+
298
+ - Node.js >= 10
299
+ - MySQL >= 5.7 / PostgreSQL >= 8 / SQL Server >= 11
300
+ - Redis >= 3.0
301
+
302
+ ### 安装依赖
303
+
304
+ ```bash
305
+ npm install
306
+ ```
307
+
308
+ ### 配置
309
+
310
+ 1. 配置数据库连接(`src/config/config.default.ts`)
311
+ 2. 配置 Redis 连接
312
+ 3. 配置 OSS(可选)
313
+ 4. 配置工作台信息
314
+
315
+ ### 数据库初始化
316
+
317
+ ```sql
318
+ -- 执行初始化 SQL
319
+ source ddl/2025-12-20.sql
320
+ ```
321
+
322
+ ### 启动服务
323
+
324
+ ```bash
325
+ # 开发模式
326
+ npm run dev
327
+
328
+ # 生产构建
329
+ npm run build
330
+
331
+ # 生产运行
332
+ npm start
333
+ ```
334
+
335
+ ### 代码检查
336
+
337
+ ```bash
338
+ # 检查代码规范
339
+ npm run lint
340
+
341
+ # 自动修复
342
+ npm run lint:fix
343
+ ```
344
+
345
+ ### 测试
346
+
347
+ ```bash
348
+ # 运行测试
349
+ npm test
350
+
351
+ # 测试覆盖率
352
+ npm run cov
353
+ ```
354
+
355
+ ## 工具接口
356
+
357
+ ### 加密工具
358
+
359
+ ```
6
360
  http://127.0.0.1:7002/ns/api/helpers/cryptoAes128CBC?input=123
361
+ ```
362
+
363
+ ## 项目结构
364
+
365
+ ```
366
+ midway-fatcms/
367
+ ├── src/
368
+ │ ├── config/ # 配置文件
369
+ │ ├── controller/ # 控制器
370
+ │ │ ├── base/ # 基础控制器
371
+ │ │ ├── gateway/ # 网关接口
372
+ │ │ ├── manage/ # 管理接口
373
+ │ │ ├── myinfo/ # 个人信息
374
+ │ │ └── render/ # 渲染接口
375
+ │ ├── filter/ # 异常过滤器
376
+ │ ├── libs/ # 核心库
377
+ │ │ ├── crud-pro/ # CRUD-Pro 引擎
378
+ │ │ ├── global-config/ # 全局配置
379
+ │ │ └── utils/ # 工具函数
380
+ │ ├── middleware/ # 中间件
381
+ │ ├── models/ # 数据模型
382
+ │ ├── schedule/ # 定时任务
383
+ │ ├── service/ # 业务服务
384
+ │ │ ├── anyapi/ # 动态 API
385
+ │ │ ├── asyncTask/ # 异步任务
386
+ │ │ ├── base/ # 基础服务
387
+ │ │ ├── crudstd/ # CRUD 标准
388
+ │ │ ├── curd/ # CRUD 服务
389
+ │ │ ├── flow/ # 工作流
390
+ │ │ └── proxyapi/ # 代理 API
391
+ │ └── views/ # 视图模板
392
+ ├── ddl/ # 数据库脚本
393
+ ├── scripts/ # 脚本工具
394
+ └── package.json # 项目配置
395
+ ```
396
+
397
+ ## 版本
398
+
399
+ 当前版本:`0.0.1-beta.82`
400
+
401
+ ## 许可证
402
+
403
+ MIT License
7
404
 
405
+ ## 作者
8
406
 
407
+ 企业级低代码开发团队
@@ -20,12 +20,14 @@ const global_config_1 = require("../../libs/global-config/global-config");
20
20
  const keys_1 = require("../../libs/crud-pro/models/keys");
21
21
  const WorkbenchService_1 = require("../../service/WorkbenchService");
22
22
  const common_dto_1 = require("../../libs/utils/common-dto");
23
+ const fatcms_request_1 = require("../../libs/utils/fatcms-request");
23
24
  function checkIsNumber(value, name) {
24
- if (!value) {
25
- throw `${name}参数不存在`;
25
+ if (value === null || value === '' || typeof value === 'undefined') {
26
+ throw new Error(`${name}参数不存在`);
26
27
  }
27
- if (!parseInt(value)) {
28
- throw `${name}参数不是一个整数`;
28
+ const num = parseInt(value, 10);
29
+ if (isNaN(num)) {
30
+ throw new Error(`${name}参数不是一个整数`);
29
31
  }
30
32
  }
31
33
  /**
@@ -34,7 +36,7 @@ function checkIsNumber(value, name) {
34
36
  */
35
37
  let DocGatewayController = class DocGatewayController extends BaseApiController_1.BaseApiController {
36
38
  async updateDocPV(docId, docLibId, pv) {
37
- const ip = this.ctx.headers['x-real-ip'];
39
+ const ip = (0, fatcms_request_1.getRealIpSafe)(this.ctx);
38
40
  const lockKey = `doc_ip_pv_${docId}_${ip}`;
39
41
  const EXPIRE_TIME = 5 * 60 * 1000; // 在五分钟内,同一个IP地址重复刷新同一篇文章,认为是一个PV
40
42
  const nxRes = await this.redisService.set(lockKey, 1, 'EX', EXPIRE_TIME, 'NX');
@@ -11,6 +11,7 @@ declare module '@midwayjs/core' {
11
11
  workbenchInfo: IWorkbenchEntity;
12
12
  workbenchInfoTools: WorkbenchInfoTools;
13
13
  contextLogger: ContextLogger;
14
+ fatcmsRealIp: string;
14
15
  }
15
16
  }
16
17
  export interface ICacheService {
@@ -38,7 +38,7 @@ class TransactionConnection {
38
38
  }
39
39
  async rollback() {
40
40
  this.isTxRollback = true;
41
- await this.originConnection.commit();
41
+ await this.originConnection.rollback();
42
42
  }
43
43
  async release() {
44
44
  if (this.isReleased) {
@@ -22,7 +22,7 @@ class MySqlConnectionClient {
22
22
  await this.originConnection.commit();
23
23
  }
24
24
  async rollback() {
25
- await this.originConnection.commit();
25
+ await this.originConnection.rollback();
26
26
  }
27
27
  async release() {
28
28
  try {
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.MixinUtils = void 0;
4
4
  const exceptions_1 = require("../exceptions");
5
+ // 至少需要2个字符
5
6
  const FIELD_NAME_REG_EXP = /^[a-zA-Z][0-9a-zA-Z_]+$/;
6
7
  const MixinUtils = {
7
8
  isNil(obj) {
@@ -11,22 +12,22 @@ const MixinUtils = {
11
12
  return !MixinUtils.isNil(obj);
12
13
  },
13
14
  isEmpty(obj) {
14
- if (typeof obj === 'number') {
15
- return false;
15
+ if (typeof obj === 'number' || typeof obj === 'boolean') {
16
+ return false; // ✅ 数字和布尔值永远不为空
16
17
  }
17
- if (obj) {
18
- if (typeof obj === 'string') {
19
- const str = obj;
20
- return str.length === 0;
21
- }
22
- if (Array.isArray(obj)) {
23
- return obj.length === 0;
24
- }
25
- if (typeof obj === 'object') {
26
- return Object.keys(obj).length === 0;
27
- }
18
+ if (obj === null || obj === undefined) {
19
+ return true;
20
+ }
21
+ if (typeof obj === 'string') {
22
+ return obj.length === 0;
28
23
  }
29
- return !obj;
24
+ if (Array.isArray(obj)) {
25
+ return obj.length === 0;
26
+ }
27
+ if (typeof obj === 'object') {
28
+ return Object.keys(obj).length === 0;
29
+ }
30
+ return false;
30
31
  },
31
32
  isNotEmpty(obj) {
32
33
  return !MixinUtils.isEmpty(obj);
@@ -35,12 +36,11 @@ const MixinUtils = {
35
36
  return obj1 === obj2;
36
37
  },
37
38
  equalsIgnoreCase(str1, str2) {
38
- if (str1 && str2) {
39
- const str1Upper = str1.toUpperCase();
40
- const str2Upper = str2.toUpperCase();
41
- return str1Upper === str2Upper;
39
+ // 严格检查类型
40
+ if (typeof str1 !== 'string' || typeof str2 !== 'string') {
41
+ return str1 === str2;
42
42
  }
43
- return !str1 && !str2;
43
+ return str1.toUpperCase() === str2.toUpperCase();
44
44
  },
45
45
  startsWith(str1, str2) {
46
46
  if (typeof str1 === 'string' && typeof str2 === 'string') {
@@ -1,2 +1,20 @@
1
+ /**
2
+ * 将错误对象转换为详细的字符串描述
3
+ *
4
+ * 支持多种错误类型:
5
+ * - Error对象(包含堆栈信息)
6
+ * - 原始值类型(string, number, boolean等)
7
+ * - 普通对象
8
+ * - null/undefined
9
+ *
10
+ * @param error 任意类型的错误对象
11
+ * @returns 格式化后的错误描述字符串
12
+ *
13
+ * @example
14
+ * errorToString(new Error('test')) // 包含堆栈信息
15
+ * errorToString('error message') // 'Error: A primitive value was thrown...'
16
+ * errorToString({ code: 500, msg: 'fail' }) // JSON格式的对象
17
+ * errorToString(null) // 'Error: null was thrown'
18
+ */
1
19
  declare function errorToString(error: any): string;
2
20
  export { errorToString };
@@ -1,6 +1,24 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.errorToString = void 0;
4
+ /**
5
+ * 将错误对象转换为详细的字符串描述
6
+ *
7
+ * 支持多种错误类型:
8
+ * - Error对象(包含堆栈信息)
9
+ * - 原始值类型(string, number, boolean等)
10
+ * - 普通对象
11
+ * - null/undefined
12
+ *
13
+ * @param error 任意类型的错误对象
14
+ * @returns 格式化后的错误描述字符串
15
+ *
16
+ * @example
17
+ * errorToString(new Error('test')) // 包含堆栈信息
18
+ * errorToString('error message') // 'Error: A primitive value was thrown...'
19
+ * errorToString({ code: 500, msg: 'fail' }) // JSON格式的对象
20
+ * errorToString(null) // 'Error: null was thrown'
21
+ */
4
22
  function errorToString(error) {
5
23
  var _a;
6
24
  // 处理非错误对象的情况
@@ -11,11 +29,11 @@ function errorToString(error) {
11
29
  return 'Error: null was thrown';
12
30
  }
13
31
  // 处理原始值类型
14
- const primitiveTypes = ['string', 'number', 'boolean', 'symbol'];
32
+ const primitiveTypes = ['string', 'number', 'boolean', 'symbol', 'bigint'];
15
33
  if (primitiveTypes.includes(typeof error)) {
16
34
  return `Error: A primitive value was thrown\nValue: ${String(error)}`;
17
35
  }
18
- // 处理 Error 对象
36
+ // 处理 Error 对象(必须在处理普通对象之前)
19
37
  if (error instanceof Error) {
20
38
  const errorDetails = [];
21
39
  errorDetails.push(`Error: ${error.name || 'Error'}`);
@@ -24,21 +42,24 @@ function errorToString(error) {
24
42
  errorDetails.push('Stack Trace:');
25
43
  errorDetails.push(error.stack);
26
44
  }
27
- const extraProperties = ['code', 'fileName', 'lineNumber', 'columnNumber'];
45
+ // 常见的错误扩展属性
46
+ const extraProperties = ['code', 'fileName', 'lineNumber', 'columnNumber', 'statusCode', 'status'];
28
47
  extraProperties.forEach(prop => {
29
48
  if (error[prop] !== undefined) {
30
49
  errorDetails.push(`${prop}: ${error[prop]}`);
31
50
  }
32
51
  });
52
+ // 获取自定义属性
33
53
  const customProps = Object.keys(error).filter(key => !['name', 'message', 'stack', ...extraProperties].includes(key));
34
54
  if (customProps.length > 0) {
35
55
  errorDetails.push('Custom Properties:');
36
56
  customProps.forEach(prop => {
37
57
  try {
38
- const value = JSON.stringify(error[prop], null, 2) || String(error[prop]);
58
+ const value = JSON.stringify(error[prop], null, 2);
39
59
  errorDetails.push(` ${prop}: ${value}`);
40
60
  }
41
61
  catch (e) {
62
+ // 处理循环引用或不可序列化的对象
42
63
  errorDetails.push(` ${prop}: [Object cannot be serialized]`);
43
64
  }
44
65
  });
@@ -51,6 +72,7 @@ function errorToString(error) {
51
72
  return `Error: A plain object was thrown\nObject: ${objectDetails}`;
52
73
  }
53
74
  catch (e) {
75
+ // 处理循环引用或不可序列化的对象
54
76
  return `Error: An object was thrown but could not be serialized\nType: ${((_a = error.constructor) === null || _a === void 0 ? void 0 : _a.name) || 'Unknown'}`;
55
77
  }
56
78
  }
@@ -28,3 +28,18 @@ export declare function getCsrfToken(ctx: Context): string;
28
28
  export declare function getExtLocalLoaderPort(ctx: Context): string;
29
29
  export declare function isCsrfTokenValid(ctx: Context): Promise<boolean>;
30
30
  export declare function assertCsrfToken(ctx: Context): Promise<void>;
31
+ /**
32
+ * 安全获取客户端真实IP地址(优先从 userSession 获取)
33
+ *
34
+ * 推荐在业务代码中使用此方法,因为:
35
+ * 1. userSession 中的 IP 在全局中间件初始化时已解析,性能更好
36
+ * 2. 自动处理 userSession 不存在的边界情况
37
+ *
38
+ * @param ctx Koa上下文
39
+ * @returns 客户端真实IP地址
40
+ *
41
+ * @example
42
+ * // 在 Controller/Service 中推荐使用
43
+ * const ip = getRealIpSafe(this.ctx);
44
+ */
45
+ export declare function getRealIpSafe(ctx: Context): string;