@h-ai/cli 0.1.0-alpha.65 → 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 -251
- package/README.ja.md +67 -252
- package/README.md +66 -251
- package/dist/{chunk-47V5OT22.js → chunk-7DSURTJ4.js} +32 -115
- package/dist/chunk-7DSURTJ4.js.map +1 -0
- package/dist/{chunk-IPG3QNYK.js → chunk-AOQ4N657.js} +212 -8
- package/dist/chunk-AOQ4N657.js.map +1 -0
- package/dist/cli-config-templates-6AHNUGBN.js +5 -0
- package/dist/{cli-config-templates-SJ2MBIR7.js.map → cli-config-templates-6AHNUGBN.js.map} +1 -1
- package/dist/{cli-skill-templates-A5VIR5J6.js → cli-skill-templates-FC3QRKDO.js} +3 -3
- package/dist/{cli-skill-templates-A5VIR5J6.js.map → cli-skill-templates-FC3QRKDO.js.map} +1 -1
- package/dist/index.js +644 -391
- package/dist/index.js.map +1 -1
- package/dist/{src-6PPBKQ6Q.js → src-2V6VP4I5.js} +972 -520
- 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 +18 -12
- package/templates/apps/api/.gitignore.hbs +9 -1
- 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 -3
- package/templates/apps/api/hai.build.json.hbs +1 -0
- package/templates/apps/api/images/app.Dockerfile.hbs +35 -0
- package/templates/apps/api/package.json.hbs +10 -8
- package/templates/apps/fullstack/.dockerignore.hbs +18 -14
- package/templates/apps/fullstack/.gitignore.hbs +9 -1
- package/templates/apps/fullstack/apps/{{projectName}}-app/README.md.hbs +23 -8
- package/templates/apps/fullstack/apps/{{projectName}}-app/index.html.hbs +1 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/package.json.hbs +0 -2
- 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 +3 -3
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/README.md.hbs +25 -10
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/package.json.hbs +2 -3
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/src/lib.rs.hbs +73 -2
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/tauri.conf.json.hbs +2 -2
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/vite.config.ts.hbs +2 -2
- package/templates/apps/fullstack/apps/{{projectName}}-miniapp/README.md.hbs +16 -6
- package/templates/apps/fullstack/apps/{{projectName}}-web/README.md.hbs +24 -10
- package/templates/apps/fullstack/apps/{{projectName}}-web/package.json.hbs +0 -2
- package/templates/apps/fullstack/apps/{{projectName}}-web/vite.config.ts.hbs +3 -3
- package/templates/apps/fullstack/e2e/fullstack.spec.ts.hbs +12 -1
- package/templates/apps/fullstack/hai.build.json.hbs +7 -0
- 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 +7 -12
- package/templates/apps/fullstack/packages/{{projectName}}-contract/README.md.hbs +23 -2
- package/templates/apps/fullstack/packages/{{projectName}}-serv/README.md.hbs +28 -7
- package/templates/apps/fullstack/packages/{{projectName}}-serv/package.json.hbs +0 -1
- package/templates/apps/fullstack/packages/{{projectName}}-shared/.gitignore.hbs +7 -0
- package/templates/apps/fullstack/packages/{{projectName}}-shared/README.md.hbs +33 -12
- 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/index.html.hbs +1 -0
- package/templates/apps/mobile-app/public/runtime-config.js +1 -0
- package/templates/apps/mobile-app/src/App.svelte.hbs +2 -2
- package/templates/apps/mobile-app/src/lib/api.ts.hbs +2 -1
- package/templates/apps/mobile-app/src/lib/app-config.ts.hbs +3 -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 +18 -13
- package/templates/base/.gitignore.hbs +9 -1
- package/templates/base/e2e/app.spec.ts.hbs +10 -0
- package/templates/base/hai.build.json.hbs +1 -0
- package/templates/base/images/app.Dockerfile.hbs +64 -0
- package/templates/base/{docker → images/web}/nginx.conf.hbs +6 -0
- package/templates/base/package.json.hbs +20 -19
- package/templates/base/svelte.config.js.hbs +2 -0
- package/templates/base/vite.config.ts.hbs +9 -3
- package/templates/shared/.gitattributes +11 -0
- package/templates/shared/container-runtime-config.sh +30 -0
- package/templates/shared/run-e2e.mjs +65 -0
- package/templates/shared/vite-runtime-config.ts +54 -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 +6 -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 +24 -3
- 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 +28 -271
- package/templates/skills/hai-deploy/middleware.md +6 -18
- 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 +11 -5
- package/templates/skills/hai-vecdb/SKILL.md +2 -2
- package/dist/chunk-47V5OT22.js.map +0 -1
- package/dist/chunk-IPG3QNYK.js.map +0 -1
- package/dist/cli-config-templates-SJ2MBIR7.js +0 -5
- package/dist/src-6PPBKQ6Q.js.map +0 -1
- package/templates/apps/api/Dockerfile.hbs +0 -48
- package/templates/apps/fullstack/Dockerfile.hbs +0 -69
- package/templates/base/Dockerfile.hbs +0 -79
- /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
|
@@ -1,292 +1,49 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: hai-deploy
|
|
3
|
-
description: "
|
|
3
|
+
description: "构建并发布或交付已有镜像:完整离线包、按服务更新、SSH 与跨平台手工部署、失败恢复;也支持 Vercel prebuilt。"
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# hai-deploy
|
|
7
7
|
|
|
8
8
|
## 能力契约
|
|
9
9
|
|
|
10
|
-
`hai deploy` 在模块/配置缺失或凭证、扫描、初始化、部署、关闭失败时返回非零退出码。自动化流程必须以退出码判断成功,不要只判断命令是否完成或是否输出日志。
|
|
11
|
-
|
|
12
10
|
| 项目 | 契约 |
|
|
13
11
|
| --- | --- |
|
|
14
|
-
| 能力 |
|
|
15
|
-
| 适用场景 |
|
|
16
|
-
| 输入 |
|
|
17
|
-
| 输出 |
|
|
18
|
-
| 限制 |
|
|
19
|
-
|
|
20
|
-
> `@h-ai/deploy` 提供两种部署方式:
|
|
21
|
-
> 1. **vercel** — 将 SvelteKit(adapter-vercel)应用部署到 Vercel,并自动开通 Neon/Upstash/R2/Resend/阿里云等云基础设施;
|
|
22
|
-
> 2. **docker-ssh** — 将原生 fullstack API 或 SvelteKit(adapter-node)应用容器化后经 SSH 部署到任意具备 docker/podman + compose 的 Linux 主机,按所选功能自动生成 compose sidecar。
|
|
23
|
-
|
|
24
|
-
## 运行环境
|
|
25
|
-
|
|
26
|
-
> ⚠️ **Node.js CLI / 服务端模块。** 通过 `hai deploy` CLI 命令或在 Node.js 脚本中使用。
|
|
27
|
-
> docker-ssh 方式要求本地安装 docker 或 podman(`auto` 优先 podman)。
|
|
28
|
-
|
|
29
|
-
当前 Node 要求 `^22.22.2 || ^24.15.0 || >=26.0.0`,生成工程固定 pnpm 12.11.2;Node 20 / pnpm 9.5.0 不受支持。Windows 必须有已启动、能构建目标 Linux 架构的容器引擎,不能把仅安装客户端或远程有 Docker 当作本地可构建。
|
|
30
|
-
|
|
31
|
-
## 适用场景
|
|
32
|
-
|
|
33
|
-
- 一键部署 SvelteKit 应用到 Vercel
|
|
34
|
-
- 容器化后经 SSH 部署到自有服务器 / 云主机 / 内网虚拟机(docker-ssh)
|
|
35
|
-
- 自动开通 PostgreSQL、Redis、S3 等云服务,或按功能生成自建 sidecar
|
|
36
|
-
- 管理部署凭证(~/.hai/credentials.yml)
|
|
37
|
-
- 扫描应用依赖,自动检测所需服务
|
|
38
|
-
- CLI 部署流程(`hai deploy`)
|
|
39
|
-
|
|
40
|
-
## 使用步骤
|
|
12
|
+
| 能力 | 配置环境与中间件,交付完整离线包或发布选中服务 |
|
|
13
|
+
| 适用场景 | 一体化构建发布、独立打包、隔离网络手工部署、增量更新与故障排查 |
|
|
14
|
+
| 输入 | package.json、hai.build.json、运行依赖锁文件、目标环境、已有制品或明确构建要求 |
|
|
15
|
+
| 输出 | 带校验和的 tar、跨平台部署入口与分步结果,或经过 HTTP 检查的部署结果 |
|
|
16
|
+
| 限制 | SDK 只消费产物;CLI --build 显式先构建。首次安装必须完整交付,packaged 不代表服务器 ready。云资源需要外网。 |
|
|
41
17
|
|
|
42
|
-
|
|
18
|
+
## 操作流程
|
|
43
19
|
|
|
44
|
-
|
|
20
|
+
1. 读取项目 scripts、images/ 中的 Dockerfile、hai.build.json 与服务目录配置。创建默认带开发期部署工具;用户明确 --no-deploy 时不生成部署入口。构建细节见 [hai-build](../hai-build/SKILL.md)。
|
|
21
|
+
2. 选择环境,验证 .env.<mode> 存在,再处理 SSH 与必要中间件。使用人类可读字段说明,复用有效配置;私有参数写 .env.local,私钥仅引用路径。环境规则见 [middleware.md](middleware.md)。
|
|
22
|
+
3. 先校验所选制品、版本、服务和 SHA-256。--build 只构建所选应用镜像;不带它时缺产物明确失败,不隐式补构建。格式版本 3 记录 appVersion、buildId、partial、hasWeb、镜像归档及显式配置的 appContainerPort/webContainerPort;部分构建也保留未选镜像端口,缺省使用部署默认值。同一应用版本可重建,镜像引用区分实际内容;禁止 latest 与无版本镜像。
|
|
23
|
+
4. 完整包自动导出实际自建中间件镜像并去重;只更新 Web 不重新打包 API/中间件。打包机需容器引擎导出中间件,服务器仅 load,禁止 pull/build。外部连接及云开通不能伪称为离线自包含服务。
|
|
24
|
+
5. --services 使用目标服务器最后成功状态为基线,检查端口/拓扑及未选依赖,保留未选配置、凭据、网络和卷。脚本先按项目/服务标签仅删除选中旧容器(保留卷),再 up -d --no-deps --no-build --pull never --no-recreate 创建新容器并保留其他容器,避免 Compose Provider 连带重建反向依赖,禁止整个项目 down。storage-init 是一次性创建 S3 桶,涉及存储初始化才运行。仅更新 storage 也需含 app 镜像的制品运行初始化;最近为 web-only 时,用 --artifact-dir 选择原完整制品,或先 pnpm build --target docker --images app,不会更新运行中的 app 服务。
|
|
25
|
+
6. --bundle-only 不连接 SSH,同时输出 tar 和 -deploy/ 操作目录。Windows deploy.ps1、Linux/macOS deploy.sh 上传并执行包内 apply.sh;完全隔离时搬运 tar,独立目录解包执行 sh apply.sh,可传部署根目录。脚本不依赖源码、Node 或 pnpm。
|
|
26
|
+
7. 工具预检、基线、上传、校验、镜像导入、服务更新和 HTTP 检查分别报告。失败返回非零退出码,结果文件保留失败阶段;只有检查成功才更新服务器 current。已有部署失败尝试恢复所选服务的旧容器和配置;数据卷保留,已写业务数据不回退,恢复后仍需验证健康。禁止把本地打包记录当服务器成功状态。
|
|
45
27
|
|
|
46
|
-
|
|
47
|
-
# config/_deploy.yml
|
|
48
|
-
provider:
|
|
49
|
-
type: vercel
|
|
50
|
-
token: ${HAI_DEPLOY_PROVIDER_TOKEN}
|
|
28
|
+
## CLI
|
|
51
29
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
apiKey: ${HAI_DEPLOY_SERVICES_CACHE_APIKEY}
|
|
60
|
-
storage:
|
|
61
|
-
provisioner: cloudflare-r2
|
|
62
|
-
accountId: ${HAI_DEPLOY_SERVICES_STORAGE_ACCOUNTID}
|
|
63
|
-
apiToken: ${HAI_DEPLOY_SERVICES_STORAGE_APITOKEN}
|
|
64
|
-
accessKeyId: ${HAI_DEPLOY_SERVICES_STORAGE_ACCESSKEYID}
|
|
65
|
-
secretAccessKey: ${HAI_DEPLOY_SERVICES_STORAGE_SECRETACCESSKEY}
|
|
66
|
-
email:
|
|
67
|
-
provisioner: resend
|
|
68
|
-
apiKey: ${HAI_DEPLOY_SERVICES_EMAIL_APIKEY}
|
|
69
|
-
from: ${HAI_DEPLOY_SERVICES_EMAIL_FROM}
|
|
70
|
-
sms:
|
|
71
|
-
provisioner: aliyun
|
|
72
|
-
accessKeyId: ${HAI_DEPLOY_SERVICES_SMS_ACCESSKEYID}
|
|
73
|
-
accessKeySecret: ${HAI_DEPLOY_SERVICES_SMS_ACCESSKEYSECRET}
|
|
74
|
-
signName: ${HAI_DEPLOY_SERVICES_SMS_SIGNNAME}
|
|
30
|
+
```sh
|
|
31
|
+
pnpm run deploy --env prod --build
|
|
32
|
+
pnpm build --target docker
|
|
33
|
+
pnpm run deploy --env prod --artifact-dir .hai/build/docker/<buildId>
|
|
34
|
+
pnpm run deploy --env prod --bundle-only --output release-prod.tar
|
|
35
|
+
pnpm build --target docker --images web
|
|
36
|
+
pnpm run deploy --env prod --services web
|
|
75
37
|
```
|
|
76
38
|
|
|
77
|
-
|
|
78
|
-
`HAI_DEPLOY_PROVIDER_TOKEN` 等约定变量,约定变量仍拥有最高优先级。
|
|
79
|
-
|
|
80
|
-
#### docker-ssh 配置
|
|
81
|
-
|
|
82
|
-
将上面的 `provider` 替换为 docker-ssh(远程主机需 SSH + docker/podman + compose):
|
|
83
|
-
|
|
84
|
-
```yaml
|
|
85
|
-
# config/_deploy.yml
|
|
86
|
-
provider:
|
|
87
|
-
type: docker-ssh
|
|
88
|
-
ssh:
|
|
89
|
-
host: ${HAI_DEPLOY_PROVIDER_SSH_HOST}
|
|
90
|
-
port: 22
|
|
91
|
-
username: deploy
|
|
92
|
-
identityFile: ${HAI_DEPLOY_PROVIDER_SSH_IDENTITYFILE} # 私钥文件路径,省略则用 ssh-agent
|
|
93
|
-
remote:
|
|
94
|
-
baseDir: /opt/hai/apps
|
|
95
|
-
runtime: auto # auto 优先 podman,其次 docker
|
|
96
|
-
expose:
|
|
97
|
-
type: port
|
|
98
|
-
containerPort: 3000
|
|
99
|
-
hostPort: 18080 # 访问地址 http://<host>:18080
|
|
100
|
-
|
|
101
|
-
container:
|
|
102
|
-
runtime: auto # 本地构建运行时,可选
|
|
103
|
-
|
|
104
|
-
services:
|
|
105
|
-
# 启用的服务会被加入远程 compose sidecar(postgres/redis/rustfs 或 minio)
|
|
106
|
-
# 若配置了云 Provisioner(如 neon),则优先用云资源,不再自建 sidecar
|
|
107
|
-
db:
|
|
108
|
-
provisioner: neon
|
|
109
|
-
apiKey: ${HAI_DEPLOY_SERVICES_DB_APIKEY}
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
docker-ssh 要点:
|
|
113
|
-
|
|
114
|
-
- 应用镜像优先使用工程自带的 Dockerfile;SvelteKit 需要 adapter-node,原生 fullstack 服务自动生成以 `dist/index.js` 启动的镜像。
|
|
115
|
-
- compose 按 `deploy.scan()` 识别的服务**及其后端类型**生成匹配 sidecar 并注入 `HAI_*` 覆盖变量:
|
|
116
|
-
- `db`:`postgresql`→postgres、`mysql`→mysql、`sqlite`→数据卷(无 sidecar)→ `HAI_DB`
|
|
117
|
-
- `cache`:`redis`→redis → `HAI_CACHE`;`storage`:`s3`→RustFS/MinIO(container.s3Engine) → `HAI_STORAGE`
|
|
118
|
-
- `vecdb`:`qdrant`/`pgvector`/`chroma`→对应 sidecar、`lancedb`→数据卷 → `HAI_VECDB`
|
|
119
|
-
- 若同一服务已配置云 Provisioner(如 neon 提供 `HAI_DB`),优先用云资源,不再自建 sidecar
|
|
120
|
-
- sidecar 密码写被忽略的本机私有配置及远程 `.env.runtime`(权限 600),不入公共环境文件或 `compose.yml`
|
|
121
|
-
- `identityFile` 是私钥**路径**(非私钥内容);禁止把私钥内容写进 `_deploy.yml`
|
|
122
|
-
|
|
123
|
-
### 2. 初始化与关闭
|
|
124
|
-
|
|
125
|
-
```typescript
|
|
126
|
-
import { deploy } from '@h-ai/deploy'
|
|
127
|
-
|
|
128
|
-
deploy.credentials.load()
|
|
129
|
-
|
|
130
|
-
await deploy.init({
|
|
131
|
-
provider: { type: 'vercel', token: 'vel_xxx' },
|
|
132
|
-
services: {
|
|
133
|
-
db: { provisioner: 'neon', apiKey: 'neon_xxx' },
|
|
134
|
-
},
|
|
135
|
-
})
|
|
136
|
-
|
|
137
|
-
// 使用后关闭
|
|
138
|
-
await deploy.close()
|
|
139
|
-
```
|
|
140
|
-
|
|
141
|
-
### 3. 扫描应用
|
|
142
|
-
|
|
143
|
-
```typescript
|
|
144
|
-
const scanResult = await deploy.scan('./apps/my-app', { environment: 'test' })
|
|
145
|
-
if (scanResult.success) {
|
|
146
|
-
const { appName, isSvelteKit, requiredServices } = scanResult.data
|
|
147
|
-
}
|
|
148
|
-
```
|
|
149
|
-
|
|
150
|
-
### 4. 开通基础设施
|
|
151
|
-
|
|
152
|
-
```typescript
|
|
153
|
-
const provResults = await deploy.provisionAll('my-project')
|
|
154
|
-
if (provResults.success) {
|
|
155
|
-
for (const prov of provResults.data) {
|
|
156
|
-
// prov.envVars 包含需要注入的环境变量
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
```
|
|
160
|
-
|
|
161
|
-
### 5. 完整部署
|
|
162
|
-
|
|
163
|
-
```typescript
|
|
164
|
-
const result = await deploy.deployApp('./apps/my-app', {
|
|
165
|
-
projectName: 'my-project',
|
|
166
|
-
skipProvision: false,
|
|
167
|
-
skipBuild: false,
|
|
168
|
-
})
|
|
169
|
-
if (result.success) {
|
|
170
|
-
// result.data.url — 部署地址
|
|
171
|
-
// result.data.deploymentId — 部署 ID
|
|
172
|
-
// result.data.envVarsSet — 已设置的环境变量列表
|
|
173
|
-
}
|
|
174
|
-
```
|
|
39
|
+
使用 pnpm run deploy,避免与 pnpm 自带 deploy 混淆。fullstack 指向 packages/<项目>-serv,API workspace 指向 apps/<项目>-service;这是扫描入口,不是镜像选择。--services 负责选择;--skip-middleware 只跳过配置及开通。--yes 复用有效配置,缺值时不盲目继续。
|
|
175
40
|
|
|
176
|
-
##
|
|
41
|
+
## SDK
|
|
177
42
|
|
|
178
|
-
|
|
179
|
-
| ---------------------- | -------------------------------------------------------------------------- | ------------ |
|
|
180
|
-
| `deploy.credentials.load` | `() => HaiResult<string[]>` | 加载凭证到 `process.env` |
|
|
181
|
-
| `deploy.credentials.save` | `(key: string, value: string) => HaiResult<void>` | 保存单个凭证 |
|
|
182
|
-
| `deploy.credentials.saveAll` | `(entries: Record<string, string>) => HaiResult<void>` | 批量保存凭证 |
|
|
183
|
-
| `deploy.credentials.getPath` | `() => string` | 获取凭证文件路径 |
|
|
184
|
-
| `deploy.init` | `(config: DeployConfigInput) => Promise<HaiResult<void>>` | 初始化模块 |
|
|
185
|
-
| `deploy.close` | `() => Promise<void>` | 关闭模块 |
|
|
186
|
-
| `deploy.scan` | `(appDir: string, options?: DeployScanOptions) => Promise<HaiResult<ScanResult>>` | 按环境扫描(不依赖 init) |
|
|
187
|
-
| `deploy.provisionAll` | `(projectName: string, services?: ServiceType[]) => Promise<HaiResult<ProvisionResult[]>>` | 开通指定或全部已配置服务 |
|
|
188
|
-
| `deploy.deployApp` | `(appDir: string, options?) => Promise<HaiResult<DeployResult>>` | 完整部署 |
|
|
189
|
-
| `deploy.config` | `DeployConfig \| null` | 当前配置 |
|
|
190
|
-
| `deploy.isInitialized` | `boolean` | 初始化状态 |
|
|
43
|
+
deploy.init 后检查 HaiResult.success,deploy.deployApp 接收 artifactDir、bundleOnly、selectedServices、runtime 与 onProgress;finally 中 deploy.close。deploy.scan 和 deploy.credentials.* 不依赖 init。Vercel 只消费已有 .vercel/output,不执行源码构建,也不支持 Compose 离线/部分更新。
|
|
191
44
|
|
|
192
|
-
##
|
|
193
|
-
|
|
194
|
-
所有凭证操作都通过 `deploy.credentials.*` 访问,模块入口不再单独导出自由函数。
|
|
195
|
-
|
|
196
|
-
> `deploy.credentials.*()` 与 `deploy.scan()` 都不依赖 `deploy.init()`,可用于 CLI 在读取 `_deploy.yml` 之前准备环境。
|
|
197
|
-
|
|
198
|
-
## 配置消费与失败恢复
|
|
199
|
-
|
|
200
|
-
云资源配置消费:Neon 输出 HAI_DB(PostgreSQL JSON),Upstash 输出 HAI_CACHE(Redis rediss URL JSON),R2 输出 HAI_STORAGE(扁平 S3 JSON)。邮件/短信输出 HAI_REACH_PROVIDERS 数组,deployApp 合并两个渠道并保留 YAML 中的消息模板;此覆盖替换原 providers 数组。Core 支持整体对象/数组覆盖,父节点优先于叶子变量,仍执行 Schema 校验;构建子进程与目标运行环境使用相同开通结果。R2 必须提供已有 S3 accessKeyId/secretAccessKey;Resend 必须提供已验证域名的 from;阿里云必须提供已审核 signName/模板。应用须已有相应 _db/_cache/_storage/_reach.yml 并在启动时加载,网络客户端需要 Node 运行时。
|
|
201
|
-
|
|
202
|
-
部署失败恢复:`ProvisionResult.resourceStatus` 标记 created/reused;`getDeployRecovery(result.error)` 读取 projectName、stage、platformProjectId 和资源清单,unknown 表示请求结果不确定,必须先在服务商控制台核对。失败资源已返回 ID 时保留该 ID。恢复结果不含 envVars、原始异常或连接凭证,CLI 会展示清单和建议。同名重试复用现有资源;不自动删除复用资源,新建资源仅在人工确认未被使用后清理。
|
|
203
|
-
|
|
204
|
-
## 错误码 — `HaiDeployError`
|
|
205
|
-
|
|
206
|
-
| 错误码 | code | 说明 |
|
|
207
|
-
| --------------------------------------- | ------------------ | ------------------------ |
|
|
208
|
-
| `HaiDeployError.DEPLOY_FAILED` | `hai:deploy:001` | 部署失败(通用) |
|
|
209
|
-
| `HaiDeployError.PROJECT_CREATE_FAILED` | `hai:deploy:002` | 平台项目创建失败 |
|
|
210
|
-
| `HaiDeployError.BUILD_FAILED` | `hai:deploy:003` | 应用构建失败 |
|
|
211
|
-
| `HaiDeployError.UPLOAD_FAILED` | `hai:deploy:004` | 构建产物上传失败 |
|
|
212
|
-
| `HaiDeployError.AUTH_REQUIRED` | `hai:deploy:005` | 未认证 |
|
|
213
|
-
| `HaiDeployError.AUTH_FAILED` | `hai:deploy:006` | 认证失败 |
|
|
214
|
-
| `HaiDeployError.PROVISION_FAILED` | `hai:deploy:007` | 基础设施开通失败 |
|
|
215
|
-
| `HaiDeployError.ADAPTER_MISSING` | `hai:deploy:008` | SvelteKit adapter 未安装 |
|
|
216
|
-
| `HaiDeployError.SCAN_FAILED` | `hai:deploy:009` | 应用扫描失败 |
|
|
217
|
-
| `HaiDeployError.NOT_INITIALIZED` | `hai:deploy:010` | 模块未初始化 |
|
|
218
|
-
| `HaiDeployError.ENV_VAR_FAILED` | `hai:deploy:011` | 环境变量设置失败 |
|
|
219
|
-
| `HaiDeployError.UNSUPPORTED_TYPE` | `hai:deploy:012` | 不支持的类型 |
|
|
220
|
-
| `HaiDeployError.CONFIG_ERROR` | `hai:deploy:013` | 配置错误 |
|
|
221
|
-
| `HaiDeployError.CREDENTIAL_ERROR` | `hai:deploy:014` | 凭证读写失败 |
|
|
222
|
-
| `HaiDeployError.CONTAINER_RUNTIME_NOT_FOUND` | `hai:deploy:015` | 未找到容器运行时 |
|
|
223
|
-
| `HaiDeployError.CONTAINER_BUILD_FAILED` | `hai:deploy:016` | 镜像构建失败 |
|
|
224
|
-
| `HaiDeployError.REMOTE_CONNECT_FAILED` | `hai:deploy:017` | 远程连接失败 |
|
|
225
|
-
| `HaiDeployError.REMOTE_COMMAND_FAILED` | `hai:deploy:018` | 远程命令失败 |
|
|
226
|
-
| `HaiDeployError.IMAGE_TRANSFER_FAILED` | `hai:deploy:019` | 镜像传输失败 |
|
|
227
|
-
| `HaiDeployError.HEALTH_CHECK_FAILED` | `hai:deploy:020` | 健康检查未通过 |
|
|
228
|
-
|
|
229
|
-
## CLI 命令
|
|
230
|
-
|
|
231
|
-
通过 CLI 部署前,先按 [CLI 安装与命令](../hai-app-create/development.md#cli-安装与命令) 全局安装 `@h-ai/cli`,确认 `hai --version` 可运行后再执行下列命令。在 Node.js 脚本中直接使用 `@h-ai/deploy` API 时无需安装 CLI。
|
|
232
|
-
|
|
233
|
-
```bash
|
|
234
|
-
# 部署当前目录
|
|
235
|
-
hai deploy
|
|
236
|
-
|
|
237
|
-
# 部署指定应用
|
|
238
|
-
hai deploy ./apps/admin-console --env test
|
|
239
|
-
|
|
240
|
-
# 跳过基础设施开通
|
|
241
|
-
hai deploy --env prod --skip-provision
|
|
242
|
-
|
|
243
|
-
# 仅 Vercel:跳过应用构建;docker-ssh 仍会构建镜像
|
|
244
|
-
hai deploy --env prod --skip-build
|
|
245
|
-
|
|
246
|
-
# 自定义项目名
|
|
247
|
-
hai deploy --project-name my-custom-name
|
|
248
|
-
```
|
|
249
|
-
|
|
250
|
-
> 部署方式由 `config/_deploy.yml` 的 `provider.type` 决定(vercel / docker-ssh),
|
|
251
|
-
> CLI 命令相同。docker-ssh 成功后输出的访问地址为 `http://<host>:<hostPort>`。
|
|
252
|
-
|
|
253
|
-
fullstack 需明确部署服务包;`hai deploy packages/my-app-serv --env prod` 不会发布 Web。根 Dockerfile 的 `runtime` / `web` target 分别构建 API / Web;Web 单独转移、运行,设置浏览器可访问的 `PUBLIC_API_BASE` 和 API 的 CORS。模板 `pnpm run deploy` 是打包脚本,不等于 CLI 发布。
|
|
254
|
-
|
|
255
|
-
离线部署先按 [离线准备与验收](middleware.md#离线准备与验收) 检查材料。docker-ssh 在本地构建并导出镜像 tar,再分别上传镜像、Compose、运行环境和启动脚本,不在远程构建源码。当前无直接指定预构建镜像的参数;不得声称 `--skip-build` 能离线发布容器,也不得将联网部署测试作为断网证明。
|
|
256
|
-
|
|
257
|
-
## 常见模式
|
|
258
|
-
|
|
259
|
-
### 仅部署(跳过基础设施)
|
|
260
|
-
|
|
261
|
-
```typescript
|
|
262
|
-
await deploy.deployApp('./apps/my-app', { skipProvision: true })
|
|
263
|
-
```
|
|
264
|
-
|
|
265
|
-
### 仅开通基础设施
|
|
266
|
-
|
|
267
|
-
```typescript
|
|
268
|
-
await deploy.init(config)
|
|
269
|
-
const result = await deploy.provisionAll('my-project')
|
|
270
|
-
// 手动使用 result.data 中的环境变量
|
|
271
|
-
await deploy.close()
|
|
272
|
-
```
|
|
273
|
-
|
|
274
|
-
### 自定义部署流程
|
|
275
|
-
|
|
276
|
-
```typescript
|
|
277
|
-
import { deploy } from '@h-ai/deploy'
|
|
278
|
-
|
|
279
|
-
// Provider 通过 init 配置自动创建,不需要手动导入
|
|
280
|
-
await deploy.init({
|
|
281
|
-
provider: { type: 'vercel', token: 'vel_xxx' },
|
|
282
|
-
})
|
|
283
|
-
|
|
284
|
-
// 使用 deploy API 部署
|
|
285
|
-
const result = await deploy.deployApp('./apps/my-app')
|
|
286
|
-
```
|
|
45
|
+
## 离线交付与验证
|
|
287
46
|
|
|
288
|
-
|
|
47
|
+
Linux 预备 Docker/Podman、支持 --pull never 的 Compose(Docker Compose v2 或 podman-compose 1.6.0 及以上)、sh、tar、sha256sum、awk、curl 及目录/容器权限。完整 tar 包含全部自建中间件归档;目标架构须匹配。包中有私有配置,安全搬运,不提交 Git。离线构建额外准备基础镜像、Linux 依赖缓存、工具链与原生预编译包;Windows node_modules 不能替代。
|
|
289
48
|
|
|
290
|
-
|
|
291
|
-
- **hai-reldb** — 数据库(Neon PostgreSQL)
|
|
292
|
-
- **hai-cache** — 缓存(Upstash Redis)
|
|
49
|
+
按 [分轮验证与集中修复](../hai-app-tests/validation.md) 收集适用的 typecheck、lint、build、test、scaffold gates 和 test:deploy-gates,汇总后集中修复、再验证。实际验证无 registry 网络的首次部署、登录刷新、同版本重发、只更新 Web/API、损坏归档、缺基线、健康失败与恢复。Windows/Linux/macOS 上传入口分别执行验收;原生安装包在对应 OS 验证。测试临时工程、容器、镜像、数据和进程在 finally/teardown 清理,只清理测试拥有的资源;日志、截图和报告保留到分析和交付结束再清理。
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
2. **使用自定义配置**:逐项输入所选后端的连接字段,展示变量名、字段用途与约束,校验必填项、端口、布尔和 URL;凭据用密码输入。保存配置后跳过该服务的开通与 sidecar。
|
|
30
30
|
3. **保持不变**:仅在目标 `.env.<mode>` 显式声明当前后端及完整连接配置时出现;私有凭据可来自 `.env.local`/进程。选择后不修改该项、不认证其 Provisioner、不创建或移除已有中间件容器,继续下一项。只有 `.env` 或其他环境有配置不满足此条件。
|
|
31
31
|
|
|
32
|
-
逐项配置前提供“环境配置已完备,直接部署”。选择后跳过全部中间件提示、变量改写、云资源与 sidecar
|
|
32
|
+
逐项配置前提供“环境配置已完备,直接部署”。选择后跳过全部中间件提示、变量改写、云资源与 sidecar 创建,保留已有服务;仍校验已有构建产物并执行部署及健康检查。命令行使用唯一的 `--skip-middleware`,SDK 使用 `skipMiddleware`。不要因为缺少某项中间件的目标文件声明而阻止用户主动跳过。
|
|
33
33
|
|
|
34
34
|
`--yes` 用于已授权的自动化:目标环境已有完整配置时保持不变,否则使用当前后端默认配置;不支持的云后端或缺少必需凭据直接失败。自动化直接部署用 `hai deploy packages/my-app-serv --env prod --yes --skip-middleware`。跳过不作为配置完整性或连接成功的证明。
|
|
35
35
|
|
|
@@ -71,24 +71,12 @@ S3 是协议,不是某个容器产品。默认配置时另选 RustFS/MinIO,
|
|
|
71
71
|
|
|
72
72
|
## 离线准备与验收
|
|
73
73
|
|
|
74
|
-
|
|
74
|
+
构建与部署可分开,也可通过 pnpm run deploy --build 明确串联。构建保持环境无关;部署选择环境并只消费所选镜像。完整包自动包含 Compose 自建中间件镜像,因此导出阶段需要本地 Podman/Docker,缺镜像在打包阶段准备,不能留给隔离服务器下载。
|
|
75
75
|
|
|
76
|
-
|
|
76
|
+
--bundle-only 生成带版本 tar 与 -deploy/ 目录。操作目录含 Windows deploy.ps1、Linux/macOS deploy.sh 和目标配置。隔离服务器搬运并解包后执行 sh apply.sh。每步失败返回非零,结果写 result-<部署ID>.json;只有 HTTP 检查通过才更新服务器 current。
|
|
77
77
|
|
|
78
|
-
|
|
78
|
+
部分更新通过 --services 指定,使用服务器成功基线保留未选服务和中间件凭据。首次安装不能使用部分包。storage-init 复用应用镜像及 S3 SDK 检查/创建桶,不清空数据;只有涉及存储初始化时执行,并检查退出码。仅更新 storage 仍需含 app 镜像的制品;最近为 web-only 时,用 --artifact-dir 选择原完整制品,或先 pnpm build --target docker --images app。app 镜像仅用于初始化,不会更新 app 服务。失败恢复尝试还原容器和配置,保留数据卷,不回退已写业务数据。
|
|
79
79
|
|
|
80
|
-
|
|
80
|
+
服务器需要 Linux、Docker/Podman、Compose、sh、tar、sha256sum、awk、curl、持久化磁盘和容器/目录权限。完整交付包含自建服务镜像;自定义外部连接仍需准备服务、账号和网络可达性。云 Provider/Provisioner 需要外网,不能用于完全离线。
|
|
81
81
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
## 启动与验证
|
|
85
|
-
|
|
86
|
-
fullstack 创建时可通过 `--features deploy` 安装对应版本的本地部署工具。SQLite 在模板及兜底 Dockerfile 中先按驱动版本、Node ABI、架构和 libc 下载官方预编译包,最多尝试三次,加载验证后由生产依赖安装复用缓存;不预装 Python/make/C++,无匹配包或下载失败时停止构建。受限网络可预置 prebuild-install 缓存,或配置 container.sqlitePrebuildMirror(HAI_DEPLOY_CONTAINER_SQLITEPREBUILDMIRROR;自带 Dockerfile 使用 SQLITE_PREBUILD_MIRROR build arg)。镜像根地址后附 /v<驱动版本>/<匹配 ABI 的包名>,不得包含认证信息。不能把更换下载命令当成解决网络不可达,也不能用 CUDA 开关掩盖无关 AI 依赖。
|
|
87
|
-
|
|
88
|
-
Podman 构建默认继承运行时代理。代理地址在容器内不可达时,在 `_deploy.yml` 配置 `container.httpProxy: false`(已声明该节点时也可使用 `HAI_DEPLOY_CONTAINER_HTTPPROXY=false`),不要修改用户的系统代理。`loadDeploymentEnvironment`、`loadDeploymentConfig`、`saveDeploymentEnvironment` 可用于脚本显式读取、解析和保存同一份部署配置,均返回 HaiResult。
|
|
89
|
-
|
|
90
|
-
构建上下文必须排除 `.env.local`、`.env.*.local`、`.env.runtime` 和 `.hai/deploy`(含镜像归档与凭据);公共环境文件仍可进入上下文。兜底 Dockerfile 使用伴随忽略文件,Podman 通过 `--ignorefile` 显式指定,避免使用 workspace 根规则后把上次部署的归档或运行配置重新打进构建层。
|
|
91
|
-
|
|
92
|
-
容器运行期设置 `NODE_ENV=production`,`HAI_DEPLOY_ENVIRONMENT` 保存文件模式,`HAI_CORE_ENV` 映射为 development/test/production/staging。显式环境部署的资源名追加环境后缀,避免 test/prod 复用同一数据卷。启动脚本位于服务包 `.hai/deploy/<mode>/start-<mode>.sh`,显式导出环境并执行 compose;私有 `.env.runtime` 权限为 600。自带 Dockerfile 可读取同名 build args;不得把私有环境文件或运行凭据复制进镜像。
|
|
93
|
-
|
|
94
|
-
修改此流程后执行 deploy/CLI 的 typecheck、lint、build、test,以及 `pnpm --filter @h-ai/cli test:deploy-gates`(当前源码 tarball → fullstack → Podman/SSH → 健康检查、环境标识、登录和重复部署)。模板变动另执行受影响的 scaffold gates。日志、截图、验收报告保存在 Git 忽略的临时目录,不提交仓库;付费云资源、原生客户端与远程生产部署须单独验证并说明边界。
|
|
82
|
+
完全离线构建另需基础镜像、Linux 依赖缓存、pnpm 和匹配驱动/Node ABI/架构/libc 的原生预编译包。构建可用 --sqlite-prebuild-mirror,Podman 代理不可达可用 --no-http-proxy;不要改系统代理或让 deploy 下载编译环境。生产 HTTPS、CORS、证书、DNS 另行准备。实际断网验收登录、刷新、重启和数据持久化。
|
|
@@ -75,7 +75,7 @@ npm view @h-ai/cli dist-tags --json
|
|
|
75
75
|
| --- | --- |
|
|
76
76
|
| 自动处理 | 目标明确、可保持现有行为的依赖更新、兼容性修正、无冲突的 skills 合并和验证,直接执行 |
|
|
77
77
|
| 用户选择 | 存在多个有意义的方案且影响产品行为、架构、成本、Provider 或运维方式,展示证据、选项、推荐与取舍,等待回答 |
|
|
78
|
-
| 阻塞 |
|
|
78
|
+
| 阻塞 | 无法取得必要产物、依赖不兼容、无法无损合并、必要工具/服务/授权缺失等导致后续无法执行,先处理可自行解决的原因;仍无法推进时说明具体步骤。普通门禁失败先记录,继续独立检查,本轮汇总后集中修复;必要门禁未通过仍不能交付完成 |
|
|
79
79
|
| 不适用 | 当前应用未使用且不影响其依赖/运行的变化,记录依据;不要求用户逐项确认 |
|
|
80
80
|
|
|
81
81
|
- 尽量合并相互独立的选择,明确每个回答控制哪项改动;已给出的目标、授权和选择持续有效,不重复询问。
|
|
@@ -95,7 +95,7 @@ npm view @h-ai/cli dist-tags --json
|
|
|
95
95
|
|
|
96
96
|
### 6. 验证与交付
|
|
97
97
|
|
|
98
|
-
按应用实际 scripts 和 [
|
|
98
|
+
按应用实际 scripts 和 [分轮验证与集中修复](../hai-app-tests/validation.md) 先收集一轮适用门禁,汇总根因后集中修复、再验证;优先受影响 workspace,跨包契约验证所有消费者。按 [hai-app-tests](../hai-app-tests/SKILL.md) 选择集成/E2E,覆盖启动/关闭、变更 API、认证、持久化等实际路径;CI/发布按 [hai-ci](../hai-ci/SKILL.md) 保留失败阻断。
|
|
99
99
|
|
|
100
100
|
- 依赖升级后另核对 lockfile 与安装版本、公开入口、配置加载和关键业务流程;build 通过不能替代 typecheck 或运行验收。
|
|
101
101
|
- 检查同步后的全部相邻资源、相对引用和各工具入口;目标存在 `check:skills` 时运行,不假定应用具有框架仓库私有脚本。
|
|
@@ -45,5 +45,5 @@ PowerShell 使用 `pnpm.cmd`;占位包名替换后执行。本地来源先验
|
|
|
45
45
|
|
|
46
46
|
- 仅 skills/入口变更:检查 diff、完整目录、目标实际存在的入口及相邻资源、相对引用;排除框架仓库私有绝对路径/命令。
|
|
47
47
|
- 框架模板维护:运行已有 `check:skills` 和 CLI `test:scaffold-gates`,确认资源与路由在生成项目中可达。
|
|
48
|
-
- 同步应用依赖或运行时:按入口升级流程和 [hai-ci](../hai-ci/SKILL.md)
|
|
48
|
+
- 同步应用依赖或运行时:按入口升级流程和 [分轮验证与集中修复](../hai-app-tests/validation.md) 收集适用门禁、集中修复并复测;CI/发布遵守 [hai-ci](../hai-ci/SKILL.md),不以模板已同步代替运行验收。
|
|
49
49
|
- 双仓改动时说明来源版本/提交、改动顺序、应用私有覆盖和双方验证;涉及 PR 时按 [hai-pr-review](../hai-pr-review/SKILL.md) 关联同步说明。仅授权一个仓库时记录另一方待同步,不擅自修改。
|
|
@@ -28,14 +28,12 @@ description: "使用 @h-ai/iam 实现登录、注册、会话、OTP/LDAP/API Key
|
|
|
28
28
|
- LDAP/Reach 均可选:基础认证无需安装;默认 LDAP 关闭,选用默认客户端才安装 ldapts 并设置 login.ldap + ldap。OTP 邮件/短信由 otp.email/sms 控制,选择 Reach 标准模板或自定义发送回调;具体签名与失败处理见参考中的“可选 LDAP 与通知”。
|
|
29
29
|
- 多节点共享 DB + Redis,Memory 只用于单进程。重置的策略失败计入 maxAttempts;OTP 消费不解除 resendInterval。管理员 createUser 不受自助注册开关限制。
|
|
30
30
|
|
|
31
|
-
- 协议支持配置中的 userAgreementMarkdown/privacyPolicyMarkdown 正文及 URL;同主题优先 Markdown,由安全渲染器在弹窗显示。
|
|
32
|
-
|
|
33
31
|
- 受保护请求使用 `iam.auth.verifyToken`:当前用户状态 + 角色 JOIN + 批量权限读取,失败即拒绝;`iam.session.verifyToken` 仅验证会话快照。serv 内置 IAM 注入默认使用当前授权入口,无需手动补查。
|
|
34
32
|
- 用户批量读取用 `iam.user.getUsers(ids, { include: ['roles'] })`(最多 1000,去重保留输入顺序,缺失省略);角色筛选用 `listUsers({ roleCode })`,在分页前执行,避免 N+1 和分页后过滤。
|
|
35
33
|
|
|
36
34
|
## 按需参考
|
|
37
35
|
|
|
38
|
-
|
|
36
|
+
按任务读取 [reference.md](reference.md) 的对应小节;签名以当前源码或安装版本的公开类型为准,不默认加载全部资料。示例中的业务变量由应用提供。
|
|
39
37
|
|
|
40
38
|
- 运行环境
|
|
41
39
|
- 依赖
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: hai-kit
|
|
3
|
-
description: "
|
|
3
|
+
description: "在 SvelteKit 中接入 hooks、认证守卫、输入校验、响应和同源加密客户端;不用于独立 serv 服务。"
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# hai-kit
|
|
@@ -15,173 +15,18 @@ description: "使用 @h-ai/kit 集成 SvelteKit hooks、守卫、校验、响应
|
|
|
15
15
|
| 输出 | handle、Response、校验值和客户端请求结果 |
|
|
16
16
|
| 限制 | 无 init/close;保留 SvelteKit 控制流异常。跨端共享 HTTP contract 用 api-contract + serv,勿复制到 kit。 |
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
## 使用路径
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
1. 仅在 SvelteKit 集成;先查现有 hooks、配置、守卫与路由,不将其套用于独立 serv 应用。
|
|
21
|
+
2. 用 createHandle 编排已初始化模块;认证 API 与公开路径复用 kit.auth,客户端复用框架动作。
|
|
22
|
+
3. 区分返回 HaiResult 的业务操作与抛 Response/redirect/error 的框架控制流,不吞掉控制流。
|
|
23
|
+
4. 对齐 Cookie/CSRF/transport 与服务端配置,验证匿名、登录、权限拒绝和表单失败。
|
|
21
24
|
|
|
22
|
-
|
|
25
|
+
## 按需参考
|
|
23
26
|
|
|
24
|
-
|
|
25
|
-
import type { Handle } from '@sveltejs/kit'
|
|
26
|
-
import { kit } from '@h-ai/kit'
|
|
27
|
+
按本次操作读取对应小节;以已安装版本的公开类型核验签名,示例中的业务变量由应用提供。
|
|
27
28
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
protectedPaths: ['/admin/*'],
|
|
33
|
-
},
|
|
34
|
-
rateLimit: { windowMs: 60_000, maxRequests: 100 },
|
|
35
|
-
logging: true,
|
|
36
|
-
})
|
|
37
|
-
|
|
38
|
-
export const handle: Handle = haiHandle
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
### 2. SvelteKit API endpoint
|
|
42
|
-
|
|
43
|
-
```ts
|
|
44
|
-
import { kit } from '@h-ai/kit'
|
|
45
|
-
import { z } from 'zod'
|
|
46
|
-
|
|
47
|
-
const CreateUserSchema = z.object({ name: z.string().min(1) })
|
|
48
|
-
|
|
49
|
-
export const POST = kit.handler(async ({ request, locals }) => {
|
|
50
|
-
kit.guard.require(locals.session, 'user:create')
|
|
51
|
-
const payload = await kit.validate.body(request, CreateUserSchema)
|
|
52
|
-
return kit.response.created(payload)
|
|
53
|
-
})
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
### 3. 浏览器同源请求
|
|
57
|
-
|
|
58
|
-
```ts
|
|
59
|
-
import { kit } from '@h-ai/kit'
|
|
60
|
-
|
|
61
|
-
const client = kit.client.create()
|
|
62
|
-
export const { apiFetch } = client
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
> 同源 endpoint 优先使用 httpOnly Cookie;`auth: true` 只读取 `kit.auth.setBrowserToken()` 写入的页面内存 Token,不会默认读写 localStorage。确需 Bearer Header 时,显式传入 `BrowserTokenStore`,禁止把敏感 Token 存入 localStorage。
|
|
66
|
-
|
|
67
|
-
### 4. 同源传输加密
|
|
68
|
-
|
|
69
|
-
kit 不再维护本地传输加密实现;服务端和客户端都统一委托 `@h-ai/crypto` 的 `crypto.transport`。
|
|
70
|
-
|
|
71
|
-
```ts
|
|
72
|
-
// hooks.server.ts
|
|
73
|
-
import { cache } from '@h-ai/cache'
|
|
74
|
-
import { createRedisTransportKeyStore, crypto } from '@h-ai/crypto'
|
|
75
|
-
import { kit } from '@h-ai/kit'
|
|
76
|
-
|
|
77
|
-
await crypto.init()
|
|
78
|
-
await cache.init({ type: 'redis', host: '127.0.0.1', port: 6379 })
|
|
79
|
-
|
|
80
|
-
export const handle = kit.createHandle({
|
|
81
|
-
auth: {
|
|
82
|
-
verifyToken,
|
|
83
|
-
protectedPaths: ['/api/*'],
|
|
84
|
-
publicPaths: ['/api/_hai/*'],
|
|
85
|
-
},
|
|
86
|
-
crypto: {
|
|
87
|
-
crypto,
|
|
88
|
-
transport: {
|
|
89
|
-
requireEncryption: true,
|
|
90
|
-
keyStore: createRedisTransportKeyStore({ cache, ttlSeconds: 3600 }),
|
|
91
|
-
},
|
|
92
|
-
},
|
|
93
|
-
})
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
需要保护会话 Cookie 时配置 `crypto.encryptedCookies`,并通过 `crypto.cookieEncryptionKey` 或 `HAI_KIT_COOKIE_KEY` 提供密钥。缺少密钥会在 `createHandle()` 时失败;加解密错误不会回退到明文 Cookie。
|
|
97
|
-
|
|
98
|
-
```ts
|
|
99
|
-
// routes/+layout.svelte
|
|
100
|
-
<script lang='ts'>
|
|
101
|
-
import { browser } from '$app/environment'
|
|
102
|
-
import { crypto } from '@h-ai/crypto'
|
|
103
|
-
import { kit } from '@h-ai/kit'
|
|
104
|
-
import { appKitConfig } from '$lib/config/kit-config'
|
|
105
|
-
|
|
106
|
-
// 一次性安装:内部按 appKitConfig.transport 是否启用决定行为,并预热 crypto
|
|
107
|
-
if (browser) {
|
|
108
|
-
kit.client.installBrowserTransport(appKitConfig, { crypto })
|
|
109
|
-
}
|
|
110
|
-
</script>
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
```ts
|
|
114
|
-
// lib/utils/api.ts —— 业务层只看到 apiFetch
|
|
115
|
-
import { kit } from '@h-ai/kit'
|
|
116
|
-
|
|
117
|
-
export const { apiFetch } = kit.client.create()
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
默认协商端点:`/api/_hai/key-exchange`。如 `keyExchangePath` 自定义,浏览器端同步设置 `keyExchangeUrl`。
|
|
121
|
-
|
|
122
|
-
多节点部署时,可在 `kit.createHandle({ crypto: { transport } })` 的运行时对象里直接传 `keyStore`;推荐使用 `@h-ai/crypto` 根入口导出的 `createRedisTransportKeyStore()` / `createReldbTransportKeyStore()`。这类对象依赖不要写进 `_kit.yml`。
|
|
123
|
-
|
|
124
|
-
transport 默认保护同源 `/api/*` endpoint 与 SvelteKit `__data.json` 页面数据请求;页面文档、静态资源与 `multipart/form-data` 上传请求保持明文透传。
|
|
125
|
-
|
|
126
|
-
安全策略默认 fail-closed:`requireEncryption: true` 时,受保护路径缺少 `X-Client-Id` 必须返回 400;服务端 transport 管理器不可用、响应体无法加密或超过单次加密上限时返回错误,禁止明文业务响应回退。`requireEncryption: false` 只适合迁移期灰度。
|
|
127
|
-
|
|
128
|
-
### 5. 使用 `_kit.yml` 统一 transport 配置
|
|
129
|
-
|
|
130
|
-
`@h-ai/kit` 现在提供 `KitConfigSchema` / `resolveKitConfig()`,适合同一份 `_kit.yml`
|
|
131
|
-
同时驱动 `hooks.server.ts` 与浏览器端 `kit.client.installBrowserTransport()`:
|
|
132
|
-
|
|
133
|
-
```yml
|
|
134
|
-
server:
|
|
135
|
-
host: 127.0.0.1
|
|
136
|
-
port: 3000
|
|
137
|
-
|
|
138
|
-
transport:
|
|
139
|
-
keyExchangePath: /api/_hai/key-exchange
|
|
140
|
-
requireEncryption: true
|
|
141
|
-
encryptResponse: true
|
|
142
|
-
excludePaths:
|
|
143
|
-
- /api/storage
|
|
144
|
-
- /api/public
|
|
145
|
-
- /api/auth/profile/avatar
|
|
146
|
-
maxClients: 10000
|
|
147
|
-
```
|
|
148
|
-
|
|
149
|
-
```ts
|
|
150
|
-
import { resolveKitConfig } from '@h-ai/kit'
|
|
151
|
-
import { parse } from 'yaml'
|
|
152
|
-
import rawKitConfig from '../config/_kit.yml?raw'
|
|
153
|
-
|
|
154
|
-
export const appKitConfig = resolveKitConfig(parse(rawKitConfig) ?? {})
|
|
155
|
-
```
|
|
156
|
-
|
|
157
|
-
`resolveKitConfig()` 同时解析 `server`(host/port)与 `transport`;host/port 优先级为 `HOST` / `PORT` 环境变量 > `_kit.yml` 的 `server` > 默认 `127.0.0.1:3000`(与 `@h-ai/serv` 同构)。
|
|
158
|
-
`_kit.yml` 只放公开的 server/transport 路径/开关,不放密钥;如自定义 `keyExchangePath`,浏览器端必须同步映射为 `keyExchangeUrl`。
|
|
159
|
-
|
|
160
|
-
## 核心 API
|
|
161
|
-
|
|
162
|
-
| API | 用途 |
|
|
163
|
-
| --- | --- |
|
|
164
|
-
| `kit.createHandle()` | 创建 SvelteKit Handle |
|
|
165
|
-
| `kit.sequence()` | 组合多个 Handle |
|
|
166
|
-
| `kit.handler()` | API endpoint 错误边界 |
|
|
167
|
-
| `kit.guard.require()` | 权限守卫 |
|
|
168
|
-
| `kit.response.*` | 统一响应工厂 |
|
|
169
|
-
| `kit.validate.*` | Zod 请求校验 |
|
|
170
|
-
| `kit.auth.*` | Cookie / Token 辅助 |
|
|
171
|
-
| `kit.client.create()` | 浏览器端同源 apiFetch |
|
|
172
|
-
| `kit.client.installBrowserTransport(config, { crypto })` | 推荐入口:按解析后的 `_kit.yml` 一键安装浏览器端传输加密 |
|
|
173
|
-
| `kit.client.installBrowserTransportFetch()` | 底层入口:直接传 transport 配置安装浏览器全局 fetch 包装 |
|
|
174
|
-
| `kit.crud.define()` | 声明式 CRUD 资源 |
|
|
175
|
-
|
|
176
|
-
## 常见模式
|
|
177
|
-
|
|
178
|
-
- SvelteKit 应用内同源请求用 `kit.client.create().apiFetch`。
|
|
179
|
-
- SvelteKit 应用启用 transport 时,在 `+layout.svelte` 的 `if (browser)` 分支调用一次 `kit.client.installBrowserTransport(appKitConfig, { crypto })`,无需重复连线 `keyExchangeUrl` / `excludePaths`。
|
|
180
|
-
- Web/App/小程序跨域访问公共 API 用 `@h-ai/api-client` typed client。
|
|
181
|
-
- 传输加密只通过 `crypto.transport` 间接装配,不在 kit 中新增本地加密工厂或鸭子类型。
|
|
182
|
-
- 服务端业务模块直接调用模块 API,不通过 HTTP 自环。
|
|
183
|
-
- API endpoint 必须用 Zod 校验用户输入,并设置认证/权限边界。
|
|
184
|
-
|
|
185
|
-
## 声明式 CRUD 表单字段
|
|
186
|
-
|
|
187
|
-
`kit.crud.define` 的字段支持 `colSpan: 'auto' | 1 | 2`,以及 `validation.message: string | (() => string)`。布局通过 UI 的 `CrudPage.form` 配置;客户端校验用于交互提示,服务端仍使用 Zod 验证和现有权限守卫。UI 公共契约见相邻 [hai-ui skill](../hai-ui/SKILL.md)。
|
|
29
|
+
- [使用步骤](reference.md#使用步骤)
|
|
30
|
+
- [核心 API](reference.md#核心-api)
|
|
31
|
+
- [常见模式](reference.md#常见模式)
|
|
32
|
+
- [声明式 CRUD 表单字段](reference.md#声明式-crud-表单字段)
|