@h-ai/cli 0.1.0-alpha.42 → 0.1.0-alpha.44
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/dist/{chunk-CFCXOKGV.js → chunk-U4PA632G.js} +123 -125
- package/dist/chunk-U4PA632G.js.map +1 -0
- package/dist/cli-config-templates-GSXKLGU5.js +5 -0
- package/dist/{cli-config-templates-5J5VKWWD.js.map → cli-config-templates-GSXKLGU5.js.map} +1 -1
- package/dist/index.js +2 -2
- package/package.json +2 -2
- package/templates/apps/api/apps/{{projectName}}-contract/README.md.hbs +4 -2
- package/templates/apps/api/apps/{{projectName}}-contract/src/app-contract.ts.hbs +3 -3
- package/templates/apps/api/apps/{{projectName}}-contract/src/app-schemas.ts.hbs +0 -7
- package/templates/apps/api/apps/{{projectName}}-service/config/_core.yml.hbs +1 -1
- package/templates/apps/api/apps/{{projectName}}-service/config/_serv.yml.hbs +18 -1
- package/templates/apps/api/apps/{{projectName}}-service/src/app.ts.hbs +20 -34
- package/templates/apps/api/apps/{{projectName}}-service/tests/service.test.ts.hbs +7 -2
- package/templates/apps/api/e2e/api-service.spec.ts.hbs +5 -1
- package/templates/apps/api/package.json.hbs +4 -0
- package/templates/apps/fullstack/e2e/fullstack.spec.ts.hbs +5 -3
- package/templates/apps/fullstack/package.json.hbs +1 -0
- package/templates/apps/fullstack/packages/{{projectName}}-contract/README.md.hbs +6 -4
- package/templates/apps/fullstack/packages/{{projectName}}-contract/src/app-contract.ts.hbs +6 -7
- package/templates/apps/fullstack/packages/{{projectName}}-contract/src/app-schemas.ts.hbs +3 -6
- package/templates/apps/fullstack/packages/{{projectName}}-contract/src/index.ts.hbs +0 -1
- package/templates/apps/fullstack/packages/{{projectName}}-contract/src/{{projectName}}-contract.ts.hbs +4 -1
- package/templates/apps/fullstack/packages/{{projectName}}-contract/tests/contract.test.ts.hbs +2 -17
- package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_core.yml.hbs +3 -3
- package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_serv.yml.hbs +24 -6
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/app-server.ts.hbs +17 -19
- package/templates/apps/fullstack/packages/{{projectName}}-serv/tests/app-server.test.ts.hbs +8 -5
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/api/api-client.ts.hbs +3 -3
- package/templates/apps/mobile-app/src/lib/api.ts.hbs +1 -2
- package/templates/base/e2e/app.spec.ts.hbs +4 -1
- package/templates/base/package.json.hbs +1 -1
- package/templates/skills/hai-ai/SKILL.md +11 -4
- package/templates/skills/hai-ai/reference.md +2 -2
- package/templates/skills/hai-api-contract/SKILL.md +34 -8
- package/templates/skills/hai-build/SKILL.md +9 -6
- package/templates/skills/hai-cache/SKILL.md +9 -6
- package/templates/skills/hai-capacitor/SKILL.md +0 -2
- package/templates/skills/hai-core/SKILL.md +15 -9
- package/templates/skills/hai-deploy/SKILL.md +3 -0
- package/templates/skills/hai-iam/SKILL.md +7 -4
- package/templates/skills/hai-payment/SKILL.md +14 -11
- package/templates/skills/hai-reach/SKILL.md +3 -0
- package/templates/skills/hai-reldb/SKILL.md +9 -6
- package/templates/skills/hai-serv/SKILL.md +46 -2
- package/templates/skills/hai-storage/SKILL.md +10 -7
- package/templates/skills/hai-vecdb/SKILL.md +8 -5
- package/dist/chunk-CFCXOKGV.js.map +0 -1
- package/dist/cli-config-templates-5J5VKWWD.js +0 -5
- package/templates/apps/fullstack/packages/{{projectName}}-contract/src/app-routes.ts.hbs +0 -27
|
@@ -80,7 +80,7 @@ const WidgetCreateInputSchema = z.object({
|
|
|
80
80
|
description: z.string().optional(),
|
|
81
81
|
})
|
|
82
82
|
|
|
83
|
-
//
|
|
83
|
+
// 同一 contract 内两个接口复用,因此保留为文件私有常量,不从 schemas 导出。
|
|
84
84
|
const WidgetOutputSchema = apiContract.haiResultSchema(z.object({
|
|
85
85
|
id: z.string(),
|
|
86
86
|
name: z.string(),
|
|
@@ -196,16 +196,27 @@ myContract.ai.chats.createCompletion // POST /ai/chats/completion
|
|
|
196
196
|
import { apiContract } from '@h-ai/api-contract'
|
|
197
197
|
import { z } from 'zod'
|
|
198
198
|
|
|
199
|
-
const
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
199
|
+
const getWidget = apiContract
|
|
200
|
+
.route({ method: 'GET', path: '/widgets/{id}' })
|
|
201
|
+
.output(apiContract.haiResultSchema(z.object({
|
|
202
|
+
id: z.string(),
|
|
203
|
+
name: z.string(),
|
|
204
|
+
})))
|
|
203
205
|
|
|
204
206
|
// 等价于:
|
|
205
207
|
// { success: true, data: { id: string, name: string } }
|
|
206
208
|
// | { success: false, error: HaiError }
|
|
207
209
|
```
|
|
208
210
|
|
|
211
|
+
### `apiContract.pathOf(procedure)` — 读取业务路径
|
|
212
|
+
|
|
213
|
+
```typescript
|
|
214
|
+
const refreshPath = apiContract.pathOf(apiContract.iam.auth.refresh)
|
|
215
|
+
// '/auth/refresh'
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
middleware、client 与 E2E 需要业务路径时从 contract 元数据读取,不维护第二份 route 常量。服务端挂载时再与 `_serv.yml` 的 `http.apiPrefix` 拼接。
|
|
219
|
+
|
|
209
220
|
### 公共 Schema 工具
|
|
210
221
|
|
|
211
222
|
| 访问路径 | 说明 |
|
|
@@ -224,6 +235,11 @@ const OutputSchema = apiContract.haiResultSchema(z.object({
|
|
|
224
235
|
### 必须遵循
|
|
225
236
|
|
|
226
237
|
- **输出必须包装为 `apiContract.haiResultSchema`** — 客户端依赖 `success` 判断成功/失败
|
|
238
|
+
- **业务 path 直接写在对应 `*-contract.ts`** — 禁止新增 `*-routes.ts` 或业务路径常量
|
|
239
|
+
- **一次性 `*OutputSchema` 直接内联** — 不在 schemas 文件声明或导出
|
|
240
|
+
- **同一 contract 内重复使用的输出可设私有常量** — 不得为复用扩大公共导出
|
|
241
|
+
- **公共 HTTP 传输配置以配置文件为准** — API 前缀、健康检查、OpenAPI/docs、密钥协商路径与跨端 Header 放在 service 的 `_serv.yml`,不放 contract 包
|
|
242
|
+
- **schemas 只保留真实复用结构** — 至少被多个接口或 contract/procedure 等不同层共同消费
|
|
227
243
|
- **`operationId` 全局唯一** — 格式为 `domain.resource.action`,如 `iam.auth.login`
|
|
228
244
|
- **`tags` 至少包含领域名** — 供 OpenAPI 文档分组
|
|
229
245
|
- **输入 Schema 用 Zod v4** — 与 `@orpc/zod/zod4` 的 `ZodToJsonSchemaConverter` 配套
|
|
@@ -234,6 +250,16 @@ const OutputSchema = apiContract.haiResultSchema(z.object({
|
|
|
234
250
|
- ❌ Contract 文件中 `import` 任何业务模块(`@h-ai/iam`、`@h-ai/storage` 等)
|
|
235
251
|
- ❌ 在 contract 中使用 `z.any()` 或 `z.unknown()` 作为输出类型
|
|
236
252
|
- ❌ 重复定义已存在的领域 contract
|
|
253
|
+
- ❌ 导出只被单个 contract 接口使用的 `*OutputSchema`
|
|
254
|
+
- ❌ 用独立 `*-routes.ts` 维护业务接口路径
|
|
255
|
+
|
|
256
|
+
### 文件职责
|
|
257
|
+
|
|
258
|
+
| 文件 | 只负责 |
|
|
259
|
+
| --- | --- |
|
|
260
|
+
| `*-contract.ts` | 业务接口 path、method、输入与输出边界 |
|
|
261
|
+
| `*-schemas.ts` | 跨接口或跨层复用的输入、实体、数据结构 |
|
|
262
|
+
| service `config/_serv.yml` | API 前缀、基础设施端点、跨端 Header 与加密 transport 配置 |
|
|
237
263
|
|
|
238
264
|
---
|
|
239
265
|
|
|
@@ -266,9 +292,9 @@ const getWidget = apiContract
|
|
|
266
292
|
### 无输入的端点
|
|
267
293
|
|
|
268
294
|
```typescript
|
|
269
|
-
const
|
|
270
|
-
.route({ method: 'GET', path: '/
|
|
271
|
-
.output(apiContract.haiResultSchema(z.object({
|
|
295
|
+
const versionContract = apiContract
|
|
296
|
+
.route({ method: 'GET', path: '/system/version', operationId: 'system.version', tags: ['system'] })
|
|
297
|
+
.output(apiContract.haiResultSchema(z.object({ version: z.string() })))
|
|
272
298
|
```
|
|
273
299
|
|
|
274
300
|
---
|
|
@@ -193,17 +193,20 @@ export async function initCapacitor() {
|
|
|
193
193
|
|
|
194
194
|
### 配置文件格式
|
|
195
195
|
|
|
196
|
-
所有模块配置使用 YAML
|
|
196
|
+
所有模块配置使用 YAML 保存默认值;`HAI_<配置名>_<YAML 路径>` 环境变量
|
|
197
|
+
自动覆盖对应叶子项,且优先级更高:
|
|
197
198
|
|
|
198
199
|
```yaml
|
|
199
200
|
# config/_core.yml
|
|
200
|
-
app
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
level: ${HAI_LOG_LEVEL:info}
|
|
201
|
+
name: my-app
|
|
202
|
+
env: development
|
|
203
|
+
logging:
|
|
204
|
+
level: info
|
|
205
205
|
```
|
|
206
206
|
|
|
207
|
+
以上字段分别对应 `HAI_CORE_NAME`、`HAI_CORE_ENV`、
|
|
208
|
+
`HAI_CORE_LOGGING_LEVEL`。
|
|
209
|
+
|
|
207
210
|
---
|
|
208
211
|
|
|
209
212
|
## 技能导航
|
|
@@ -42,15 +42,18 @@ description: 使用 @h-ai/cache 进行内存或 Redis 缓存操作(kv/hash/lis
|
|
|
42
42
|
|
|
43
43
|
```yaml
|
|
44
44
|
# config/_cache.yml
|
|
45
|
-
type:
|
|
45
|
+
type: memory # memory | redis
|
|
46
46
|
# Redis 配置(type=redis 时生效):
|
|
47
|
-
# host:
|
|
48
|
-
# port:
|
|
49
|
-
# password:
|
|
50
|
-
# db:
|
|
51
|
-
# keyPrefix:
|
|
47
|
+
# host: localhost
|
|
48
|
+
# port: 6379
|
|
49
|
+
# password: ''
|
|
50
|
+
# db: 0
|
|
51
|
+
# keyPrefix: 'hai:'
|
|
52
52
|
```
|
|
53
53
|
|
|
54
|
+
约定环境变量优先于 YAML,例如 `HAI_CACHE_TYPE`、`HAI_CACHE_HOST`、
|
|
55
|
+
`HAI_CACHE_KEYPREFIX`。
|
|
56
|
+
|
|
54
57
|
### 2. 初始化与关闭
|
|
55
58
|
|
|
56
59
|
```typescript
|
|
@@ -69,7 +69,6 @@ await apiClient.init({
|
|
|
69
69
|
baseUrl: import.meta.env.PUBLIC_API_BASE,
|
|
70
70
|
auth: {
|
|
71
71
|
storage: createCapacitorTokenStorage(),
|
|
72
|
-
refreshPath: '/api/v1/auth/refresh',
|
|
73
72
|
},
|
|
74
73
|
})
|
|
75
74
|
```
|
|
@@ -289,7 +288,6 @@ export async function initApi() {
|
|
|
289
288
|
baseUrl: `${import.meta.env.PUBLIC_API_BASE}/api/v1`,
|
|
290
289
|
auth: {
|
|
291
290
|
storage: createCapacitorTokenStorage(),
|
|
292
|
-
refreshPath: '/auth/refresh',
|
|
293
291
|
},
|
|
294
292
|
})
|
|
295
293
|
}
|
|
@@ -131,23 +131,29 @@ const unwatch = core.config.watch('db', (cfg, error) => {
|
|
|
131
131
|
unwatch()
|
|
132
132
|
```
|
|
133
133
|
|
|
134
|
-
配置文件格式(YAML
|
|
134
|
+
配置文件格式(YAML,支持约定式环境变量映射和显式插值):
|
|
135
|
+
|
|
136
|
+
- 每个 YAML 叶子配置项自动映射为 `HAI_<配置名>_<YAML 路径>`;配置名和 key 转大写、层级以 `_` 分隔,camelCase 不拆词
|
|
137
|
+
- 例如 `core.config.load('ai', ...)` 加载的 `llm.apiKey` 对应 `HAI_AI_LLM_APIKEY`,`llm.models[0].temperature` 对应 `HAI_AI_LLM_MODELS_0_TEMPERATURE`
|
|
138
|
+
- 约定环境变量存在时覆盖 YAML 值并按 YAML 标量规则还原类型;不存在时保留 YAML 值,且不会新增 YAML 中未声明的配置项
|
|
139
|
+
- 优先级固定为约定环境变量 > 显式 `${VAR}` > YAML 默认值;显式语法只用于指定特殊变量名
|
|
135
140
|
|
|
136
141
|
- `${VAR}` — 读取 `process.env.VAR`;缺失则返回 `HaiConfigError.CONFIG_ENV_VAR_MISSING` 错误
|
|
137
142
|
- `${VAR:default}` — 读取 `process.env.VAR`;缺失则使用默认值
|
|
138
143
|
- **类型还原**:整个值恰好是单个变量表达式时,结果还原为原生类型(number / boolean 等);混合文本始终为字符串
|
|
144
|
+
- **空值语义**:`${VAR:}` 或值为空字符串的 `${VAR}` 保持为 `''`,不会被 YAML 解析为 `null`;是否把空字符串视为未配置由模块 Schema 明确定义
|
|
139
145
|
|
|
140
146
|
```yaml
|
|
141
147
|
# config/_core.yml
|
|
142
|
-
app
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
format: ${HAI_LOG_FORMAT:pretty} # → string,pretty | json
|
|
148
|
+
name: my-app
|
|
149
|
+
env: development
|
|
150
|
+
logging:
|
|
151
|
+
level: info
|
|
152
|
+
format: pretty
|
|
148
153
|
feature:
|
|
149
|
-
debug:
|
|
150
|
-
maxRetries:
|
|
154
|
+
debug: false
|
|
155
|
+
maxRetries: 3
|
|
156
|
+
# 只有特殊变量名才需要显式语法:
|
|
151
157
|
url: http://${HOST}:${PORT} # → string(混合文本,不还原)
|
|
152
158
|
```
|
|
153
159
|
|
|
@@ -54,12 +54,12 @@ description: 使用 @h-ai/iam 进行身份认证(密码/OTP/LDAP/API Key)、
|
|
|
54
54
|
```yaml
|
|
55
55
|
# config/_iam.yml
|
|
56
56
|
password:
|
|
57
|
-
minLength:
|
|
57
|
+
minLength: 8
|
|
58
58
|
requireUppercase: true
|
|
59
59
|
requireNumber: true
|
|
60
60
|
session:
|
|
61
|
-
maxAge:
|
|
62
|
-
refreshTokenMaxAge:
|
|
61
|
+
maxAge: 86400
|
|
62
|
+
refreshTokenMaxAge: 604800 # 7 天
|
|
63
63
|
sliding: true
|
|
64
64
|
singleDevice: false
|
|
65
65
|
login:
|
|
@@ -68,13 +68,16 @@ login:
|
|
|
68
68
|
register:
|
|
69
69
|
enabled: true
|
|
70
70
|
security:
|
|
71
|
-
maxLoginAttempts:
|
|
71
|
+
maxLoginAttempts: 5
|
|
72
72
|
lockoutDuration: 900
|
|
73
73
|
rbac:
|
|
74
74
|
defaultRole: user
|
|
75
75
|
superAdminRole: super_admin
|
|
76
76
|
```
|
|
77
77
|
|
|
78
|
+
约定环境变量优先于 YAML,例如 `HAI_IAM_PASSWORD_MINLENGTH`、
|
|
79
|
+
`HAI_IAM_SESSION_MAXAGE`、`HAI_IAM_SECURITY_MAXLOGINATTEMPTS`。
|
|
80
|
+
|
|
78
81
|
### 2. 初始化
|
|
79
82
|
|
|
80
83
|
```typescript
|
|
@@ -51,26 +51,29 @@ description: 使用 @h-ai/payment 接入微信支付、支付宝、Stripe 统一
|
|
|
51
51
|
```yaml
|
|
52
52
|
# config/_payment.yml(自行创建)
|
|
53
53
|
wechat:
|
|
54
|
-
appId:
|
|
55
|
-
mchId:
|
|
56
|
-
apiV3Key:
|
|
57
|
-
privateKey:
|
|
58
|
-
serialNo:
|
|
59
|
-
platformCert:
|
|
54
|
+
appId: ''
|
|
55
|
+
mchId: ''
|
|
56
|
+
apiV3Key: ''
|
|
57
|
+
privateKey: ''
|
|
58
|
+
serialNo: ''
|
|
59
|
+
platformCert: '' # 可选,回调验签需要
|
|
60
60
|
|
|
61
61
|
alipay:
|
|
62
|
-
appId:
|
|
63
|
-
privateKey:
|
|
64
|
-
alipayPublicKey:
|
|
62
|
+
appId: ''
|
|
63
|
+
privateKey: ''
|
|
64
|
+
alipayPublicKey: ''
|
|
65
65
|
signType: RSA2 # 可选,默认 RSA2
|
|
66
66
|
sandbox: false # 可选,默认 false
|
|
67
67
|
|
|
68
68
|
stripe:
|
|
69
|
-
secretKey:
|
|
70
|
-
webhookSecret:
|
|
69
|
+
secretKey: ''
|
|
70
|
+
webhookSecret: ''
|
|
71
71
|
webhookToleranceSeconds: 300 # 可选,默认 300 秒
|
|
72
72
|
```
|
|
73
73
|
|
|
74
|
+
约定环境变量优先于 YAML,例如 `HAI_PAYMENT_WECHAT_APIV3KEY`、
|
|
75
|
+
`HAI_PAYMENT_STRIPE_WEBHOOKSECRET`。生产密钥只放环境或密钥管理器。
|
|
76
|
+
|
|
74
77
|
### 2. 初始化(服务端)
|
|
75
78
|
|
|
76
79
|
```typescript
|
|
@@ -88,6 +88,9 @@ dnd:
|
|
|
88
88
|
end: '08:00'
|
|
89
89
|
```
|
|
90
90
|
|
|
91
|
+
`providers` 是数组,因此示例保留稳定、可读的显式厂商变量名;若同时设置
|
|
92
|
+
按索引生成的约定变量(如 `HAI_REACH_PROVIDERS_0_HOST`),约定变量优先。
|
|
93
|
+
|
|
91
94
|
API Provider 只接受 HTTP(S) URL。回调地址若来自不可信输入,业务层必须先做域名 allowlist,并由企业网络出口策略限制内网访问;不要在日志中记录完整 URL、收件人或第三方响应体。
|
|
92
95
|
|
|
93
96
|
### 2. 初始化与关闭
|
|
@@ -41,13 +41,13 @@ description: 使用 @h-ai/reldb 进行 SQLite/PostgreSQL/MySQL 的初始化、SQ
|
|
|
41
41
|
|
|
42
42
|
```yaml
|
|
43
43
|
# config/_db.yml
|
|
44
|
-
type:
|
|
45
|
-
database:
|
|
44
|
+
type: sqlite
|
|
45
|
+
database: ./data/app.db
|
|
46
46
|
# PostgreSQL/MySQL 额外字段:
|
|
47
|
-
# host:
|
|
48
|
-
# port:
|
|
49
|
-
# user:
|
|
50
|
-
# password:
|
|
47
|
+
# host: localhost
|
|
48
|
+
# port: 5432
|
|
49
|
+
# user: postgres
|
|
50
|
+
# password: ''
|
|
51
51
|
operationLog:
|
|
52
52
|
read: false # query / get / queryPage
|
|
53
53
|
write: false # DDL, execute / batch, tx.begin / tx.commit / tx.rollback
|
|
@@ -55,6 +55,9 @@ operationLog:
|
|
|
55
55
|
level: debug # info | debug | trace
|
|
56
56
|
```
|
|
57
57
|
|
|
58
|
+
约定环境变量优先于 YAML,例如 `HAI_DB_TYPE`、`HAI_DB_DATABASE`、
|
|
59
|
+
`HAI_DB_OPERATIONLOG_LEVEL`。
|
|
60
|
+
|
|
58
61
|
`operationLog` 在 reldb provider-base 的 DML 操作层统一输出日志,不要在 `reldb-main.ts`、各 raw provider 或调用方重复包装。`read` 覆盖 `query/get/queryPage`,`write` 覆盖 DDL、`execute/batch` 与 `tx.begin/commit/rollback`,`maxLength` 用于截断序列化后的 SQL 参数和批量语句,`level` 默认 `debug`。
|
|
59
62
|
|
|
60
63
|
### 2. 初始化与关闭
|
|
@@ -36,6 +36,7 @@ description: 使用 @h-ai/serv 将 oRPC contract 挂载为最小 HTTP App 抽象
|
|
|
36
36
|
- 自定义 `ServContext`(注入 session、tenant 等请求级数据)
|
|
37
37
|
- 生成 OpenAPI 3.1 文档供外部工具消费
|
|
38
38
|
- 通过 `config/_serv.yml` 管理 API 前缀、OpenAPI、docs、health、rpc 等 HTTP 挂载配置
|
|
39
|
+
- 通过 `serv.createRuntimeSecurityPolicy(...)` 对生产 CORS Origin、原生 WebView Origin、Secure Cookie 与 API 文档暴露执行 fail-closed 策略
|
|
39
40
|
- 启用传输加密:`serv.createApp({ transport: { crypto } })`
|
|
40
41
|
- 高级场景通过 `createApp({ middlewares })` 自定义 HTTP middleware,或通过共享类型自定义 procedure wrapper
|
|
41
42
|
- 为 Audio WebSocket 配置一次性 ticket 校验、IAM/模型/配额授权与并发释放钩子
|
|
@@ -91,6 +92,40 @@ serv.listen(app, {
|
|
|
91
92
|
})
|
|
92
93
|
```
|
|
93
94
|
|
|
95
|
+
生产部署先从 `_core.yml.env` 与 `_serv.yml.cors` 创建安全策略;不要在业务应用重复实现 Origin 解析:
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
const security = serv.createRuntimeSecurityPolicy({
|
|
99
|
+
environment: coreConfig.env,
|
|
100
|
+
corsOrigin: servConfig.cors.origin,
|
|
101
|
+
nativeOrigins: servConfig.cors.nativeOrigins,
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
const app = serv.createApp({
|
|
105
|
+
contract,
|
|
106
|
+
procedures,
|
|
107
|
+
http: {
|
|
108
|
+
...servConfig.http,
|
|
109
|
+
openapi: security.exposeApiDocs ? servConfig.http.openapi : false,
|
|
110
|
+
docs: security.exposeApiDocs ? servConfig.http.docs : false,
|
|
111
|
+
},
|
|
112
|
+
refreshCookie: { secure: security.secureRefreshCookie },
|
|
113
|
+
middlewares: [{
|
|
114
|
+
middleware: serv.cors({
|
|
115
|
+
origin: security.allowOrigin,
|
|
116
|
+
credentials: servConfig.cors.credentials,
|
|
117
|
+
allowedHeaders: [...servConfig.cors.allowedHeaders],
|
|
118
|
+
exposedHeaders: [...servConfig.cors.exposedHeaders],
|
|
119
|
+
}),
|
|
120
|
+
}],
|
|
121
|
+
})
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
- 生产环境 `cors.origin` 禁止为空或 `*`,启动时 fail-fast。
|
|
125
|
+
- 普通 Web Origin 仅接受 HTTP(S);原生 Origin 通过 `cors.nativeOrigins` 显式允许 HTTP(S)、Capacitor 或 Tauri。
|
|
126
|
+
- Origin 按完整规范化值精确匹配,不使用前缀/后缀匹配。
|
|
127
|
+
- 生产环境自动要求 Secure refresh cookie,并把 OpenAPI/docs 作为安全上限关闭。
|
|
128
|
+
|
|
94
129
|
### 2.1 Audio WebSocket 安全接入
|
|
95
130
|
|
|
96
131
|
```typescript
|
|
@@ -383,7 +418,7 @@ const adminOnly = serv.requireAuth(
|
|
|
383
418
|
|
|
384
419
|
#### HTTP middleware
|
|
385
420
|
|
|
386
|
-
适用于请求日志、trace、限流、CORS、租户头校验等 **HTTP 层**
|
|
421
|
+
适用于请求日志、trace、限流、CORS、租户头校验等 **HTTP 层** 横切逻辑,也可挂载不适合进入 JSON/oRPC contract 的 WebSocket、文件和二进制端点。常见 CORS 场景可直接复用 `serv.cors(...)`:
|
|
387
422
|
|
|
388
423
|
```typescript
|
|
389
424
|
import type { ServMiddleware } from '@h-ai/serv'
|
|
@@ -416,13 +451,22 @@ const app = serv.createApp({
|
|
|
416
451
|
await next()
|
|
417
452
|
},
|
|
418
453
|
},
|
|
454
|
+
{
|
|
455
|
+
path: '/assets/*',
|
|
456
|
+
middleware: c => c.body(imageBytes, 200, {
|
|
457
|
+
'Content-Type': 'image/png',
|
|
458
|
+
'Cache-Control': 'public, max-age=31536000, immutable',
|
|
459
|
+
}),
|
|
460
|
+
},
|
|
419
461
|
],
|
|
420
462
|
})
|
|
421
463
|
```
|
|
422
464
|
|
|
423
465
|
执行顺序固定为:`securityHeaders` → `middlewares` → `transport`(若启用)→ health/refresh-cookie/OpenAPI/RPC/docs/oRPC routes。
|
|
424
466
|
|
|
425
|
-
- `middlewares` 先于 `transport` 执行,因此 CORS
|
|
467
|
+
- `middlewares` 先于 `transport` 执行,因此 CORS preflight 或二进制端点可以直接短路;短路响应仍带内置安全头,但不会经过 transport。
|
|
468
|
+
- 文件端点的允许方法、路径校验、MIME、缓存和访问授权由应用显式决定;框架不内置头像等业务概念。
|
|
469
|
+
- 若需要读取解密后的业务 body,请改用 context / procedure 层扩展。
|
|
426
470
|
- 浏览器若需读取自定义响应头(例如 transport 的 `X-Encrypted`),记得通过 `serv.cors({ exposedHeaders: [...] })` 显式暴露。
|
|
427
471
|
|
|
428
472
|
#### Context pipeline
|
|
@@ -41,15 +41,15 @@ description: 使用 @h-ai/storage 进行文件存储操作(本地/S3),包
|
|
|
41
41
|
|
|
42
42
|
```yaml
|
|
43
43
|
# config/_storage.yml
|
|
44
|
-
type:
|
|
44
|
+
type: local # local | s3
|
|
45
45
|
# 本地存储
|
|
46
|
-
root:
|
|
46
|
+
root: ./data/storage
|
|
47
47
|
# S3 存储(type=s3 时生效)
|
|
48
|
-
# endpoint:
|
|
49
|
-
# region:
|
|
50
|
-
# accessKeyId:
|
|
51
|
-
# secretAccessKey:
|
|
52
|
-
# bucket:
|
|
48
|
+
# endpoint: ''
|
|
49
|
+
# region: us-east-1
|
|
50
|
+
# accessKeyId: ''
|
|
51
|
+
# secretAccessKey: ''
|
|
52
|
+
# bucket: my-bucket
|
|
53
53
|
# forcePathStyle: false
|
|
54
54
|
# prefix: ''
|
|
55
55
|
# publicUrl: ''
|
|
@@ -60,6 +60,9 @@ operationLog:
|
|
|
60
60
|
level: debug # info | debug | trace
|
|
61
61
|
```
|
|
62
62
|
|
|
63
|
+
约定环境变量优先于 YAML,例如 `HAI_STORAGE_ROOT`、
|
|
64
|
+
`HAI_STORAGE_ACCESSKEYID`、`HAI_STORAGE_SECRETACCESSKEY`。
|
|
65
|
+
|
|
63
66
|
`operationLog` 在 Local/S3 Provider 的真实 file/dir/presign 操作处输出日志,不要在 `storage-main.ts` 或调用方重复包装。文件内容不会写入日志,上传数据只记录字节长度;`maxLength` 用于截断序列化后的参数,`level` 默认 `debug`。
|
|
64
67
|
|
|
65
68
|
### 2. 初始化与关闭
|
|
@@ -47,22 +47,22 @@ path: ./data/vecdb
|
|
|
47
47
|
|
|
48
48
|
# pgvector
|
|
49
49
|
# type: pgvector
|
|
50
|
-
# url:
|
|
50
|
+
# url: postgres://user:pass@localhost:5432/mydb
|
|
51
51
|
# indexType: hnsw
|
|
52
52
|
# tablePrefix: vec_
|
|
53
53
|
|
|
54
54
|
# Qdrant
|
|
55
55
|
# type: qdrant
|
|
56
|
-
# url:
|
|
57
|
-
# apiKey:
|
|
56
|
+
# url: http://localhost:6333
|
|
57
|
+
# apiKey: ''
|
|
58
58
|
|
|
59
59
|
# Chroma(嵌入式:提供 path 且无 url 时自动拉起本地 `chroma run` 服务并持久化)
|
|
60
60
|
# type: chroma
|
|
61
61
|
# path: ./data/chroma
|
|
62
62
|
# Chroma(直连已有服务,不拉起进程)
|
|
63
63
|
# type: chroma
|
|
64
|
-
# url:
|
|
65
|
-
# apiKey:
|
|
64
|
+
# url: http://localhost:8000
|
|
65
|
+
# apiKey: ''
|
|
66
66
|
# serverCommand: chroma # 嵌入式模式拉起服务的可执行命令(默认 chroma,来自 chromadb 包)
|
|
67
67
|
# startupTimeout: 30000 # 嵌入式服务就绪等待超时(毫秒)
|
|
68
68
|
operationLog:
|
|
@@ -72,6 +72,9 @@ operationLog:
|
|
|
72
72
|
level: debug # info | debug | trace
|
|
73
73
|
```
|
|
74
74
|
|
|
75
|
+
约定环境变量优先于 YAML,例如 `HAI_VECDB_URL`、
|
|
76
|
+
`HAI_VECDB_APIKEY`、`HAI_VECDB_OPERATIONLOG_LEVEL`。
|
|
77
|
+
|
|
75
78
|
`operationLog` 在集合/向量操作进入真实 Provider 前输出日志,不要在 `vecdb-main.ts` 或调用方重复包装。`maxLength` 用于截断序列化后的向量、文档和过滤条件,`level` 默认 `debug`。
|
|
76
79
|
|
|
77
80
|
### 2. 初始化与关闭
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/commands/cli-config-templates.ts"],"names":[],"mappings":"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","file":"chunk-CFCXOKGV.js","sourcesContent":["/**\n * @h-ai/cli — 配置文件模板\n *\n * 为各模块生成默认配置文件内容。\n * @module cli-config-templates\n */\n\nimport type { AiModuleConfig, CacheModuleConfig, CoreModuleConfig, DbModuleConfig, FeatureId, IamModuleConfig, ModuleConfigs, StorageModuleConfig } from '../cli-types.js'\n\n/**\n * 生成模块配置文件内容\n *\n * @param moduleKey - 模块标识(core/serv/db/cache/iam/storage/ai)\n * @param configs - 用户自定义配置值\n * @returns YAML 格式的配置内容\n */\nexport function generateConfigFile(moduleKey: string, configs?: ModuleConfigs): string {\n switch (moduleKey) {\n case 'core':\n return generateCoreConfig(configs?.core)\n case 'serv':\n return generateServConfig()\n case 'db':\n return generateDbConfig(configs?.db)\n case 'cache':\n return generateCacheConfig(configs?.cache)\n case 'iam':\n return generateIamConfig(configs?.iam)\n case 'storage':\n return generateStorageConfig(configs?.storage)\n case 'ai':\n return generateAiConfig(configs?.ai)\n case 'deploy':\n return generateDeployConfig()\n case 'vecdb':\n return generateVecdbConfig()\n case 'reach':\n return generateReachConfig()\n case 'scheduler':\n return generateSchedulerConfig()\n case 'audit':\n return generateAuditConfig()\n case 'payment':\n return generatePaymentConfig()\n default:\n return `# ${moduleKey} 配置\\n`\n }\n}\n\nfunction generateCoreConfig(cfg?: CoreModuleConfig): string {\n const name = cfg?.name ?? 'my-app'\n const locale = cfg?.defaultLocale ?? 'zh-CN'\n const supportedLocales = locale === 'en-US'\n ? ` - en-US\\n - zh-CN`\n : ` - zh-CN\\n - en-US`\n\n return `# =============================================================================\n# 应用配置\n# =============================================================================\n\nname: ${name}\nversion: 0.1.0\nenv: \\${HAI_ENV:development}\ndebug: \\${HAI_DEBUG:false}\n\ndefaultLocale: ${locale}\nsupportedLocales:\n${supportedLocales}\n`\n}\n\nfunction generateServConfig(): string {\n return `# =============================================================================\n# Service / HTTP 入口配置\n# =============================================================================\n\nhttp:\n apiPrefix: /api/v1\n openapi:\n path: /openapi.json\n docs:\n path: /docs\n health:\n path: /health\n readyPath: /ready\n rpc: false\n\n# 开发期默认关闭 transport;启用 @h-ai/crypto 后可按需改为对象配置\ntransport: false\n`\n}\n\nfunction generateDbConfig(cfg?: DbModuleConfig): string {\n const dbType = cfg?.type ?? 'sqlite'\n const database = cfg?.database ?? (dbType === 'sqlite' ? './data/app.db' : 'hai')\n const host = cfg?.host ?? 'localhost'\n const port = cfg?.port ?? (dbType === 'postgresql' ? 5432 : 3306)\n\n if (dbType === 'sqlite') {\n return `# =============================================================================\n# 数据库配置\n# =============================================================================\n\n# 数据库类型: sqlite | postgresql | mysql\ntype: sqlite\n\n# SQLite 数据库路径\ndatabase: \\${HAI_RELDB_DATABASE:${database}}\n\n# PostgreSQL/MySQL 配置(可选)\n# host: \\${HAI_RELDB_HOST:localhost}\n# port: \\${HAI_RELDB_PORT:5432}\n# user: \\${HAI_RELDB_USER:postgres}\n# password: \\${HAI_RELDB_PASSWORD:}\n`\n }\n\n const user = dbType === 'postgresql' ? 'postgres' : 'root'\n\n return `# =============================================================================\n# 数据库配置\n# =============================================================================\n\n# 数据库类型: sqlite | postgresql | mysql\ntype: ${dbType}\n\n# 数据库名称\ndatabase: \\${HAI_RELDB_DATABASE:${database}}\n\n# 连接配置\nhost: \\${HAI_RELDB_HOST:${host}}\nport: \\${HAI_RELDB_PORT:${port}}\nuser: \\${HAI_RELDB_USER:${user}}\npassword: \\${HAI_RELDB_PASSWORD:}\n`\n}\n\nfunction generateCacheConfig(cfg?: CacheModuleConfig): string {\n const cacheType = cfg?.type ?? 'memory'\n\n if (cacheType === 'memory') {\n return `# =============================================================================\n# 缓存配置\n# =============================================================================\n\n# 缓存类型: memory | redis\ntype: memory\n\n# Redis 配置(当 type 为 redis 时使用)\n# url: \\${HAI_CACHE_REDIS_URL:redis://localhost:6379/0}\n# host: \\${HAI_CACHE_REDIS_HOST:localhost}\n# port: \\${HAI_CACHE_REDIS_PORT:6379}\n# password: \\${HAI_CACHE_REDIS_PASSWORD:}\n# keyPrefix: \\${HAI_CACHE_KEY_PREFIX:hai:}\n`\n }\n\n const host = cfg?.host ?? 'localhost'\n const port = cfg?.port ?? 6379\n\n return `# =============================================================================\n# 缓存配置\n# =============================================================================\n\n# 缓存类型: memory | redis\ntype: redis\n\n# Redis 配置\nhost: \\${HAI_CACHE_REDIS_HOST:${host}}\nport: \\${HAI_CACHE_REDIS_PORT:${port}}\npassword: \\${HAI_CACHE_REDIS_PASSWORD:}\nkeyPrefix: \\${HAI_CACHE_KEY_PREFIX:hai:}\n`\n}\n\nfunction generateIamConfig(cfg?: IamModuleConfig): string {\n const loginPassword = cfg?.loginPassword ?? true\n const loginOtp = cfg?.loginOtp ?? false\n\n return `# =============================================================================\n# IAM 配置\n# =============================================================================\n\n# 登录配置\nlogin:\n password: ${loginPassword}\n otp: ${loginOtp}\n ldap: false\n\n# 密码策略\npassword:\n minLength: 8\n maxLength: 128\n requireUppercase: false\n requireLowercase: false\n requireNumber: false\n requireSpecialChar: false\n\n# 会话配置\nsession:\n maxAge: 86400 # 会话超时(秒),默认 24 小时\n sliding: true # 滑动窗口(每次请求刷新过期时间)\n singleDevice: false # 单设备登录\n refreshTokenMaxAge: 604800 # refreshToken 过期(秒),默认 7 天\n\n# RBAC 配置\nrbac:\n defaultRole: user\n\n# 是否自动创建默认数据\nseedDefaultData: true\n`\n}\n\nfunction generateStorageConfig(cfg?: StorageModuleConfig): string {\n const storageType = cfg?.type ?? 'local'\n\n if (storageType === 'local') {\n const localPath = cfg?.localPath ?? './data/uploads'\n return `# =============================================================================\n# 存储配置\n# =============================================================================\n\n# 默认 Provider\ndefaultProvider: local\n\n# Provider 配置\nproviders:\n local:\n type: local\n root: \\${HAI_STORAGE_PATH:${localPath}}\n maxFileSize: 10485760 # 10MB\n\n # S3 配置(可选)\n # s3:\n # type: s3\n # bucket: \\${HAI_STORAGE_S3_BUCKET}\n # region: \\${HAI_STORAGE_S3_REGION:us-east-1}\n # accessKeyId: \\${HAI_STORAGE_S3_ACCESS_KEY}\n # secretAccessKey: \\${HAI_STORAGE_S3_SECRET_KEY}\n # endpoint: \\${HAI_STORAGE_S3_ENDPOINT}\n # forcePathStyle: \\${HAI_STORAGE_S3_FORCE_PATH_STYLE:false}\n`\n }\n\n return `# =============================================================================\n# 存储配置\n# =============================================================================\n\n# 默认 Provider\ndefaultProvider: s3\n\n# Provider 配置\nproviders:\n s3:\n type: s3\n bucket: \\${HAI_STORAGE_S3_BUCKET}\n region: \\${HAI_STORAGE_S3_REGION:us-east-1}\n accessKeyId: \\${HAI_STORAGE_S3_ACCESS_KEY}\n secretAccessKey: \\${HAI_STORAGE_S3_SECRET_KEY}\n # endpoint: \\${HAI_STORAGE_S3_ENDPOINT}\n # forcePathStyle: \\${HAI_STORAGE_S3_FORCE_PATH_STYLE:false}\n\n # 本地存储(可选)\n # local:\n # type: local\n # root: \\${HAI_STORAGE_PATH:./data/uploads}\n # maxFileSize: 10485760 # 10MB\n`\n}\n\nfunction generateAiConfig(cfg?: AiModuleConfig): string {\n const model = cfg?.model ?? 'gpt-4o-mini'\n\n // AIConfigSchema 结构:llm(必填)+ 可选子模块\n // LLMConfigSchema 支持顶层 apiKey/model,以及多模型 models 数组\n return `# =============================================================================\n# AI 配置\n# =============================================================================\n\n# LLM 配置(必填)\nllm:\n # API Key(留空时自动读取 HAI_AI_LLM_API_KEY / OPENAI_API_KEY 环境变量)\n apiKey: \\${HAI_AI_LLM_API_KEY:}\n # Base URL(留空时自动读取 HAI_AI_LLM_BASE_URL / OPENAI_BASE_URL,默认 https://api.openai.com/v1)\n # baseUrl: \\${HAI_AI_LLM_BASE_URL:}\n # 默认模型\n model: ${model}\n # 全局最大 Token 数(默认 4096)\n # maxTokens: 4096\n # 采样温度(0-2,默认 0.7)\n # temperature: 0.7\n # 请求超时(毫秒,默认 60000)\n # timeout: 60000\n\n # 多模型配置(可选,用于场景路由)\n # models:\n # - id: fast\n # model: gpt-4o-mini\n # - id: smart\n # model: gpt-4o\n # apiKey: \\${HAI_AI_LLM_API_KEY:}\n\n # 场景模型映射(可选)\n # scenarios:\n # default: fast\n # analysis: smart\n`\n}\n\n/**\n * 根据选中的功能模块生成 .env.example 内容\n *\n * @param features - 选中的功能模块列表\n * @param configs - 用户自定义配置值\n * @returns .env.example 文件内容\n */\nexport function generateEnvExample(features: FeatureId[], configs?: ModuleConfigs): string {\n const sections: string[] = []\n\n // 文件头\n sections.push(`# =============================================================================\n# Environment Variables\n# =============================================================================\n#\n# Copy this file to .env and fill in actual values.\n# Config files in config/ reference these variables via \\${VAR_NAME:default} syntax.\n#\n# Naming convention: HAI_<MODULE>_<SETTING>\n# =============================================================================`)\n\n // 应用基础(始终生成)\n sections.push(`\n# =============================================================================\n# Application\n# =============================================================================\n# Environment: development | production | test\nHAI_ENV=development\n# Enable debug mode\nHAI_DEBUG=false`)\n\n // 数据库\n if (features.includes('db')) {\n const dbType = configs?.db?.type ?? 'sqlite'\n const dbDatabase = configs?.db?.database ?? (dbType === 'sqlite' ? './data/app.db' : 'hai')\n const isSqlite = dbType === 'sqlite'\n sections.push(`\n# =============================================================================\n# Database (@h-ai/reldb)\n# =============================================================================\n# Database type: sqlite | postgresql | mysql\nHAI_RELDB_TYPE=${dbType}\n# SQLite: file path; PostgreSQL/MySQL: database name\nHAI_RELDB_DATABASE=${dbDatabase}${isSqlite\n ? `\n# PostgreSQL/MySQL connection (uncomment when not using sqlite)\n# HAI_RELDB_HOST=localhost\n# HAI_RELDB_PORT=5432\n# HAI_RELDB_USER=postgres\n# HAI_RELDB_PASSWORD=`\n : `\n# Connection\nHAI_RELDB_HOST=${configs?.db?.host ?? 'localhost'}\nHAI_RELDB_PORT=${configs?.db?.port ?? (dbType === 'postgresql' ? 5432 : 3306)}\nHAI_RELDB_USER=${dbType === 'postgresql' ? 'postgres' : 'root'}\nHAI_RELDB_PASSWORD=`}`)\n }\n\n // IAM / Session\n if (features.includes('iam')) {\n sections.push(`\n# =============================================================================\n# IAM (@h-ai/iam)\n# =============================================================================\nHAI_IAM_SESSION_SECRET=change-me-to-a-strong-random-string-min-32-chars\n# HAI_IAM_PASSWORD_MIN_LENGTH=8\n# HAI_IAM_SESSION_MAX_AGE=86400\n# HAI_IAM_REFRESH_TOKEN_MAX_AGE=604800\n# HAI_IAM_MAX_LOGIN_ATTEMPTS=5\n# HAI_KIT_COOKIE_KEY= # 32-char hex for SM4-CBC cookie encryption\n`)\n }\n\n // 缓存\n if (features.includes('cache')) {\n const cacheType = configs?.cache?.type ?? 'memory'\n if (cacheType === 'redis') {\n sections.push(`\n# =============================================================================\n# Cache (@h-ai/cache)\n# =============================================================================\n# Cache type: memory | redis\nHAI_CACHE_TYPE=redis\n# Redis connection\nHAI_CACHE_REDIS_HOST=${configs?.cache?.host ?? 'localhost'}\nHAI_CACHE_REDIS_PORT=${configs?.cache?.port ?? 6379}\nHAI_CACHE_REDIS_PASSWORD=\n# HAI_CACHE_REDIS_URL=redis://localhost:6379/0\n# HAI_CACHE_REDIS_DB=0\n# HAI_CACHE_CONNECT_TIMEOUT=10000\n# HAI_CACHE_COMMAND_TIMEOUT=5000\n# HAI_CACHE_KEY_PREFIX=hai:`)\n }\n else {\n sections.push(`\n# =============================================================================\n# Cache (@h-ai/cache)\n# =============================================================================\n# Cache type: memory | redis\nHAI_CACHE_TYPE=memory\n# Redis connection (uncomment when type=redis)\n# HAI_CACHE_REDIS_URL=redis://localhost:6379/0\n# HAI_CACHE_REDIS_HOST=localhost\n# HAI_CACHE_REDIS_PORT=6379\n# HAI_CACHE_REDIS_PASSWORD=\n# HAI_CACHE_REDIS_DB=0\n# HAI_CACHE_CONNECT_TIMEOUT=10000\n# HAI_CACHE_COMMAND_TIMEOUT=5000\n# HAI_CACHE_KEY_PREFIX=hai:`)\n }\n }\n\n // 存储\n if (features.includes('storage')) {\n const storageType = configs?.storage?.type ?? 'local'\n if (storageType === 's3') {\n sections.push(`\n# =============================================================================\n# Storage (@h-ai/storage)\n# =============================================================================\n# Storage type: local | s3\nHAI_STORAGE_TYPE=s3\n# S3 / S3-compatible\nHAI_STORAGE_S3_BUCKET=\nHAI_STORAGE_S3_REGION=us-east-1\nHAI_STORAGE_S3_ACCESS_KEY=\nHAI_STORAGE_S3_SECRET_KEY=\n# HAI_STORAGE_S3_ENDPOINT= # Custom endpoint for MinIO / Aliyun OSS etc.\n# HAI_STORAGE_S3_FORCE_PATH_STYLE=false\n# Local storage (uncomment when type=local)\n# HAI_STORAGE_PATH=./data/uploads`)\n }\n else {\n sections.push(`\n# =============================================================================\n# Storage (@h-ai/storage)\n# =============================================================================\n# Storage type: local | s3\nHAI_STORAGE_TYPE=local\n# Local storage root path\nHAI_STORAGE_PATH=${configs?.storage?.localPath ?? './data/uploads'}\n# S3 / S3-compatible (uncomment when type=s3)\n# HAI_STORAGE_S3_BUCKET=\n# HAI_STORAGE_S3_REGION=us-east-1\n# HAI_STORAGE_S3_ACCESS_KEY=\n# HAI_STORAGE_S3_SECRET_KEY=\n# HAI_STORAGE_S3_ENDPOINT= # Custom endpoint for MinIO / Aliyun OSS etc.\n# HAI_STORAGE_S3_FORCE_PATH_STYLE=false`)\n }\n }\n\n // AI\n if (features.includes('ai')) {\n const provider = configs?.ai?.defaultProvider ?? 'openai'\n if (provider === 'anthropic') {\n sections.push(`\n# =============================================================================\n# AI (@h-ai/ai)\n# =============================================================================\n# OpenAI-compatible API Key (Anthropic models via OpenAI-compatible proxy)\nHAI_AI_LLM_API_KEY=\n# HAI_AI_LLM_BASE_URL= # Custom base URL\n# Optional compatibility fallback\n# OPENAI_API_KEY=`)\n }\n else if (provider === 'openai') {\n sections.push(`\n# =============================================================================\n# AI (@h-ai/ai)\n# =============================================================================\n# OpenAI / OpenAI-compatible\nHAI_AI_LLM_API_KEY=\n# HAI_AI_LLM_BASE_URL= # Custom base URL (e.g. local proxy, Azure)\n# Optional compatibility fallback\n# OPENAI_API_KEY=`)\n }\n else {\n sections.push(`\n# =============================================================================\n# AI (@h-ai/ai)\n# =============================================================================\n# OpenAI-compatible provider\nHAI_AI_LLM_API_KEY=\n# HAI_AI_LLM_BASE_URL= # Custom base URL\n# Optional compatibility fallback\n# OPENAI_API_KEY=`)\n }\n }\n\n // VecDB\n if (features.includes('vecdb')) {\n sections.push(`\n# =============================================================================\n# Vector Database (@h-ai/vecdb)\n# =============================================================================\n# Backend type: lancedb | pgvector | qdrant\nHAI_VECDB_TYPE=lancedb\n# LanceDB data path\nHAI_VECDB_PATH=./data/vecdb\n# Qdrant (uncomment when type=qdrant)\n# HAI_VECDB_QDRANT_URL=http://localhost:6333\n# HAI_VECDB_QDRANT_API_KEY=\n`)\n }\n\n // Reach\n if (features.includes('reach')) {\n sections.push(`\n# =============================================================================\n# Reach (@h-ai/reach)\n# =============================================================================\n# SMTP (uncomment when enabling email provider)\n# HAI_REACH_SMTP_HOST=smtp.example.com\n# HAI_REACH_SMTP_PORT=465\n# HAI_REACH_SMTP_USER=\n# HAI_REACH_SMTP_PASS=\n# HAI_REACH_SMTP_FROM=noreply@example.com\n`)\n }\n\n // Payment\n if (features.includes('payment')) {\n sections.push(`\n# =============================================================================\n# Payment (@h-ai/payment)\n# =============================================================================\n# WeChat Pay\n# HAI_PAYMENT_WECHAT_MCH_ID=\n# HAI_PAYMENT_WECHAT_API_V3_KEY=\n# HAI_PAYMENT_WECHAT_SERIAL_NO=\n# HAI_PAYMENT_WECHAT_PRIVATE_KEY=\n# HAI_PAYMENT_WECHAT_APP_ID=\n# Alipay\n# HAI_PAYMENT_ALIPAY_APP_ID=\n# HAI_PAYMENT_ALIPAY_PRIVATE_KEY=\n# HAI_PAYMENT_ALIPAY_PUBLIC_KEY=\n# Stripe\n# HAI_PAYMENT_STRIPE_SECRET_KEY=\n# HAI_PAYMENT_STRIPE_WEBHOOK_SECRET=\n`)\n }\n\n // 测试(始终生成)\n sections.push(`\n# =============================================================================\n# Testing\n# =============================================================================\n# E2E test mode (relaxes rate limiting etc.)\n# HAI_E2E=1\n`)\n\n return sections.join('\\n')\n}\n\nfunction generateDeployConfig(): string {\n return `# =============================================================================\n# 部署配置 (@h-ai/deploy)\n# =============================================================================\n# 部署平台凭证通过 ~/.hai/credentials.yml 管理\n# 环境变量引用格式: \\${VAR_NAME:default_value}\n\n# 部署平台\nprovider:\n type: vercel\n token: \\${HAI_DEPLOY_VERCEL_TOKEN}\n\n# 基础设施服务(按需开启)\nservices:\n # PostgreSQL 数据库 (Neon)\n # db:\n # provisioner: neon\n # apiKey: \\${HAI_DEPLOY_NEON_API_KEY}\n\n # Redis 缓存 (Upstash)\n # cache:\n # provisioner: upstash\n # email: \\${HAI_DEPLOY_UPSTASH_EMAIL}\n # apiKey: \\${HAI_DEPLOY_UPSTASH_API_KEY}\n\n # S3 存储 (Cloudflare R2)\n # storage:\n # provisioner: cloudflare-r2\n # accountId: \\${HAI_DEPLOY_CF_ACCOUNT_ID}\n # apiToken: \\${HAI_DEPLOY_CF_API_TOKEN}\n\n # 邮件 (Resend)\n # email:\n # provisioner: resend\n # apiKey: \\${HAI_DEPLOY_RESEND_API_KEY}\n\n # 短信 (阿里云)\n # sms:\n # provisioner: aliyun\n # accessKeyId: \\${HAI_DEPLOY_ALIYUN_ACCESS_KEY_ID}\n # accessKeySecret: \\${HAI_DEPLOY_ALIYUN_ACCESS_KEY_SECRET}\n`\n}\n\nfunction generateVecdbConfig(): string {\n return `# =============================================================================\n# 向量数据库配置 (@h-ai/vecdb)\n# =============================================================================\n\n# 数据库类型: lancedb | pgvector | qdrant\ntype: lancedb\n\n# LanceDB 数据目录\npath: \\${HAI_VECDB_PATH:./data/vecdb}\n\n# 距离度量: cosine | euclidean | dot\n# metric: cosine\n\n# pgvector 配置(当 type 为 pgvector 时使用)\n# type: pgvector\n# host: \\${HAI_RELDB_HOST:localhost}\n# port: \\${HAI_RELDB_PORT:5432}\n# database: \\${HAI_RELDB_DATABASE:hai}\n# user: \\${HAI_RELDB_USER:postgres}\n# password: \\${HAI_RELDB_PASSWORD:}\n# indexType: hnsw\n# tablePrefix: vec_\n\n# Qdrant 配置(当 type 为 qdrant 时使用)\n# type: qdrant\n# url: \\${HAI_VECDB_QDRANT_URL:http://localhost:6333}\n# apiKey: \\${HAI_VECDB_QDRANT_API_KEY:}\n`\n}\n\nfunction generateReachConfig(): string {\n return `# =============================================================================\n# 消息推送配置 (@h-ai/reach)\n# =============================================================================\n\nproviders:\n # 控制台输出(开发/测试用)\n - name: console_dev\n type: console\n\n # SMTP 邮件(按需开启)\n # - name: email\n # type: smtp\n # host: \\${HAI_REACH_SMTP_HOST:smtp.example.com}\n # port: \\${HAI_REACH_SMTP_PORT:465}\n # secure: true\n # user: \\${HAI_REACH_SMTP_USER:}\n # pass: \\${HAI_REACH_SMTP_PASS:}\n # from: \\${HAI_REACH_SMTP_FROM:noreply@example.com}\n\n # 阿里云短信(按需开启)\n # - name: sms\n # type: aliyun-sms\n # accessKeyId: \\${HAI_REACH_SMS_ACCESS_KEY}\n # accessKeySecret: \\${HAI_REACH_SMS_SECRET_KEY}\n # signName: MyApp\n\n# 模板(可选)\n# templates:\n# - name: verification_code\n# provider: email\n# subject: \"验证码: {code}\"\n# body: \"您的验证码是 {code},有效期 {minutes} 分钟。\"\n\n# 免打扰(可选)\n# dnd:\n# enabled: true\n# strategy: delay\n# start: \"22:00\"\n# end: \"08:00\"\n`\n}\n\nfunction generateSchedulerConfig(): string {\n return `# =============================================================================\n# 任务调度配置 (@h-ai/scheduler)\n# =============================================================================\n\n# 是否启用数据库日志(需要 @h-ai/reldb)\nenableDb: true\n\n# 执行日志表名\ntableName: hai_scheduler_logs\n\n# 任务定义表名\ntaskTableName: hai_scheduler_tasks\n\n# 分布式锁表名\nlockTableName: hai_scheduler_locks\n\n# 检查间隔(毫秒)\ntickInterval: 1000\n\n# 锁过期时间(毫秒)\nlockExpireMs: 300000\n`\n}\n\nfunction generateAuditConfig(): string {\n return `# =============================================================================\n# 审计日志配置 (@h-ai/audit)\n# =============================================================================\n\n# 审计日志表名\ntableName: hai_audit_logs\n\n# 用户表名(用于 JOIN 查询)\nuserTable: hai_iam_users\n\n# 用户表主键列名\nuserIdColumn: id\n\n# 用户名列名\nuserNameColumn: username\n`\n}\n\nfunction generatePaymentConfig(): string {\n return `# =============================================================================\n# 支付配置 (@h-ai/payment)\n# =============================================================================\n\n# 微信支付(按需开启)\n# wechat:\n# mchId: \\${HAI_PAYMENT_WECHAT_MCH_ID}\n# apiV3Key: \\${HAI_PAYMENT_WECHAT_API_V3_KEY}\n# serialNo: \\${HAI_PAYMENT_WECHAT_SERIAL_NO}\n# privateKey: \\${HAI_PAYMENT_WECHAT_PRIVATE_KEY}\n# appId: \\${HAI_PAYMENT_WECHAT_APP_ID}\n\n# 支付宝(按需开启)\n# alipay:\n# appId: \\${HAI_PAYMENT_ALIPAY_APP_ID}\n# privateKey: \\${HAI_PAYMENT_ALIPAY_PRIVATE_KEY}\n# alipayPublicKey: \\${HAI_PAYMENT_ALIPAY_PUBLIC_KEY}\n# signType: RSA2\n# sandbox: false\n\n# Stripe(按需开启)\n# stripe:\n# secretKey: \\${HAI_PAYMENT_STRIPE_SECRET_KEY}\n# webhookSecret: \\${HAI_PAYMENT_STRIPE_WEBHOOK_SECRET}\n`\n}\n"]}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
/** HTTP 与业务 API 路径常量,前端、服务端与 E2E 共用。 */
|
|
2
|
-
export const APP_HTTP_ROUTES = {
|
|
3
|
-
apiPrefix: '/api/v1',
|
|
4
|
-
health: '/health',
|
|
5
|
-
ready: '/ready',
|
|
6
|
-
openapi: '/openapi.json',
|
|
7
|
-
docs: '/docs',
|
|
8
|
-
} as const
|
|
9
|
-
|
|
10
|
-
/** app 领域 contract 内部路径,最终会挂载到 APP_HTTP_ROUTES.apiPrefix 下。 */
|
|
11
|
-
export const APP_CONTRACT_ROUTES = {
|
|
12
|
-
info: '/app/info',
|
|
13
|
-
echo: '/app/echo',
|
|
14
|
-
} as const
|
|
15
|
-
|
|
16
|
-
/** 已拼接 apiPrefix 的实际 HTTP 路径,便于测试和非 typed client 场景使用。 */
|
|
17
|
-
export const APP_API_ROUTES = {
|
|
18
|
-
health: APP_HTTP_ROUTES.health,
|
|
19
|
-
ready: APP_HTTP_ROUTES.ready,
|
|
20
|
-
openapi: APP_HTTP_ROUTES.openapi,
|
|
21
|
-
docs: APP_HTTP_ROUTES.docs,
|
|
22
|
-
info: `${APP_HTTP_ROUTES.apiPrefix}${APP_CONTRACT_ROUTES.info}`,
|
|
23
|
-
echo: `${APP_HTTP_ROUTES.apiPrefix}${APP_CONTRACT_ROUTES.echo}`,
|
|
24
|
-
} as const
|
|
25
|
-
|
|
26
|
-
/** 当前工程启用的前端目标。 */
|
|
27
|
-
export const ENABLED_FRONTENDS = {{{fullstack.selectedFrontendsJson}}} as const
|