@h-ai/cli 0.1.0-alpha.66 → 0.1.0-alpha.67
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/README.en.md +66 -257
- package/README.ja.md +66 -257
- package/README.md +66 -257
- package/dist/{chunk-QYX6V7H2.js → chunk-7DSURTJ4.js} +22 -79
- package/dist/chunk-7DSURTJ4.js.map +1 -0
- package/dist/{chunk-FWN5B54L.js → chunk-AOQ4N657.js} +182 -10
- package/dist/chunk-AOQ4N657.js.map +1 -0
- package/dist/cli-config-templates-6AHNUGBN.js +5 -0
- package/dist/{cli-config-templates-3XSFIXBP.js.map → cli-config-templates-6AHNUGBN.js.map} +1 -1
- package/dist/{cli-skill-templates-TSYEQTCY.js → cli-skill-templates-FC3QRKDO.js} +3 -3
- package/dist/{cli-skill-templates-TSYEQTCY.js.map → cli-skill-templates-FC3QRKDO.js.map} +1 -1
- package/dist/index.js +435 -247
- package/dist/index.js.map +1 -1
- package/dist/{src-RDQ2EIBT.js → src-2V6VP4I5.js} +553 -119
- package/dist/src-2V6VP4I5.js.map +1 -0
- package/package.json +8 -8
- package/templates/README.md +48 -145
- package/templates/apps/admin/src/routes/admin/+layout.svelte.hbs +1 -1
- package/templates/apps/api/.dockerignore.hbs +14 -7
- package/templates/apps/api/.gitignore.hbs +7 -0
- package/templates/apps/api/apps/{{projectName}}-contract/README.md.hbs +23 -2
- package/templates/apps/api/apps/{{projectName}}-service/README.md.hbs +28 -7
- package/templates/apps/api/apps/{{projectName}}-service/package.json.hbs +1 -2
- package/templates/apps/api/hai.build.json.hbs +1 -1
- package/templates/apps/api/images/app.Dockerfile.hbs +35 -0
- package/templates/apps/api/package.json.hbs +5 -0
- package/templates/apps/fullstack/.dockerignore.hbs +14 -9
- package/templates/apps/fullstack/.gitignore.hbs +7 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/README.md.hbs +23 -5
- package/templates/apps/fullstack/apps/{{projectName}}-app/index.html.hbs +1 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/public/runtime-config.js.hbs +4 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/vite.config.ts.hbs +1 -1
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/README.md.hbs +22 -4
- package/templates/apps/fullstack/apps/{{projectName}}-miniapp/README.md.hbs +16 -6
- package/templates/apps/fullstack/apps/{{projectName}}-web/README.md.hbs +23 -5
- package/templates/apps/fullstack/apps/{{projectName}}-web/vite.config.ts.hbs +1 -1
- package/templates/apps/fullstack/e2e/fullstack.spec.ts.hbs +11 -1
- package/templates/apps/fullstack/hai.build.json.hbs +2 -2
- package/templates/apps/fullstack/images/app.Dockerfile.hbs +36 -0
- package/templates/apps/fullstack/images/web.Dockerfile.hbs +34 -0
- package/templates/apps/fullstack/package.json.hbs +2 -0
- package/templates/apps/fullstack/packages/{{projectName}}-contract/README.md.hbs +23 -2
- package/templates/apps/fullstack/packages/{{projectName}}-serv/README.md.hbs +27 -6
- package/templates/apps/fullstack/packages/{{projectName}}-shared/.gitignore.hbs +7 -0
- package/templates/apps/fullstack/packages/{{projectName}}-shared/README.md.hbs +32 -11
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/AppShell.svelte.hbs +2 -2
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/settings.svelte.hbs +3 -3
- package/templates/apps/fullstack/packages/{{projectName}}-shared/tests/api-client.test.ts.hbs +2 -2
- package/templates/apps/h5/src/routes/+layout.svelte.hbs +2 -2
- package/templates/apps/mobile-app/src/App.svelte.hbs +2 -2
- package/templates/apps/mobile-app/src/lib/pages/home.svelte.hbs +2 -2
- package/templates/apps/website/src/routes/+layout.svelte.hbs +3 -3
- package/templates/apps/website/src/routes/services/+page.svelte.hbs +1 -1
- package/templates/base/.dockerignore.hbs +14 -8
- package/templates/base/.gitignore.hbs +7 -0
- package/templates/base/e2e/app.spec.ts.hbs +10 -0
- package/templates/base/hai.build.json.hbs +1 -1
- package/templates/base/{Dockerfile.hbs → images/app.Dockerfile.hbs} +19 -17
- package/templates/base/package.json.hbs +8 -3
- package/templates/base/vite.config.ts.hbs +3 -1
- package/templates/shared/.gitattributes +6 -0
- package/templates/skills/AGENTS.md +3 -3
- package/templates/skills/bridges/admin/AGENTS.md +1 -1
- package/templates/skills/bridges/api/AGENTS.md +1 -1
- package/templates/skills/bridges/fullstack/AGENTS.md.hbs +1 -1
- package/templates/skills/bridges/h5/AGENTS.md +1 -1
- package/templates/skills/bridges/mobile-app/AGENTS.md +1 -1
- package/templates/skills/bridges/website/AGENTS.md +1 -1
- package/templates/skills/hai-api-client/SKILL.md +14 -161
- package/templates/skills/hai-api-client/reference.md +166 -0
- package/templates/skills/hai-api-contract/SKILL.md +15 -273
- package/templates/skills/hai-api-contract/reference.md +278 -0
- package/templates/skills/hai-app-create/SKILL.md +2 -2
- package/templates/skills/hai-app-create/development.md +4 -4
- package/templates/skills/hai-app-create/reference.md +5 -4
- package/templates/skills/hai-app-review/SKILL.md +1 -1
- package/templates/skills/hai-app-tests/SKILL.md +4 -3
- package/templates/skills/hai-app-tests/reference.md +2 -2
- package/templates/skills/hai-app-tests/validation.md +31 -0
- package/templates/skills/hai-audit/SKILL.md +17 -142
- package/templates/skills/hai-audit/reference.md +145 -0
- package/templates/skills/hai-build/SKILL.md +14 -8
- package/templates/skills/hai-build/reference.md +10 -9
- package/templates/skills/hai-cache/SKILL.md +15 -182
- package/templates/skills/hai-cache/reference.md +187 -0
- package/templates/skills/hai-capacitor/SKILL.md +4 -3
- package/templates/skills/hai-capacitor/reference.md +6 -4
- package/templates/skills/hai-ci/SKILL.md +2 -0
- package/templates/skills/hai-core/SKILL.md +3 -3
- package/templates/skills/hai-crypto/SKILL.md +16 -252
- package/templates/skills/hai-crypto/reference.md +256 -0
- package/templates/skills/hai-datapipe/SKILL.md +15 -196
- package/templates/skills/hai-datapipe/reference.md +202 -0
- package/templates/skills/hai-deploy/SKILL.md +20 -55
- package/templates/skills/hai-deploy/middleware.md +5 -11
- package/templates/skills/hai-framework-sync/SKILL.md +2 -2
- package/templates/skills/hai-framework-sync/sync.md +1 -1
- package/templates/skills/hai-iam/SKILL.md +1 -3
- package/templates/skills/hai-kit/SKILL.md +12 -167
- package/templates/skills/hai-kit/reference.md +172 -0
- package/templates/skills/hai-payment/SKILL.md +19 -251
- package/templates/skills/hai-payment/reference.md +256 -0
- package/templates/skills/hai-pr-review/SKILL.md +4 -4
- package/templates/skills/hai-reach/SKILL.md +2 -2
- package/templates/skills/hai-reldb/SKILL.md +1 -1
- package/templates/skills/hai-scheduler/SKILL.md +15 -195
- package/templates/skills/hai-scheduler/reference.md +202 -0
- package/templates/skills/hai-serv/SKILL.md +2 -2
- package/templates/skills/hai-spec-implement/SKILL.md +1 -1
- package/templates/skills/hai-spec-implement/workflow.md +3 -3
- package/templates/skills/hai-storage/SKILL.md +17 -238
- package/templates/skills/hai-storage/reference.md +243 -0
- package/templates/skills/hai-ui/SKILL.md +1 -1
- package/templates/skills/hai-ui/reference.md +9 -5
- package/templates/skills/hai-vecdb/SKILL.md +2 -2
- package/dist/chunk-FWN5B54L.js.map +0 -1
- package/dist/chunk-QYX6V7H2.js.map +0 -1
- package/dist/cli-config-templates-3XSFIXBP.js +0 -5
- package/dist/src-RDQ2EIBT.js.map +0 -1
- package/templates/apps/api/Dockerfile.hbs +0 -35
- package/templates/apps/fullstack/Dockerfile.hbs +0 -57
- /package/templates/apps/fullstack/{docker → images/web}/19-runtime-config.envsh.hbs +0 -0
- /package/templates/apps/fullstack/{docker → images/web}/nginx.conf.hbs +0 -0
- /package/templates/apps/fullstack/{docker → images/web}/runtime-config.js.template.hbs +0 -0
- /package/templates/base/{docker → images/web}/nginx.conf.hbs +0 -0
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
# hai-api-contract 参考
|
|
2
|
+
|
|
3
|
+
> `@h-ai/api-contract` 是 hai-framework 的公共 HTTP API 契约包,使用 oRPC contract + Zod v4 作为唯一接口真相源。本包只描述接口边界,不包含任何 procedure 实现。
|
|
4
|
+
|
|
5
|
+
## 运行环境
|
|
6
|
+
|
|
7
|
+
> ✅ **契约定义包,无服务端业务依赖;Zod/oRPC schema 构造仍有运行时代码。** 可在客户端、服务端、测试环境直接引用。
|
|
8
|
+
|
|
9
|
+
## 适用场景
|
|
10
|
+
|
|
11
|
+
- 定义新领域的 oRPC contract(输入/输出 Schema + HTTP 路由元数据)
|
|
12
|
+
- 组合多个领域 contract 为应用级 contract
|
|
13
|
+
- 向 `@h-ai/serv` 提供 contract(装配为 HTTP API)
|
|
14
|
+
- 向 `@h-ai/api-client` 提供 contract(生成类型安全客户端)
|
|
15
|
+
- 复用内置领域 contract(iam/storage/ai/payment)
|
|
16
|
+
- 复用公共 Schema 工厂(`apiContract.haiResultSchema`、分页 Schema)
|
|
17
|
+
|
|
18
|
+
## 使用步骤
|
|
19
|
+
|
|
20
|
+
### 1. 组合应用级 contract
|
|
21
|
+
|
|
22
|
+
```typescript
|
|
23
|
+
import { apiContract } from '@h-ai/api-contract'
|
|
24
|
+
|
|
25
|
+
// 按需组合,传 false/undefined 的领域不会出现在 contract 或 OpenAPI spec 中
|
|
26
|
+
export const myContract = apiContract.create({
|
|
27
|
+
iam: apiContract.iam,
|
|
28
|
+
storage: apiContract.storage,
|
|
29
|
+
ai: apiContract.ai,
|
|
30
|
+
payment: false, // 禁用领域
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
// myContract.iam.auth.login
|
|
34
|
+
// myContract.storage.presignedUrls.createUpload
|
|
35
|
+
// myContract.ai.chats.createCompletion
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### 2. 组合自定义 contract
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
import { apiContract } from '@h-ai/api-contract'
|
|
42
|
+
|
|
43
|
+
export const myContract = apiContract.create({
|
|
44
|
+
iam: apiContract.iam,
|
|
45
|
+
storage: apiContract.storage,
|
|
46
|
+
payment: false, // 禁用领域(不会出现在 contract 或 OpenAPI spec 中)
|
|
47
|
+
})
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### 3. 定义新领域 contract
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
import { apiContract } from '@h-ai/api-contract'
|
|
54
|
+
import { z } from 'zod'
|
|
55
|
+
|
|
56
|
+
// 输入 Schema
|
|
57
|
+
const WidgetCreateInputSchema = z.object({
|
|
58
|
+
name: z.string().min(1),
|
|
59
|
+
description: z.string().optional(),
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
// 同一 contract 内两个接口复用,因此保留为文件私有常量,不从 schemas 导出。
|
|
63
|
+
const WidgetOutputSchema = apiContract.haiResultSchema(z.object({
|
|
64
|
+
id: z.string(),
|
|
65
|
+
name: z.string(),
|
|
66
|
+
createdAt: z.string(),
|
|
67
|
+
}))
|
|
68
|
+
|
|
69
|
+
// 领域 contract
|
|
70
|
+
export const widgetContract = {
|
|
71
|
+
widget: {
|
|
72
|
+
create: apiContract
|
|
73
|
+
.route({
|
|
74
|
+
method: 'POST',
|
|
75
|
+
path: '/widgets',
|
|
76
|
+
operationId: 'widget.create',
|
|
77
|
+
summary: 'Create widget',
|
|
78
|
+
tags: ['widget'],
|
|
79
|
+
})
|
|
80
|
+
.input(WidgetCreateInputSchema)
|
|
81
|
+
.output(WidgetOutputSchema),
|
|
82
|
+
|
|
83
|
+
getById: apiContract
|
|
84
|
+
.route({
|
|
85
|
+
method: 'GET',
|
|
86
|
+
path: '/widgets/{id}',
|
|
87
|
+
operationId: 'widget.getById',
|
|
88
|
+
summary: 'Get widget by id',
|
|
89
|
+
tags: ['widget'],
|
|
90
|
+
})
|
|
91
|
+
.input(z.object({ id: z.string() }))
|
|
92
|
+
.output(WidgetOutputSchema),
|
|
93
|
+
},
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### 4. 将新 contract 挂入应用级 contract
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
import { apiContract } from '@h-ai/api-contract'
|
|
101
|
+
import { widgetContract } from './widget-contract.js'
|
|
102
|
+
|
|
103
|
+
export const appContract = apiContract.create({
|
|
104
|
+
iam: apiContract.iam,
|
|
105
|
+
widget: widgetContract,
|
|
106
|
+
})
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### 5. 传给 `@h-ai/serv`(服务端)
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
import { serv } from '@h-ai/serv'
|
|
113
|
+
import { appContract } from './app-contract.js'
|
|
114
|
+
import { createMyProcedures } from './procedures/widget.js'
|
|
115
|
+
|
|
116
|
+
const app = serv.createApp({
|
|
117
|
+
contract: appContract,
|
|
118
|
+
procedures: {
|
|
119
|
+
iam: createIamProcedures({ iam }),
|
|
120
|
+
widget: createMyProcedures(),
|
|
121
|
+
},
|
|
122
|
+
http: { apiPrefix: '/api/v1' },
|
|
123
|
+
})
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## 核心 API
|
|
127
|
+
|
|
128
|
+
### `apiContract.create(options)` — 组合领域 contract
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
import { apiContract } from '@h-ai/api-contract'
|
|
132
|
+
|
|
133
|
+
const contract = apiContract.create({
|
|
134
|
+
iam: apiContract.iam, // 启用:纳入 contract
|
|
135
|
+
storage: false, // 禁用:不出现在 OpenAPI spec 中
|
|
136
|
+
custom: myContract, // 自定义领域
|
|
137
|
+
})
|
|
138
|
+
// 类型自动收窄,被禁用的 key 不出现在 contract 类型中
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### 内置领域 contract
|
|
142
|
+
|
|
143
|
+
| 访问路径 | 领域 | 功能概述 |
|
|
144
|
+
| --- | --- | --- |
|
|
145
|
+
| `apiContract.iam` | IAM | auth(登录/登出/OTP/刷新/注册/改密码)、users、roles、permissions Admin CRUD |
|
|
146
|
+
| `apiContract.storage` | Storage | presignedUrls(上传/下载/批量)、files(元数据查询/删除) |
|
|
147
|
+
| `apiContract.ai` | AI | chats(completion/流式)、knowledge(上传/查询) |
|
|
148
|
+
| `apiContract.payment` | Payment | orders、subscriptions |
|
|
149
|
+
|
|
150
|
+
### 内置 contract 路径示例
|
|
151
|
+
|
|
152
|
+
```typescript
|
|
153
|
+
const myContract = apiContract.create({ iam: apiContract.iam, storage: apiContract.storage, ai: apiContract.ai })
|
|
154
|
+
|
|
155
|
+
// IAM
|
|
156
|
+
myContract.iam.auth.login // POST /auth/login
|
|
157
|
+
myContract.iam.auth.logout // POST /auth/logout
|
|
158
|
+
myContract.iam.users.list // GET /iam/users
|
|
159
|
+
myContract.iam.roles.create // POST /iam/roles
|
|
160
|
+
|
|
161
|
+
// Storage
|
|
162
|
+
myContract.storage.presignedUrls.createUpload // POST /storage/presigned-urls/upload
|
|
163
|
+
|
|
164
|
+
// AI
|
|
165
|
+
myContract.ai.chats.createCompletion // POST /ai/chats/completion
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### `apiContract.haiResultSchema(dataSchema)` — 标准输出包装
|
|
169
|
+
|
|
170
|
+
所有 HTTP 输出必须通过 `apiContract.haiResultSchema` 包装为 `HaiResult<T>`,与 `@h-ai/core` 的 `ok()/err()` 返回结构一致:
|
|
171
|
+
|
|
172
|
+
```typescript
|
|
173
|
+
import { apiContract } from '@h-ai/api-contract'
|
|
174
|
+
import { z } from 'zod'
|
|
175
|
+
|
|
176
|
+
const getWidget = apiContract
|
|
177
|
+
.route({ method: 'GET', path: '/widgets/{id}' })
|
|
178
|
+
.output(apiContract.haiResultSchema(z.object({
|
|
179
|
+
id: z.string(),
|
|
180
|
+
name: z.string(),
|
|
181
|
+
})))
|
|
182
|
+
|
|
183
|
+
// 等价于:
|
|
184
|
+
// { success: true, data: { id: string, name: string } }
|
|
185
|
+
// | { success: false, error: HaiError }
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### `apiContract.pathOf(procedure)` — 读取业务路径
|
|
189
|
+
|
|
190
|
+
```typescript
|
|
191
|
+
const refreshPath = apiContract.pathOf(apiContract.iam.auth.refresh)
|
|
192
|
+
// '/auth/refresh'
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
middleware、client 与 E2E 需要业务路径时从 contract 元数据读取,不维护第二份 route 常量。服务端挂载时再与 `_serv.yml` 的 `http.apiPrefix` 拼接。
|
|
196
|
+
|
|
197
|
+
### 公共 Schema 工具
|
|
198
|
+
|
|
199
|
+
| 访问路径 | 说明 |
|
|
200
|
+
| --- | --- |
|
|
201
|
+
| `apiContract.haiResultSchema(dataSchema)` | `HaiResult<T>` 输出包装 |
|
|
202
|
+
| `apiContract.voidResultSchema` | `HaiResult<void>` 空结果包装 |
|
|
203
|
+
| `apiContract.paginatedSchema(itemSchema)` | 分页列表输出 Schema |
|
|
204
|
+
| `HaiErrorSchema` | HaiError 公共字段 Schema |
|
|
205
|
+
| `PaginationQuerySchema` | 分页参数 Schema(可选 page/pageSize,pageSize 上限 100;默认值由服务端处理) |
|
|
206
|
+
|
|
207
|
+
## Contract 定义规范
|
|
208
|
+
|
|
209
|
+
### 必须遵循
|
|
210
|
+
|
|
211
|
+
- **输出必须包装为 `apiContract.haiResultSchema`** — 客户端依赖 `success` 判断成功/失败
|
|
212
|
+
- **业务 path 直接写在对应 `*-contract.ts`** — 禁止新增 `*-routes.ts` 或业务路径常量
|
|
213
|
+
- **一次性 `*OutputSchema` 直接内联** — 不在 schemas 文件声明或导出
|
|
214
|
+
- **同一 contract 内重复使用的输出可设私有常量** — 不得为复用扩大公共导出
|
|
215
|
+
- **公共 HTTP 传输配置以配置文件为准** — API 前缀、健康检查、OpenAPI/docs、密钥协商路径与跨端 Header 放在 service 的 `_serv.yml`,不放 contract 包
|
|
216
|
+
- **schemas 只保留真实复用结构** — 至少被多个接口或 contract/procedure 等不同层共同消费
|
|
217
|
+
- **`operationId` 全局唯一** — 格式为 `domain.resource.action`,如 `iam.auth.login`
|
|
218
|
+
- **`tags` 至少包含领域名** — 供 OpenAPI 文档分组
|
|
219
|
+
- **输入 Schema 用 Zod v4** — 与 `@orpc/zod/zod4` 的 `ZodToJsonSchemaConverter` 配套
|
|
220
|
+
- **Contract 不含业务逻辑** — 只有 Schema + 路由元数据,没有 `async function`
|
|
221
|
+
|
|
222
|
+
### 禁止
|
|
223
|
+
|
|
224
|
+
- ❌ Contract 文件中 `import` 任何业务模块(`@h-ai/iam`、`@h-ai/storage` 等)
|
|
225
|
+
- ❌ 在 contract 中使用 `z.any()` 或 `z.unknown()` 作为输出类型
|
|
226
|
+
- ❌ 重复定义已存在的领域 contract
|
|
227
|
+
- ❌ 导出只被单个 contract 接口使用的 `*OutputSchema`
|
|
228
|
+
- ❌ 用独立 `*-routes.ts` 维护业务接口路径
|
|
229
|
+
|
|
230
|
+
### 文件职责
|
|
231
|
+
|
|
232
|
+
| 文件 | 只负责 |
|
|
233
|
+
| --- | --- |
|
|
234
|
+
| `*-contract.ts` | 业务接口 path、method、输入与输出边界 |
|
|
235
|
+
| `*-schemas.ts` | 跨接口或跨层复用的输入、实体、数据结构 |
|
|
236
|
+
| service `config/_serv.yml` | API 前缀、基础设施端点、跨端 Header 与加密 transport 配置 |
|
|
237
|
+
|
|
238
|
+
## 常见模式
|
|
239
|
+
|
|
240
|
+
### 带分页的列表接口
|
|
241
|
+
|
|
242
|
+
```typescript
|
|
243
|
+
import { apiContract, PaginationQuerySchema } from '@h-ai/api-contract'
|
|
244
|
+
import { z } from 'zod'
|
|
245
|
+
|
|
246
|
+
const WidgetSchema = z.object({ id: z.string(), name: z.string() })
|
|
247
|
+
|
|
248
|
+
const listWidgets = apiContract
|
|
249
|
+
.route({ method: 'GET', path: '/widgets', operationId: 'widget.list', tags: ['widget'] })
|
|
250
|
+
.input(PaginationQuerySchema.extend({ keyword: z.string().optional() }))
|
|
251
|
+
.output(apiContract.haiResultSchema(apiContract.paginatedSchema(WidgetSchema)))
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
### 路径参数
|
|
255
|
+
|
|
256
|
+
```typescript
|
|
257
|
+
// 路径中的 {id} 对应 input 中的 id 字段
|
|
258
|
+
const getWidget = apiContract
|
|
259
|
+
.route({ method: 'GET', path: '/widgets/{id}', operationId: 'widget.getById', tags: ['widget'] })
|
|
260
|
+
.input(z.object({ id: z.string() }))
|
|
261
|
+
.output(apiContract.haiResultSchema(WidgetSchema))
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
### 无输入的端点
|
|
265
|
+
|
|
266
|
+
```typescript
|
|
267
|
+
const versionContract = apiContract
|
|
268
|
+
.route({ method: 'GET', path: '/system/version', operationId: 'system.version', tags: ['system'] })
|
|
269
|
+
.output(apiContract.haiResultSchema(z.object({ version: z.string() })))
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
## 测试
|
|
273
|
+
|
|
274
|
+
```bash
|
|
275
|
+
pnpm --filter @h-ai/api-contract test
|
|
276
|
+
pnpm --filter @h-ai/api-contract typecheck
|
|
277
|
+
pnpm --filter @h-ai/api-contract lint
|
|
278
|
+
```
|
|
@@ -28,13 +28,13 @@ description: "在 hai 应用中新增、修改或修复功能,按需求复用
|
|
|
28
28
|
1. 读取 [development.md](development.md):先按真实需求匹配框架能力、已安装版本与应用架构,再确定改动。适用能力包括同一模块已有 feature、配置和扩展点,不以导入包作为复用完成的证据。
|
|
29
29
|
2. 读取所选模块的 skill 与实际公开类型,复用现有服务和组件。新增依赖或直接使用第三方 SDK 前说明已有能力的具体缺口;无需安装无关模块。
|
|
30
30
|
3. 新增或改变行为时用有意义的目标用例验证,仍适用的回归保持通过;被明确新需求替代的模板断言同步更新并记录依据。按下方参考实现,使用 [hai-app-review](../hai-app-review/SKILL.md) 检查改动涉及的安全与质量边界。
|
|
31
|
-
4.
|
|
31
|
+
4. 按 [分轮验证与集中修复](../hai-app-tests/validation.md) 运行已有门禁:一轮先收集所有可执行结果,再归并修复、复测失败及受影响项;报告行为证据、能力选择及未验证项。纯文案、格式和文档变化按影响验证,不强制新增测试。
|
|
32
32
|
|
|
33
33
|
## 按需参考
|
|
34
34
|
|
|
35
35
|
新增新手引导或帮助说明时读取 [UI 引导指南](../hai-ui/guide.md),复用 fullstack 默认的共享 guide-store、locale 和稳定 data-guide 锚点。
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
按任务读取 [reference.md](reference.md) 的对应小节;签名以当前源码或安装版本的公开类型为准,不默认加载全部资料。示例中的业务变量由应用提供。
|
|
38
38
|
|
|
39
39
|
- 适用场景
|
|
40
40
|
- 行为开发与验证
|
|
@@ -55,7 +55,7 @@ YAML 使用 `${HAI_DB_TYPE:sqlite}` 等显式引用,冒号后是未配置时
|
|
|
55
55
|
|
|
56
56
|
`prod` 是文件模式名,对应 `HAI_CORE_ENV=production`;环境枚举为 `development | test | production | staging`。SvelteKit adapter-node 产物使用 `node --env-file=.env --env-file=.env.prod --env-file-if-exists=.env.local build/index.js`。文件路径相对进程工作目录,公共文件随项目提交;容器通过部署平台或 `--env-file` 注入目标环境值。浏览器只接收公开白名单,构建产物和镜像不包含 `.env.local`。
|
|
57
57
|
|
|
58
|
-
Fullstack 的环境文件在工程根;service 脚本通过 `../../.env*` 加载,Web/App/Desktop 的 Vite
|
|
58
|
+
Fullstack 的环境文件在工程根;service 脚本通过 `../../.env*` 加载,Web/App/Desktop 的 Vite 自动 dotenv 加载关闭(`envDir: false`),开发和预览由 `core.config.loadEnvFiles` 从工程根读取,构建不读取部署环境文件。API workspace 的环境文件在 `apps/<name>-service/`。独立应用在各自根目录加载。静态 Web 通过 `runtime-config.js` 获取公开地址,部署环境覆盖无需重建镜像;原生包在复制静态资源前配置该入口。修改编译进客户端的默认值时仍需重新构建;fullstack 的公开应用元数据由 service `app.info` 返回。
|
|
59
59
|
|
|
60
60
|
#### 迁移已有项目
|
|
61
61
|
|
|
@@ -125,7 +125,7 @@ hai create my-app
|
|
|
125
125
|
|
|
126
126
|
## 将需求映射到现有能力
|
|
127
127
|
|
|
128
|
-
应用元信息只有一份配置来源:名称、版本、环境、默认语言与日志设置来自 `_core.yml`,使用 `core.config` 与 `core.logger`;Node 启动时不通过固定 `logging` 覆盖 YAML。SvelteKit 服务端 layout 和 fullstack `app.info` 仅公开必要元信息,不能下发整个 core 或模块配置。独立 Vite
|
|
128
|
+
应用元信息只有一份配置来源:名称、版本、环境、默认语言与日志设置来自 `_core.yml`,使用 `core.config` 与 `core.logger`;Node 启动时不通过固定 `logging` 覆盖 YAML。SvelteKit 服务端 layout 和 fullstack `app.info` 仅公开必要元信息,不能下发整个 core 或模块配置。独立 Vite 客户端经 Node/core 加载 YAML 默认值,Web 部署通过 `runtime-config.js` 应用公开白名单覆盖;只修改部署覆盖无需重建,修改构建默认值则须重新构建。环境覆盖使用 `HAI_CORE_NAME`、`HAI_CORE_VERSION`、`HAI_CORE_ENV`、`HAI_CORE_LOGGING_LEVEL` 等既有规则;容器明确配置生产环境,不能用另一个状态来源代替 `core.env`。
|
|
129
129
|
|
|
130
130
|
开发前,对本次涉及的需求简述“需求 → 现有应用实现/框架能力 → 已安装版本与公开契约依据 → 最小业务补充/修改位置 → 成功、权限与失败验收”。简单任务可用几句话;跨模块任务用表格。查找顺序:已有应用实现 → 框架公开能力和配置 → 最小业务补充。
|
|
131
131
|
|
|
@@ -209,9 +209,9 @@ export function pageOffset(query: PageQuery): number {
|
|
|
209
209
|
|
|
210
210
|
## 验证与交付
|
|
211
211
|
|
|
212
|
-
|
|
212
|
+
测试临时工程、镜像、容器、辅助进程与数据须在 finally/teardown 自动清理,成功、失败和取消均执行;只清理本次创建的资源。日志、截图与报告保留到分析和交付结束再清理,不提交验收产物。
|
|
213
213
|
|
|
214
214
|
- 对新增/改变行为按 [hai-app-tests](../hai-app-tests/SKILL.md) 写有意义的测试;目标用例先因行为缺失失败,既有回归保持通过。UI/路由改动覆盖真实流程、异步失败与权限路径;纯文案/文档按影响验证。
|
|
215
|
-
- 从当前 `package.json` 选择实际脚本,优先 workspace filter
|
|
215
|
+
- 从当前 `package.json` 选择实际脚本,优先 workspace filter,PowerShell 使用 `pnpm.cmd`。多项门禁按 [分轮验证与集中修复](../hai-app-tests/validation.md) 先脚本化收集结果和日志,再集中修复、复测失败及受影响项;普通失败不立即从头重跑。不能调用框架仓库私有命令替代应用检查。
|
|
216
216
|
- typecheck/lint 只覆盖部分质量规则;权限、事务、降级、真实 provider、浏览器、容器和原生交付各自需要适用行为证据。缺环境或密钥时报告未验证,不能用 mock 冒充真实集成。
|
|
217
217
|
- 完成说明:实际复用能力与自定义缺口、修改路径、已执行命令/行为结果、同步的 contract/调用方/配置/i18n/文档、未验证项及原因。无需无关模块的“覆盖率”清单。
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
### Step 4:重构与验证(Refactor 阶段)
|
|
41
41
|
|
|
42
42
|
1. 参照 `hai-app-review` 审查代码规范
|
|
43
|
-
2.
|
|
43
|
+
2. 按 [分轮验证与集中修复](../hai-app-tests/validation.md) 收集适用门禁,普通失败不中断独立检查,汇总后集中修复、再验证;优先 workspace filter,PowerShell 使用 `pnpm.cmd`
|
|
44
44
|
3. UI/路由/端到端改动执行相应 E2E;报告失败路径与安全验收结果,未运行的边界说明原因
|
|
45
45
|
|
|
46
46
|
## 目录结构约定
|
|
@@ -334,13 +334,14 @@ core.init() → crypto.init() → reldb.init() → cache.init() → iam.init()
|
|
|
334
334
|
|
|
335
335
|
仅涉及容器构建/部署变更时使用本节;普通功能开发无需每次启动镜像。
|
|
336
336
|
|
|
337
|
-
-
|
|
337
|
+
- 保持脚手架 `images/*.Dockerfile`、`.dockerignore` 与 `package.json` 中 `build` / `compile` 命令与 hai.build.json同步。保留 `.gitattributes`,让 `.dockerignore`、Dockerfile 和 POSIX 脚本统一使用 LF,保持跨平台输入一致并确保 Linux 脚本可运行。
|
|
338
338
|
- Podman 多阶段构建保持 `--format docker --jobs=1`;前者保留 `HEALTHCHECK`,后者避免部分 Podman 5.8/Buildah 组合在并行 stage 构建时触发内部 semaphore panic。
|
|
339
|
-
- 先复制 lockfile
|
|
339
|
+
- 先复制 lockfile、workspace 配置与构建器准备的 `images/dependencies/` 安装输入执行 `pnpm fetch --ignore-scripts`,再复制源码并离线安装、构建;fetch、安装和 `pnpm pm deploy` 共用 pnpm store,安装与生产依赖整理共用 SQLite 驱动缓存,避免重复下载,兼容新增 workspace、本地归档与补丁。
|
|
340
|
+
- 默认容器面向 CPU 运行或远程 AI 服务,Dockerfile 设置 `ONNXRUNTIME_NODE_INSTALL_CUDA=skip`,跳过额外 CUDA/TensorRT 下载;使用宿主机 GPU 时定制镜像和运行参数,不修改本机 npm 或全局 AI/GPU 配置。
|
|
340
341
|
- SvelteKit 服务使用 Node adapter;纯 Vite 前端使用带 SPA fallback 的静态镜像;fullstack 的 Service 与 Web 分别交付,Service 直接监听且不增加转发进程。
|
|
341
342
|
- Web 的 `PUBLIC_*` 部署配置在容器启动时生成静态运行时配置,不作为 build arg 烘焙,确保同一镜像可跨环境部署。
|
|
342
343
|
- 配置文件随镜像提供,并允许只读挂载覆盖;密钥只通过环境变量或 `--env-file` 注入。
|
|
343
|
-
- 普通构建不携带 `data/`;持续写入使用运行期 volume;需要预置数据时显式维护独立 Dockerfile
|
|
344
|
+
- 普通构建不携带 `data/`;持续写入使用运行期 volume;需要预置数据时显式维护独立 Dockerfile 和明确的 build context,不把本机数据加入默认镜像。
|
|
344
345
|
- 数据库和本地存储的持续写入必须落到 volume,不能依赖镜像可写层。
|
|
345
346
|
- 完成前实际启动镜像,验证健康端点、非 root 用户、环境变量覆盖、配置挂载和预置数据读取。
|
|
346
347
|
|
|
@@ -72,7 +72,7 @@ typecheck/lint 不能证明越权、租户隔离、事务、错误状态或资
|
|
|
72
72
|
|
|
73
73
|
## 门禁与输出
|
|
74
74
|
|
|
75
|
-
从当前 package.json 选择实际 scripts,优先 workspace filter;PowerShell 使用 `pnpm.cmd`。按
|
|
75
|
+
从当前 package.json 选择实际 scripts,优先 workspace filter;PowerShell 使用 `pnpm.cmd`。按 [分轮验证与集中修复](../hai-app-tests/validation.md) 先收集全部可执行门禁及日志,再归并根因、集中修复、验证失败及受影响项;普通失败不中断独立检查。浏览器、真实 provider、容器和原生交付各自报告实际结果。
|
|
76
76
|
|
|
77
77
|
交付说明需求/范围、带路径与影响的发现、已复用能力和必要缺口、已修正项、已执行门禁/行为证据、同步的 contract/调用方/i18n/配置/文档,以及未验证项和原因。只读审查报告问题与验证建议;不能凭审查清单宣称所有测试通过。
|
|
78
78
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: hai-app-tests
|
|
3
|
-
description: "
|
|
3
|
+
description: "为 hai 应用编写或修复 Vitest/Playwright 测试,组织分轮验证、汇总失败和集中修复,验证需求与完整用户流程。"
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# hai-app-tests
|
|
@@ -9,7 +9,7 @@ description: "编写或修复 hai 应用的 Vitest/Playwright 测试,验证业
|
|
|
9
9
|
|
|
10
10
|
| 项目 | 契约 |
|
|
11
11
|
| --- | --- |
|
|
12
|
-
| 能力 | 编写或修复
|
|
12
|
+
| 能力 | 编写或修复 Vitest/Playwright 测试,组织质量门禁与业务流程的分轮验证 |
|
|
13
13
|
| 适用场景 | 需求验收、缺陷回归或应用测试改进 |
|
|
14
14
|
| 输入 | 验收条件、被测公共入口、现有测试和运行环境 |
|
|
15
15
|
| 输出 | 可复现用例及明确区分静态、mock、浏览器/外部服务的结果 |
|
|
@@ -22,11 +22,12 @@ description: "编写或修复 hai 应用的 Vitest/Playwright 测试,验证业
|
|
|
22
22
|
- 按 [development.md](../hai-app-create/development.md) 的真实行为与安全边界选测试;新增/改变业务行为、缺陷修复有目标用例,纯文案/格式/文档及无行为变化的可逆低影响修改不补镜像测试。
|
|
23
23
|
- 不要求每个功能同时新增单元与 E2E;复用已有用例,选择能验证变化的最小适用层,关键跨层用户流程仍需端到端证据。
|
|
24
24
|
- 新用例已通过时先检查是否已有能力;不能为达成 Red 人为破坏旧测试。
|
|
25
|
+
- 执行多项门禁前读取 [分轮验证与集中修复](validation.md):一轮先脚本化收集所有可执行检查及日志,普通失败不立即改代码;汇总去重后集中修复,再复测失败和受影响项。只有真实前置阻塞才暂停依赖检查;最终必需门禁均须对最终代码有效通过。小范围 TDD Red → Green 保留。
|
|
25
26
|
- 外部服务未运行必须报告;mock/Testcontainers/浏览器/真实付费 Provider 的结果分开表述。
|
|
26
27
|
|
|
27
28
|
## 按需参考
|
|
28
29
|
|
|
29
|
-
|
|
30
|
+
按任务读取 [reference.md](reference.md) 的对应小节;签名以当前源码或安装版本的公开类型为准,不默认加载全部资料。示例中的业务变量由应用提供。
|
|
30
31
|
|
|
31
32
|
- 适用场景
|
|
32
33
|
- 行为验证与 TDD 工作流
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
|
|
53
53
|
1. 参照 `hai-app-review` 技能审查代码规范
|
|
54
54
|
2. 重构后执行受影响测试与必要回归,确认已有行为保持通过
|
|
55
|
-
3.
|
|
55
|
+
3. 多项门禁按 [分轮验证与集中修复](validation.md) 收集一轮结果,汇总后修复、再验证;不要把目标用例的小范围 TDD 扩大为每修一处就从头运行全量门禁。PowerShell 使用 `pnpm.cmd`
|
|
56
56
|
|
|
57
57
|
## 测试目录约定
|
|
58
58
|
|
|
@@ -436,7 +436,7 @@ await expect(page).toHaveURL('/admin/articles')
|
|
|
436
436
|
|
|
437
437
|
## 质量门禁
|
|
438
438
|
|
|
439
|
-
从当前 package.json 确认实际脚本和 workspace
|
|
439
|
+
从当前 package.json 确认实际脚本和 workspace 名称,按 [分轮验证与集中修复](validation.md) 组织以下适用检查;不要为格式/文档改动运行无关 provider 或新增镜像测试。逐轮记录范围、状态/退出码、日志、mock/真实环境与未验证原因,完整日志按需读取。
|
|
440
440
|
|
|
441
441
|
```bash
|
|
442
442
|
# 运行单元测试
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# 分轮验证与集中修复
|
|
2
|
+
|
|
3
|
+
适用于开发、审查修复、升级后的质量门禁与整体验收。默认执行“收集一轮 → 归并问题 → 集中修复 → 再验证”,不要遇到一个普通失败就修改代码并从头重跑。新增行为的目标用例仍可做小范围 TDD Red → Green;它不要求提前运行整套门禁。
|
|
4
|
+
|
|
5
|
+
## 1. 确定本轮检查
|
|
6
|
+
|
|
7
|
+
- 先读当前 `package.json`、任务依赖与环境要求,按影响范围列出命令、工作目录、前置条件和预期证据。优先 workspace filter;用户要求全量验收时保留全量范围。纯文案/文档修改只做适用检查,不新增镜像测试。
|
|
8
|
+
- 一轮默认按 typecheck → lint → build(涉及构建/配置/模板/跨包契约)→ test → 相关 E2E 调度;顺序服从真实依赖。同轮收集不等于同时运行:只有输出目录、端口、数据库、容器与 fixture 均隔离的检查才可并行,共用 `dist`、`.svelte-kit` 或预览产物的构建与测试必须串行。
|
|
9
|
+
- 使用现有任务编排或一个临时调度脚本逐项执行、捕获真实退出码并保存日志。不要用跨检查的 `&&`、全局 fail-fast 或 `|| true` 代替结果收集;脚本内部有前置短路时,确认后续检查是否真正执行,必要时分别调度等价子任务,保留原门禁的最终验收。
|
|
10
|
+
|
|
11
|
+
## 2. 收集全部可执行结果
|
|
12
|
+
|
|
13
|
+
- 本轮开始后保持被测源码、配置、依赖和运行环境稳定;协作 agent 也不得边测边改,不运行自动修复。普通类型错误、lint 错误、测试断言失败只记录,继续其余具备前置条件的检查。
|
|
14
|
+
- 只有真实前置失败才阻塞依赖它的检查,例如依赖无法安装、执行器无法启动、上游构建未产生必要产物、必需服务不可用。标明阻塞原因,继续独立检查;确实无法继续收集时才提前结束本轮、集中修复阻塞。不得使用旧构建、旧镜像或旧截图让下游假通过。
|
|
15
|
+
- 为每轮使用独立且被 Git 忽略的日志目录,例如 `tmp/validation/round-01/`。逐项记录命令/范围、源码版本及未提交改动、环境、状态(通过/失败/阻塞/未执行)、退出码、耗时和日志路径;未执行与不适用须说明原因,不能写成通过。超时、取消和环境失败单列,不能当作断言失败或忽略。
|
|
16
|
+
- 调度脚本等子进程退出后再记录结果;重定向或管道不能覆盖实际退出码。所有必需项均有有效通过结果,整轮才算通过;收集到任何失败或阻塞时,汇总必须保留非成功状态。
|
|
17
|
+
- 完整输出写日志,AI 先读汇总及每类失败的有限上下文,再按需定位原始日志;不反复载入整份成功日志、密集轮询或启动尚未结束的同一检查。长任务期间定期简述进度。
|
|
18
|
+
|
|
19
|
+
## 3. 集中分析与修复
|
|
20
|
+
|
|
21
|
+
- 本轮可执行检查结束后,把问题按根因去重,记录位置、证据、影响、修复项及关联门禁。上游故障造成的连锁失败合并处理;区分本次引入、原有问题和环境问题。
|
|
22
|
+
- 在已有授权范围内集中修复本次范围的问题,保留仍有效的断言与回归。不能靠删断言、跳过用例、放宽超时或关闭规则换绿灯。环境故障先保存证据、确认恢复条件,再定向重试;不反复重跑到偶然通过。
|
|
23
|
+
- 新发现的独立问题加入同一清单;修复期间的小范围诊断不触发从头全量重跑,也不能替代下一轮必要验收。
|
|
24
|
+
|
|
25
|
+
## 4. 下一轮与完成条件
|
|
26
|
+
|
|
27
|
+
- 修复后开启新一轮,先复测失败项、被阻塞项及受改动影响的检查和消费者;按同样方式收集完再修复。有影响范围不明、共享配置/依赖改变或用户要求时,扩大到全量门禁。
|
|
28
|
+
- 只有源码、依赖、配置、工具、环境及所用产物等输入均未改变的已通过项才可复用,并注明来源轮次或缓存;输入变化使相关结果失效。最终所有必需检查必须对最终代码仍有效,缺项不能称为完成。真实部署、Provider、浏览器或原生验收不能只引用静态检查/构建缓存。
|
|
29
|
+
- 保留原有 CI/发布门禁的失败阻断;问题收集时继续运行不代表允许失败提交发布或忽略 required checks。
|
|
30
|
+
- 每轮都在 finally/teardown 停止本轮进程、清理本轮临时工程/容器/镜像/数据;确认退出和资源释放后再运行冲突任务。日志、截图与报告保留到问题分析和交付结束,再按项目约定清理,不覆盖失败证据、不提交验收产物、不删除用户资源。
|
|
31
|
+
- 最终报告各项门禁状态、执行/复用范围、修复结果和未验证原因;mock、容器、真实服务及原生结果分别说明。只陈述实际证据,不以“脚本执行完了”代表验证通过。
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: hai-audit
|
|
3
|
-
description: "
|
|
3
|
+
description: "接入 @h-ai/audit 记录业务操作,查询分页日志与统计,并按保留期清理。"
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
#
|
|
6
|
+
# hai-audit
|
|
7
7
|
|
|
8
8
|
## 能力契约
|
|
9
9
|
|
|
@@ -11,150 +11,25 @@ description: "使用 @h-ai/audit 记录、查询、统计和清理操作审计
|
|
|
11
11
|
| --- | --- |
|
|
12
12
|
| 能力 | 使用 @h-ai/audit 记录、查询、统计和清理操作审计 |
|
|
13
13
|
| 适用场景 | 业务操作留痕与审计记录检索 |
|
|
14
|
-
| 输入 |
|
|
14
|
+
| 输入 | AuditInitConfigInput、已初始化 reldb、审计事件与查询条件 |
|
|
15
15
|
| 输出 | 记录/查询/统计的 HaiResult |
|
|
16
16
|
| 限制 | 仅服务端;审计内容需脱敏,保留期和清理范围由应用策略决定。 |
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
## 使用路径
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
1. 确认已初始化 reldb,校验 `AuditInitConfigInput` 后初始化 audit;每步失败停止后续操作。
|
|
21
|
+
2. 在实际业务成功/失败边界记录事件,明确审计失败如何影响业务,不重复记录同一事件。
|
|
22
|
+
3. 查询使用返回的分页元数据;清理前核对保留期及授权范围。凭证字段会被脱敏,个人信息仍需业务策略。
|
|
23
|
+
4. 检查写入、查询和清理的 HaiResult,先关闭 audit 再关闭数据库。
|
|
21
24
|
|
|
22
|
-
|
|
25
|
+
## 按需参考
|
|
23
26
|
|
|
24
|
-
|
|
27
|
+
按本次操作读取对应小节;以已安装版本的公开类型核验签名,示例中的业务变量由应用提供。
|
|
25
28
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
审计模块依赖已初始化的 `@h-ai/reldb` 单例,无需独立配置文件。审计日志固定存储在 `hai_audit_logs` 表中。
|
|
35
|
-
|
|
36
|
-
### 2. 初始化
|
|
37
|
-
|
|
38
|
-
```ts
|
|
39
|
-
import { audit } from '@h-ai/audit'
|
|
40
|
-
import { reldb } from '@h-ai/reldb'
|
|
41
|
-
|
|
42
|
-
// 先初始化依赖
|
|
43
|
-
await reldb.init({ type: 'sqlite', database: './data.db' })
|
|
44
|
-
|
|
45
|
-
// 再初始化审计模块(自动使用已初始化的 reldb 单例)
|
|
46
|
-
const result = await audit.init()
|
|
47
|
-
if (!result.success) {
|
|
48
|
-
// 处理错误
|
|
49
|
-
}
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
自定义用户表映射:
|
|
53
|
-
|
|
54
|
-
```ts
|
|
55
|
-
await audit.init({
|
|
56
|
-
userTable: 'hai_iam_users',
|
|
57
|
-
userIdColumn: 'id',
|
|
58
|
-
userNameColumn: 'username',
|
|
59
|
-
})
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
### 3. 关闭
|
|
63
|
-
|
|
64
|
-
```ts
|
|
65
|
-
const closeResult = await audit.close()
|
|
66
|
-
if (!closeResult.success) {
|
|
67
|
-
throw new Error(closeResult.error.message)
|
|
68
|
-
}
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
## 核心 API
|
|
72
|
-
|
|
73
|
-
### 生命周期
|
|
74
|
-
|
|
75
|
-
| 方法 | 签名 | 说明 |
|
|
76
|
-
| --------------------- | ---------------------------------------------------------------- | ---------- |
|
|
77
|
-
| `audit.init` | `(config: AuditInitConfig) => Promise<HaiResult<void>>` | 初始化模块 |
|
|
78
|
-
| `audit.close` | `() => Promise<HaiResult<void>>` | 关闭模块 |
|
|
79
|
-
| `audit.isInitialized` | `boolean` | 初始化状态 |
|
|
80
|
-
|
|
81
|
-
### 日志操作
|
|
82
|
-
|
|
83
|
-
| 方法 | 签名 | 说明 |
|
|
84
|
-
| --------------------- | ----------------------------------------------------------------------------------- | -------------------- |
|
|
85
|
-
| `audit.log` | `(input: CreateAuditLogInput) => Promise<HaiResult<AuditLog>>` | 记录审计日志 |
|
|
86
|
-
| `audit.list` | `(options?: ListAuditLogsOptions) => Promise<HaiResult<PaginatedResult<AuditLogWithUser>>>` | 分页查询(items/total/page/pageSize,含用户名;使用返回的规范化元数据,默认 pageSize 20) |
|
|
87
|
-
| `audit.getUserRecent` | `(userId: string, limit?: number) => Promise<HaiResult<AuditLog[]>>` | 用户最近活动 |
|
|
88
|
-
| `audit.cleanup` | `(olderThanDays?: number) => Promise<HaiResult<number>>` | 清理旧日志 |
|
|
89
|
-
| `audit.getStats` | `(days?: number) => Promise<HaiResult<AuditStatItem[]>>` | 操作统计 |
|
|
90
|
-
|
|
91
|
-
### 便捷记录器 (`audit.helper`)
|
|
92
|
-
|
|
93
|
-
| 方法 | 说明 |
|
|
94
|
-
| ------------------------------------------------------------------------------ | ------------ |
|
|
95
|
-
| `audit.helper.login(userId, ip?, ua?)` | 登录 |
|
|
96
|
-
| `audit.helper.logout(userId, ip?, ua?)` | 登出 |
|
|
97
|
-
| `audit.helper.register(userId, ip?, ua?)` | 注册 |
|
|
98
|
-
| `audit.helper.passwordResetRequest(email, ip?, ua?)` | 密码重置请求 |
|
|
99
|
-
| `audit.helper.passwordResetComplete(userId, ip?, ua?)` | 密码重置完成 |
|
|
100
|
-
| `audit.helper.crud(input)` | CRUD 操作 |
|
|
101
|
-
|
|
102
|
-
> `audit.log()` / `audit.helper.*()` 中的 `details` 仅会自动脱敏凭证类字段(如 `password` / `token` / `secret`);邮箱、手机号默认保留原值。
|
|
103
|
-
|
|
104
|
-
## 错误码
|
|
105
|
-
|
|
106
|
-
| 错误码 | code | 说明 |
|
|
107
|
-
| --------------------------------- | ---------------- | ------------ |
|
|
108
|
-
| `HaiAuditError.LOG_FAILED` | `hai:audit:001` | 记录失败 |
|
|
109
|
-
| `HaiAuditError.QUERY_FAILED` | `hai:audit:002` | 查询失败 |
|
|
110
|
-
| `HaiAuditError.CLEANUP_FAILED` | `hai:audit:003` | 清理失败 |
|
|
111
|
-
| `HaiAuditError.STATS_FAILED` | `hai:audit:004` | 统计失败 |
|
|
112
|
-
| `HaiAuditError.INIT_IN_PROGRESS` | `hai:audit:005` | 初始化进行中 |
|
|
113
|
-
| `HaiAuditError.NOT_INITIALIZED` | `hai:audit:010` | 模块未初始化 |
|
|
114
|
-
| `HaiAuditError.CONFIG_ERROR` | `hai:audit:012` | 配置错误 |
|
|
115
|
-
|
|
116
|
-
## 常见模式
|
|
117
|
-
|
|
118
|
-
### 用户认证审计
|
|
119
|
-
|
|
120
|
-
```ts
|
|
121
|
-
// 登录
|
|
122
|
-
await audit.helper.login(userId, request.ip, request.headers['user-agent'])
|
|
123
|
-
|
|
124
|
-
// 登出
|
|
125
|
-
await audit.helper.logout(userId, request.ip, request.headers['user-agent'])
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
### CRUD 操作审计
|
|
129
|
-
|
|
130
|
-
```ts
|
|
131
|
-
await audit.helper.crud({ userId, action: 'create', resource: 'users', resourceId: newUser.id, details: { name: newUser.name } })
|
|
132
|
-
await audit.helper.crud({ userId, action: 'update', resource: 'roles', resourceId: roleId, details: { changes } })
|
|
133
|
-
await audit.helper.crud({ userId, action: 'delete', resource: 'permissions', resourceId: permId })
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
### 仪表盘数据
|
|
137
|
-
|
|
138
|
-
```ts
|
|
139
|
-
// 最近日志
|
|
140
|
-
const recentLogs = await audit.list({ pageSize: 10 })
|
|
141
|
-
|
|
142
|
-
// 7 天统计
|
|
143
|
-
const stats = await audit.getStats(7)
|
|
144
|
-
```
|
|
145
|
-
|
|
146
|
-
### 定期清理
|
|
147
|
-
|
|
148
|
-
```ts
|
|
149
|
-
// 清理 90 天前的日志
|
|
150
|
-
const result = await audit.cleanup(90)
|
|
151
|
-
if (result.success) {
|
|
152
|
-
logger.info(`Cleaned up ${result.data} old audit logs`)
|
|
153
|
-
}
|
|
154
|
-
```
|
|
155
|
-
|
|
156
|
-
## 相关 Skills
|
|
157
|
-
|
|
158
|
-
- `hai-reldb` — 数据库模块(审计模块依赖)
|
|
159
|
-
- `hai-iam` — 身份认证与权限管理(审计的主要记录对象)
|
|
160
|
-
- `hai-core` — 核心工具(日志、ID 生成、i18n)
|
|
29
|
+
- [运行环境](reference.md#运行环境)
|
|
30
|
+
- [依赖](reference.md#依赖)
|
|
31
|
+
- [使用步骤](reference.md#使用步骤)
|
|
32
|
+
- [核心 API](reference.md#核心-api)
|
|
33
|
+
- [错误码](reference.md#错误码)
|
|
34
|
+
- [常见模式](reference.md#常见模式)
|
|
35
|
+
- [相关 Skills](reference.md#相关-skills)
|