@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
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
<meta charset="UTF-8" />
|
|
7
7
|
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
|
|
8
8
|
<meta name="theme-color" content="#2563eb" />
|
|
9
|
+
<script src="/runtime-config.js"></script>
|
|
9
10
|
<title>{{html fullstack.appName}}</title>
|
|
10
11
|
<link
|
|
11
12
|
rel="icon"
|
|
@@ -13,9 +13,7 @@
|
|
|
13
13
|
"dev:prod": "vite --mode prod",
|
|
14
14
|
"dev": "vite dev --host 127.0.0.1 --port {{fullstack.ports.app}}",
|
|
15
15
|
"build": "pnpm paraglide:compile && vite build",
|
|
16
|
-
"build:prod": "pnpm paraglide:compile && vite build --mode prod",
|
|
17
16
|
"package": "pnpm cap:sync",
|
|
18
|
-
"deploy": "pnpm package",
|
|
19
17
|
"preview": "vite preview --host 127.0.0.1 --port {{fullstack.ports.app}} --strictPort",
|
|
20
18
|
"check": "pnpm typecheck",
|
|
21
19
|
"typecheck": "pnpm paraglide:compile && svelte-check --tsconfig ./tsconfig.json",
|
|
@@ -11,8 +11,8 @@ import { defineConfig } from 'vitest/config'
|
|
|
11
11
|
|
|
12
12
|
const envDir = fileURLToPath(new URL('../../', import.meta.url))
|
|
13
13
|
|
|
14
|
-
export default defineConfig(({ mode }) => {
|
|
15
|
-
const envResult = core.config.loadEnvFiles({ mode, envDir })
|
|
14
|
+
export default defineConfig(({ mode, command }) => {
|
|
15
|
+
const envResult = command === 'build' ? { success: true as const, data: {} as Record<string, string> } : core.config.loadEnvFiles({ mode, envDir })
|
|
16
16
|
if (!envResult.success)
|
|
17
17
|
throw new Error(envResult.error.message)
|
|
18
18
|
const env = envResult.data
|
|
@@ -51,7 +51,7 @@ export default defineConfig(({ mode }) => {
|
|
|
51
51
|
},
|
|
52
52
|
envPrefix: ['VITE_', 'PUBLIC_'],
|
|
53
53
|
ssr: {
|
|
54
|
-
noExternal: [
|
|
54
|
+
noExternal: ['@h-ai/ui', /{{fullstack.sharedPackageName}}/],
|
|
55
55
|
},
|
|
56
56
|
test: {
|
|
57
57
|
environment: 'node',
|
|
@@ -2,9 +2,19 @@
|
|
|
2
2
|
{{#if fullstack.frontends.desktop}}
|
|
3
3
|
# {{projectName}}-desktop
|
|
4
4
|
|
|
5
|
+
## 简介
|
|
6
|
+
|
|
5
7
|
桌面端工程,参考 `apps/desktop-app` 使用 Tauri v2 + Svelte 5 + Vite,前端通过共享 contract 调用 `{{fullstack.servPackageName}}`。
|
|
6
8
|
|
|
7
|
-
##
|
|
9
|
+
## 核心功能清单
|
|
10
|
+
|
|
11
|
+
- Tauri v2 桌面壳与共享管理界面。
|
|
12
|
+
- 运行时配置 API 地址,无需按环境重复打包。
|
|
13
|
+
- 原生令牌仅存内存,重启后重新登录。
|
|
14
|
+
|
|
15
|
+
## 使用入门
|
|
16
|
+
|
|
17
|
+
### i18n 与 shared 协同
|
|
8
18
|
|
|
9
19
|
- 本端页面文案:`messages/{locale}.json` + `project.inlang/settings.json`
|
|
10
20
|
- 共享 Shell/主题/语言/API:`packages/{{fullstack.sharedPackageName}}`
|
|
@@ -14,7 +24,7 @@
|
|
|
14
24
|
|
|
15
25
|
自定义建议:桌面端菜单、状态、快捷操作文案放在本目录 `messages/`;跨端通用能力放到 shared。
|
|
16
26
|
|
|
17
|
-
|
|
27
|
+
### 编译、打包与部署
|
|
18
28
|
|
|
19
29
|
```bash
|
|
20
30
|
# 仅前端开发,默认端口 {{fullstack.ports.desktop}}
|
|
@@ -26,22 +36,27 @@ pnpm --filter {{projectName}}-desktop tauri:dev
|
|
|
26
36
|
# 编译 WebView 前端产物到 dist/
|
|
27
37
|
pnpm --filter {{projectName}}-desktop build
|
|
28
38
|
|
|
29
|
-
#
|
|
30
|
-
pnpm --
|
|
31
|
-
|
|
32
|
-
# 当前模板的 deploy 入口默认执行 package,可按发布平台替换
|
|
33
|
-
pnpm --filter {{projectName}}-desktop deploy
|
|
39
|
+
# 在工程根打包桌面安装包;在对应系统构建 windows / linux / macos
|
|
40
|
+
pnpm build --target desktop --platform windows --arch x64
|
|
34
41
|
```
|
|
35
42
|
|
|
36
|
-
打包前需要安装 Rust 工具链和 Tauri
|
|
43
|
+
打包前需要安装 Rust 工具链和 Tauri 平台依赖。构建不读取 .env.prod/.env.local;启动已安装程序前设置进程 PUBLIC_API_BASE(HTTP(S),不含凭据、查询和片段)。Rust 会在页面加载前注入公开地址,仅将该 Origin 加入 CSP;不需要为 test/prod 重打安装包。缺省连接 http://localhost:3000/api/v1。
|
|
37
44
|
|
|
38
|
-
|
|
45
|
+
### IAM 认证配置
|
|
39
46
|
|
|
40
|
-
服务端 `config/_iam.yml` 统一控制登录、注册、重置、协议与 `ui` 品牌。邮件/短信可用 `_reach.yml` 中的 Provider/标准模板或自定义发送回调。`AuthPanel` 使用安全公开配置及框架认证动作;无需在页面适配 Token
|
|
47
|
+
服务端 `config/_iam.yml` 统一控制登录、注册、重置、协议与 `ui` 品牌。邮件/短信可用 `_reach.yml` 中的 Provider/标准模板或自定义发送回调。`AuthPanel` 使用安全公开配置及框架认证动作;无需在页面适配 Token、验证码倒计时和错误提示。单实例可使用 Memory;多实例需共享数据库与 Redis,并按工程根 README 配置写入路由和传输会话。
|
|
41
48
|
|
|
42
49
|
LDAP/Reach 均可选:基础认证无需安装。默认 LDAP 客户端需在 IAM 服务端 workspace 安装 ldapts,再配置 login.ldap 与连接信息。自定义 onOtpSendEmail/onOtpSendSms/onPasswordResetRequest 回调可独立于 Reach 使用,投递失败须拒绝 Promise。协议在 Modal 显示;LoginForm/RegisterForm 提供 header/extraFields/footer,AuthPanel 透传 loginHeader/loginFields/loginFooter 和 registerHeader/registerFields/registerFooter。配置及例子见生成项目的 hai-iam/hai-ui skills。
|
|
43
50
|
|
|
44
51
|
协议支持配置中的 userAgreementMarkdown/privacyPolicyMarkdown 正文及 URL;同主题优先 Markdown,由安全渲染器在弹窗显示。
|
|
45
52
|
|
|
46
53
|
完整契约见生成应用的 `.agents/skills/hai-iam/reference.md`。
|
|
54
|
+
|
|
55
|
+
## 开发测试
|
|
56
|
+
|
|
57
|
+
从工程根执行 `pnpm typecheck`、`pnpm lint`、`pnpm build`、`pnpm test` 和 `pnpm test:e2e`。浏览器预览通过后,原生壳仍需在对应系统或设备单独验收。
|
|
58
|
+
|
|
59
|
+
## 许可证
|
|
60
|
+
|
|
61
|
+
Apache-2.0
|
|
47
62
|
{{/if}}
|
|
@@ -12,10 +12,9 @@
|
|
|
12
12
|
"dev:test": "vite --mode test",
|
|
13
13
|
"dev:prod": "vite --mode prod",
|
|
14
14
|
"dev": "vite dev --host 127.0.0.1 --port {{fullstack.ports.desktop}}",
|
|
15
|
-
"
|
|
16
|
-
"build
|
|
15
|
+
"compile": "pnpm paraglide:compile && vite build",
|
|
16
|
+
"build": "pnpm compile",
|
|
17
17
|
"package": "pnpm tauri:build",
|
|
18
|
-
"deploy": "pnpm package",
|
|
19
18
|
"preview": "vite preview --host 127.0.0.1 --port {{fullstack.ports.desktop}} --strictPort",
|
|
20
19
|
"check": "pnpm typecheck",
|
|
21
20
|
"typecheck": "pnpm paraglide:compile && svelte-check --tsconfig ./tsconfig.json",
|
|
@@ -1,16 +1,87 @@
|
|
|
1
1
|
{{! @skipIfEmpty }}
|
|
2
2
|
{{#if fullstack.frontends.desktop}}
|
|
3
|
+
use std::collections::HashMap;
|
|
4
|
+
use tauri::utils::config::{Csp, CspDirectiveSources};
|
|
5
|
+
|
|
6
|
+
/// 校验运行期 API 地址;例如 `https://api.example.test/api/v1`,不接收认证信息或查询参数。
|
|
7
|
+
fn parse_runtime_api(value: &str) -> Result<tauri::Url, &'static str> {
|
|
8
|
+
let url = tauri::Url::parse(value).map_err(|_| "Invalid PUBLIC_API_BASE")?;
|
|
9
|
+
if !["http", "https"].contains(&url.scheme())
|
|
10
|
+
|| url.host_str().is_none()
|
|
11
|
+
|| !url.username().is_empty()
|
|
12
|
+
|| url.password().is_some()
|
|
13
|
+
|| url.query().is_some()
|
|
14
|
+
|| url.fragment().is_some()
|
|
15
|
+
{
|
|
16
|
+
return Err(
|
|
17
|
+
"PUBLIC_API_BASE must be an HTTP(S) URL without credentials, query or fragment",
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
Ok(url)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/// 仅将当前运行期 API 的 origin 加入 CSP;不放宽到所有 HTTP/HTTPS 站点。
|
|
24
|
+
fn runtime_csp(policy: Csp, origin: &str) -> Csp {
|
|
25
|
+
let mut directives: HashMap<String, CspDirectiveSources> = policy.into();
|
|
26
|
+
for name in ["connect-src", "img-src"] {
|
|
27
|
+
directives.entry(name.to_string()).or_default().push(origin);
|
|
28
|
+
}
|
|
29
|
+
directives.into()
|
|
30
|
+
}
|
|
31
|
+
|
|
3
32
|
#[tauri::command]
|
|
4
33
|
fn greet(name: &str) -> String {
|
|
5
|
-
format!("Hello, {}! Welcome to
|
|
34
|
+
format!("Hello, {}! Welcome to Desktop.", name)
|
|
6
35
|
}
|
|
7
36
|
|
|
8
37
|
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
|
9
38
|
pub fn run() {
|
|
39
|
+
// 构建不读取部署环境;启动时从进程 PUBLIC_API_BASE 获取公开地址。
|
|
40
|
+
let api = parse_runtime_api(
|
|
41
|
+
&std::env::var("PUBLIC_API_BASE")
|
|
42
|
+
.unwrap_or_else(|_| "http://localhost:3000/api/v1".to_string()),
|
|
43
|
+
)
|
|
44
|
+
.expect("Invalid runtime API configuration");
|
|
45
|
+
let mut context = tauri::generate_context!();
|
|
46
|
+
if let Some(policy) = context.config_mut().app.security.csp.take() {
|
|
47
|
+
context.config_mut().app.security.csp =
|
|
48
|
+
Some(runtime_csp(policy, &api.origin().ascii_serialization()));
|
|
49
|
+
}
|
|
50
|
+
let script = format!(
|
|
51
|
+
"globalThis.__HAI_PUBLIC_CONFIG__={};",
|
|
52
|
+
serde_json::json!({ "apiBase": api.as_str() })
|
|
53
|
+
);
|
|
10
54
|
tauri::Builder::default()
|
|
11
55
|
.plugin(tauri_plugin_shell::init())
|
|
56
|
+
.append_invoke_initialization_script(script)
|
|
12
57
|
.invoke_handler(tauri::generate_handler![greet])
|
|
13
|
-
.run(
|
|
58
|
+
.run(context)
|
|
14
59
|
.expect("error while running tauri application");
|
|
15
60
|
}
|
|
61
|
+
|
|
62
|
+
#[cfg(test)]
|
|
63
|
+
mod tests {
|
|
64
|
+
use super::*;
|
|
65
|
+
|
|
66
|
+
#[test]
|
|
67
|
+
fn validates_runtime_address_and_scopes_csp() {
|
|
68
|
+
let api = parse_runtime_api("https://api.example.test:8443/custom/v2").unwrap();
|
|
69
|
+
let policy = runtime_csp(
|
|
70
|
+
Csp::Policy("default-src 'self'; connect-src 'self'".into()),
|
|
71
|
+
&api.origin().ascii_serialization(),
|
|
72
|
+
);
|
|
73
|
+
let directives: HashMap<String, CspDirectiveSources> = policy.into();
|
|
74
|
+
let sources: Vec<String> = directives["connect-src"].clone().into();
|
|
75
|
+
assert!(sources.contains(&"https://api.example.test:8443".to_string()));
|
|
76
|
+
assert!(!sources.contains(&"https:".to_string()));
|
|
77
|
+
for value in [
|
|
78
|
+
"file:///tmp/config",
|
|
79
|
+
"https://user:secret@host",
|
|
80
|
+
"https://host/?token=secret",
|
|
81
|
+
"relative",
|
|
82
|
+
] {
|
|
83
|
+
assert!(parse_runtime_api(value).is_err());
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
16
87
|
{{/if}}
|
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
{
|
|
4
4
|
"$schema": "https://schema.tauri.app/config/2",
|
|
5
5
|
"productName": {{json fullstack.appName}},
|
|
6
|
-
"version": "
|
|
6
|
+
"version": "../../../package.json",
|
|
7
7
|
"identifier": "com.hai.{{fullstack.nativeAppIdSegment}}.desktop",
|
|
8
8
|
"build": {
|
|
9
9
|
"beforeDevCommand": "pnpm dev",
|
|
10
|
-
"beforeBuildCommand": "pnpm
|
|
10
|
+
"beforeBuildCommand": "pnpm compile",
|
|
11
11
|
"devUrl": "http://localhost:{{fullstack.ports.desktop}}",
|
|
12
12
|
"frontendDist": "../dist"
|
|
13
13
|
},
|
|
@@ -12,8 +12,8 @@ import { defineConfig } from 'vitest/config'
|
|
|
12
12
|
const host = process.env.TAURI_DEV_HOST
|
|
13
13
|
const envDir = fileURLToPath(new URL('../../', import.meta.url))
|
|
14
14
|
|
|
15
|
-
export default defineConfig(({ mode }) => {
|
|
16
|
-
const envResult = core.config.loadEnvFiles({ mode, envDir })
|
|
15
|
+
export default defineConfig(({ mode, command }) => {
|
|
16
|
+
const envResult = command === 'build' ? { success: true as const, data: {} as Record<string, string> } : core.config.loadEnvFiles({ mode, envDir })
|
|
17
17
|
if (!envResult.success)
|
|
18
18
|
throw new Error(envResult.error.message)
|
|
19
19
|
const env = envResult.data
|
|
@@ -2,14 +2,24 @@
|
|
|
2
2
|
{{#if fullstack.hasMiniapp}}
|
|
3
3
|
# {{projectName}}-miniapp
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## 简介
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
- `messages/zh-CN.json`、`messages/en-US.json`:小程序端自有文案
|
|
7
|
+
小程序预留目录,目前没有可运行的小程序应用。
|
|
9
8
|
|
|
10
|
-
|
|
9
|
+
## 核心功能清单
|
|
11
10
|
|
|
12
|
-
|
|
11
|
+
- 已提供 `project.inlang/settings.json` 与中英文 `messages/`。
|
|
12
|
+
- 可复用 `packages/{{fullstack.contractPackageName}}` 的接口类型。
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
## 使用入门
|
|
15
|
+
|
|
16
|
+
选定小程序框架后再实现页面、请求适配和认证。小程序文案放本目录 `messages/`,跨端通用内容放 `packages/{{fullstack.sharedPackageName}}`;消息格式插件使用工程根已安装的依赖。
|
|
17
|
+
|
|
18
|
+
## 开发测试
|
|
19
|
+
|
|
20
|
+
本目录不参与 workspace 构建、单元测试或 E2E。接入实际框架后增加对应命令,并在目标平台验证登录、请求、导航与发布包。
|
|
21
|
+
|
|
22
|
+
## 许可证
|
|
23
|
+
|
|
24
|
+
Apache-2.0
|
|
15
25
|
{{/if}}
|
|
@@ -2,9 +2,19 @@
|
|
|
2
2
|
{{#if fullstack.frontends.web}}
|
|
3
3
|
# {{projectName}}-web
|
|
4
4
|
|
|
5
|
+
## 简介
|
|
6
|
+
|
|
5
7
|
响应式 mini 后台 Web 前端,通过 `packages/{{fullstack.sharedPackageName}}` 调用后端 API,并通过 `@h-ai/ui` 组件搭建页面。
|
|
6
8
|
|
|
7
|
-
##
|
|
9
|
+
## 核心功能清单
|
|
10
|
+
|
|
11
|
+
- 浏览器访问共享管理界面与个人设置。
|
|
12
|
+
- 与 API 共用接口类型、登录状态、主题和语言。
|
|
13
|
+
- 可单独构建 Web 镜像并增量更新。
|
|
14
|
+
|
|
15
|
+
## 使用入门
|
|
16
|
+
|
|
17
|
+
### i18n 与 shared 协同
|
|
8
18
|
|
|
9
19
|
- 本端页面文案:`messages/{locale}.json` + `project.inlang/settings.json`
|
|
10
20
|
- 共享 Shell/主题/语言/API:`packages/{{fullstack.sharedPackageName}}`
|
|
@@ -12,7 +22,7 @@
|
|
|
12
22
|
|
|
13
23
|
自定义建议:Web 独有文案放在本目录 `messages/`;跨端通用文案或组件放到 shared。
|
|
14
24
|
|
|
15
|
-
|
|
25
|
+
### 编译、打包与部署
|
|
16
26
|
|
|
17
27
|
```bash
|
|
18
28
|
# 开发预览,默认端口 {{fullstack.ports.web}}
|
|
@@ -20,21 +30,25 @@ pnpm --filter {{projectName}}-web dev
|
|
|
20
30
|
|
|
21
31
|
# 编译 Web 前端
|
|
22
32
|
pnpm --filter {{projectName}}-web build
|
|
23
|
-
|
|
24
|
-
# 打包部署产物;默认等同 build,可按目标平台替换
|
|
25
|
-
pnpm --filter {{projectName}}-web package
|
|
26
|
-
|
|
27
|
-
# 当前模板的 deploy 入口默认执行 package,可替换为 CDN / 容器发布脚本
|
|
28
|
-
pnpm --filter {{projectName}}-web deploy
|
|
29
33
|
```
|
|
30
34
|
|
|
31
|
-
|
|
35
|
+
从工程根运行 `pnpm build --target docker` 导出镜像。默认带部署工具,可运行 `pnpm run deploy --env test --build` 一次完成构建发布,或 `pnpm run deploy --env prod --bundle-only` 交付已有镜像及跨平台脚本。完整包包含自建中间件,人工部署与按服务更新见工程根 README。
|
|
32
36
|
|
|
33
|
-
|
|
37
|
+
### IAM 认证配置
|
|
38
|
+
|
|
39
|
+
服务端 `config/_iam.yml` 统一控制登录、注册、重置、协议与 `ui` 品牌。邮件/短信可用 `_reach.yml` 中的 Provider/标准模板或自定义发送回调。`AuthPanel` 使用安全公开配置及框架认证动作;无需在页面适配 Token、验证码倒计时和错误提示。单实例可使用 Memory;多实例需共享数据库与 Redis,并按工程根 README 配置写入路由和传输会话。
|
|
34
40
|
|
|
35
41
|
LDAP/Reach 均可选:基础认证无需安装。默认 LDAP 客户端需在 IAM 服务端 workspace 安装 ldapts,再配置 login.ldap 与连接信息。自定义 onOtpSendEmail/onOtpSendSms/onPasswordResetRequest 回调可独立于 Reach 使用,投递失败须拒绝 Promise。协议在 Modal 显示;LoginForm/RegisterForm 提供 header/extraFields/footer,AuthPanel 透传 loginHeader/loginFields/loginFooter 和 registerHeader/registerFields/registerFooter。配置及例子见生成项目的 hai-iam/hai-ui skills。
|
|
36
42
|
|
|
37
43
|
协议支持配置中的 userAgreementMarkdown/privacyPolicyMarkdown 正文及 URL;同主题优先 Markdown,由安全渲染器在弹窗显示。
|
|
38
44
|
|
|
39
45
|
完整契约见生成应用的 `.agents/skills/hai-iam/reference.md`。
|
|
46
|
+
|
|
47
|
+
## 开发测试
|
|
48
|
+
|
|
49
|
+
从工程根执行 `pnpm typecheck`、`pnpm lint`、`pnpm build`、`pnpm test` 和 `pnpm test:e2e`。浏览器预览通过后,原生壳仍需在对应系统或设备单独验收。
|
|
50
|
+
|
|
51
|
+
## 许可证
|
|
52
|
+
|
|
53
|
+
Apache-2.0
|
|
40
54
|
{{/if}}
|
|
@@ -13,9 +13,7 @@
|
|
|
13
13
|
"dev:prod": "vite --mode prod",
|
|
14
14
|
"dev": "vite dev --host 127.0.0.1 --port {{fullstack.ports.web}}",
|
|
15
15
|
"build": "pnpm paraglide:compile && vite build",
|
|
16
|
-
"build:prod": "pnpm paraglide:compile && vite build --mode prod",
|
|
17
16
|
"package": "pnpm build",
|
|
18
|
-
"deploy": "pnpm package",
|
|
19
17
|
"preview": "vite preview --host 127.0.0.1 --port {{fullstack.ports.web}} --strictPort",
|
|
20
18
|
"check": "pnpm typecheck",
|
|
21
19
|
"typecheck": "pnpm paraglide:compile && svelte-check --tsconfig ./tsconfig.json",
|
|
@@ -11,8 +11,8 @@ import { defineConfig } from 'vitest/config'
|
|
|
11
11
|
|
|
12
12
|
const envDir = fileURLToPath(new URL('../../', import.meta.url))
|
|
13
13
|
|
|
14
|
-
export default defineConfig(({ mode }) => {
|
|
15
|
-
const envResult = core.config.loadEnvFiles({ mode, envDir })
|
|
14
|
+
export default defineConfig(({ mode, command }) => {
|
|
15
|
+
const envResult = command === 'build' ? { success: true as const, data: {} as Record<string, string> } : core.config.loadEnvFiles({ mode, envDir })
|
|
16
16
|
if (!envResult.success)
|
|
17
17
|
throw new Error(envResult.error.message)
|
|
18
18
|
const env = envResult.data
|
|
@@ -51,7 +51,7 @@ export default defineConfig(({ mode }) => {
|
|
|
51
51
|
},
|
|
52
52
|
envPrefix: ['VITE_', 'PUBLIC_'],
|
|
53
53
|
ssr: {
|
|
54
|
-
noExternal: [
|
|
54
|
+
noExternal: ['@h-ai/ui', /{{fullstack.sharedPackageName}}/],
|
|
55
55
|
},
|
|
56
56
|
test: {
|
|
57
57
|
environment: 'node',
|
|
@@ -61,7 +61,7 @@ test('service rejects plaintext business requests', async ({ request }) => {
|
|
|
61
61
|
})
|
|
62
62
|
|
|
63
63
|
{{#if fullstack.e2eFrontend}}
|
|
64
|
-
test('hash routing
|
|
64
|
+
test('hash routing and responsive workspace preserve usable content width', async ({ page }, testInfo) => {
|
|
65
65
|
await page.goto('/#/roles?source=direct')
|
|
66
66
|
await login(page, 'admin', adminPassword)
|
|
67
67
|
await expect(page.getByTestId('roles-page')).toBeVisible()
|
|
@@ -70,6 +70,16 @@ test('hash routing restores direct links, refresh and browser history', async ({
|
|
|
70
70
|
await expect(page.getByTestId('roles-page')).toBeVisible()
|
|
71
71
|
await expect(page.locator('header')).toBeVisible()
|
|
72
72
|
await expect(page.locator('footer')).toBeVisible()
|
|
73
|
+
for (const width of [1440, 1920, 2560, 3840, 390]) {
|
|
74
|
+
await page.setViewportSize({ width, height: 960 })
|
|
75
|
+
expect(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth)).toBe(true)
|
|
76
|
+
const main = await page.locator('main').boundingBox()
|
|
77
|
+
const content = await page.getByTestId('roles-page').boundingBox()
|
|
78
|
+
expect(content!.width).toBeGreaterThan(main!.width - 80)
|
|
79
|
+
if (width === 1920)
|
|
80
|
+
await capture(page, '{{projectName}}-wide.png', testInfo.outputPath('wide-1920.png'))
|
|
81
|
+
}
|
|
82
|
+
await page.setViewportSize({ width: 1440, height: 960 })
|
|
73
83
|
await page.getByTestId('nav-settings').click()
|
|
74
84
|
await expect(page).toHaveURL(/#\/settings$/)
|
|
75
85
|
await page.goBack()
|
|
@@ -96,6 +106,7 @@ test('branded authentication, profile layout and blur validation work across scr
|
|
|
96
106
|
await capture(page, '{{projectName}}-register-mobile.png', testInfo.outputPath('register-mobile.png'))
|
|
97
107
|
expect(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth)).toBe(true)
|
|
98
108
|
await page.reload()
|
|
109
|
+
await expect(page.getByTestId('login-form')).toBeVisible()
|
|
99
110
|
await capture(page, '{{projectName}}-login-mobile.png', testInfo.outputPath('login-mobile.png'))
|
|
100
111
|
await page.setViewportSize({ width: 1440, height: 960 })
|
|
101
112
|
await login(page, 'admin', adminPassword)
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"images": [
|
|
3
|
+
{ "name": "app", "dockerfile": "images/app.Dockerfile", "containerPort": 3000 }{{#if fullstack.frontends.web}},
|
|
4
|
+
{ "name": "web", "dockerfile": "images/web.Dockerfile", "containerPort": 8080 }{{/if}}
|
|
5
|
+
]{{#if fullstack.frontends.desktop}},
|
|
6
|
+
"desktop": ["apps/{{projectName}}-desktop"]{{/if}}
|
|
7
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
FROM node:24.22.1-bookworm-slim AS build
|
|
2
|
+
ARG SQLITE_PREBUILD_MIRROR=
|
|
3
|
+
ENV npm_config_better_sqlite3_binary_host_mirror=$SQLITE_PREBUILD_MIRROR
|
|
4
|
+
|
|
5
|
+
ENV PNPM_HOME=/pnpm
|
|
6
|
+
ENV PATH=$PNPM_HOME:$PATH
|
|
7
|
+
ENV CI=true
|
|
8
|
+
ENV ONNXRUNTIME_NODE_INSTALL_CUDA=skip
|
|
9
|
+
WORKDIR /workspace
|
|
10
|
+
|
|
11
|
+
RUN corepack enable && corepack prepare pnpm@12.11.2 --activate && pnpm config set store-dir /pnpm/store
|
|
12
|
+
COPY pnpm-lock.yaml pnpm-workspace.yaml ./
|
|
13
|
+
COPY images/dependencies/ ./
|
|
14
|
+
RUN --mount=type=cache,id={{projectName}}-pnpm,target=/pnpm/store pnpm fetch --ignore-scripts
|
|
15
|
+
COPY . .
|
|
16
|
+
RUN --mount=type=cache,id={{projectName}}-pnpm,target=/pnpm/store pnpm install --offline --frozen-lockfile --ignore-scripts
|
|
17
|
+
RUN pnpm --filter {{fullstack.servPackageName}}... --if-present build
|
|
18
|
+
|
|
19
|
+
RUN --mount=type=cache,id={{projectName}}-sqlite,target=/root/.npm/_prebuilds if [ -d node_modules/better-sqlite3 ]; then cd node_modules/better-sqlite3; for attempt in 1 2 3; do node -e "require('node:child_process').execFileSync(process.execPath,[require.resolve('prebuild-install/bin.js'),'--verbose'],{stdio:'inherit'})" && break; [ "$attempt" = 3 ] && exit 1; sleep 3; done; fi
|
|
20
|
+
RUN --mount=type=cache,id={{projectName}}-pnpm,target=/pnpm/store --mount=type=cache,id={{projectName}}-sqlite,target=/root/.npm/_prebuilds pnpm pm deploy --filter {{fullstack.servPackageName}} --prod /prod/service
|
|
21
|
+
|
|
22
|
+
FROM node:24.22.1-bookworm-slim AS service
|
|
23
|
+
|
|
24
|
+
ENV NODE_ENV=production
|
|
25
|
+
ENV HOST=0.0.0.0
|
|
26
|
+
ENV PORT=3000
|
|
27
|
+
WORKDIR /app
|
|
28
|
+
|
|
29
|
+
COPY --from=build --chown=node:node /prod/service ./
|
|
30
|
+
RUN mkdir -p /app/data && chown node:node /app/data
|
|
31
|
+
|
|
32
|
+
USER node
|
|
33
|
+
EXPOSE 3000
|
|
34
|
+
HEALTHCHECK --interval=30s --timeout=5s --start-period=15s --retries=3 \
|
|
35
|
+
CMD node -e "fetch('http://127.0.0.1:'+process.env.PORT+'/health').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"
|
|
36
|
+
CMD ["node", "dist/index.js"]
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{{#if fullstack.frontends.web}}
|
|
2
|
+
FROM node:24.22.1-bookworm-slim AS build
|
|
3
|
+
ARG SQLITE_PREBUILD_MIRROR=
|
|
4
|
+
ENV npm_config_better_sqlite3_binary_host_mirror=$SQLITE_PREBUILD_MIRROR
|
|
5
|
+
|
|
6
|
+
ENV PNPM_HOME=/pnpm
|
|
7
|
+
ENV PATH=$PNPM_HOME:$PATH
|
|
8
|
+
ENV CI=true
|
|
9
|
+
WORKDIR /workspace
|
|
10
|
+
|
|
11
|
+
RUN corepack enable && corepack prepare pnpm@12.11.2 --activate && pnpm config set store-dir /pnpm/store
|
|
12
|
+
COPY pnpm-lock.yaml pnpm-workspace.yaml ./
|
|
13
|
+
COPY images/dependencies/ ./
|
|
14
|
+
RUN --mount=type=cache,id={{projectName}}-pnpm,target=/pnpm/store pnpm fetch --ignore-scripts
|
|
15
|
+
COPY . .
|
|
16
|
+
RUN --mount=type=cache,id={{projectName}}-pnpm,target=/pnpm/store pnpm install --offline --frozen-lockfile --ignore-scripts
|
|
17
|
+
RUN pnpm --filter {{fullstack.sharedPackageName}} paraglide:compile && pnpm --filter {{projectName}}-web... --if-present build
|
|
18
|
+
|
|
19
|
+
FROM nginxinc/nginx-unprivileged:1.29.8-alpine AS web
|
|
20
|
+
|
|
21
|
+
ENV PUBLIC_API_BASE=
|
|
22
|
+
ENV NGINX_ENVSUBST_FILTER=PUBLIC_API_BASE_BASE64
|
|
23
|
+
ENV NGINX_ENVSUBST_OUTPUT_DIR=/tmp/hai-runtime-config
|
|
24
|
+
|
|
25
|
+
RUN mkdir -p /tmp/hai-runtime-config
|
|
26
|
+
COPY images/web/nginx.conf /etc/nginx/conf.d/default.conf
|
|
27
|
+
COPY --chmod=755 images/web/19-runtime-config.envsh /docker-entrypoint.d/19-runtime-config.envsh
|
|
28
|
+
COPY images/web/runtime-config.js.template /etc/nginx/templates/runtime-config.js.template
|
|
29
|
+
COPY --from=build /workspace/apps/{{projectName}}-web/dist /usr/share/nginx/html
|
|
30
|
+
|
|
31
|
+
EXPOSE 8080
|
|
32
|
+
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
|
|
33
|
+
CMD wget -Y off -q -O /dev/null http://127.0.0.1:8080/health || exit 1
|
|
34
|
+
{{/if}}
|
|
@@ -15,24 +15,18 @@
|
|
|
15
15
|
"start:prod": "pnpm --filter {{fullstack.servPackageName}} start:prod",
|
|
16
16
|
"dev:test": "pnpm --filter {{fullstack.contractPackageName}} build && pnpm i18n:compile && pnpm --parallel --filter {{fullstack.servPackageName}}{{#each fullstack.frontendApps}} --filter {{packageName}}{{/each}} dev:test",
|
|
17
17
|
"dev:prod": "pnpm --filter {{fullstack.contractPackageName}} build && pnpm i18n:compile && pnpm --parallel --filter {{fullstack.servPackageName}}{{#each fullstack.frontendApps}} --filter {{packageName}}{{/each}} dev:prod",
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
|
|
21
|
-
"
|
|
22
|
-
|
|
23
|
-
"docker:build:web": "docker build --target web -t {{projectName}}-web:latest ."{{/if}},
|
|
24
|
-
"podman:build": "podman build --format docker --jobs=1 --target runtime -t {{projectName}}:latest .",
|
|
25
|
-
"podman:build:data": "podman build --format docker --jobs=1 --target runtime-with-data --build-context embedded-data=./packages/{{projectName}}-serv/data -t {{projectName}}:latest ."{{#if fullstack.frontends.web}},
|
|
26
|
-
"podman:build:web": "podman build --format docker --jobs=1 --target web -t {{projectName}}-web:latest ."{{/if}},
|
|
27
|
-
"package": "pnpm build{{#if fullstack.frontends.web}} && pnpm --filter {{projectName}}-web package{{/if}}{{#if fullstack.frontends.app}} && pnpm --filter {{projectName}}-app package{{/if}}{{#if fullstack.frontends.desktop}} && pnpm --filter {{projectName}}-desktop package{{/if}}",
|
|
28
|
-
"deploy": "pnpm package",
|
|
18
|
+
"compile": "pnpm --filter {{fullstack.contractPackageName}} build && pnpm i18n:compile && pnpm -r --if-present build",
|
|
19
|
+
"build": "hai build",
|
|
20
|
+
{{#if features.deploy}}
|
|
21
|
+
"deploy": "hai deploy packages/{{projectName}}-serv",
|
|
22
|
+
{{/if}}
|
|
29
23
|
"check": "pnpm typecheck",
|
|
30
24
|
"typecheck": "pnpm --filter {{fullstack.contractPackageName}} build && pnpm i18n:compile && pnpm -r --if-present typecheck",
|
|
31
25
|
"lint": "eslint *.js *.ts e2e && pnpm -r --if-present lint",
|
|
32
26
|
"lint:fix": "eslint *.js *.ts e2e --fix && pnpm -r --if-present lint:fix",
|
|
33
27
|
"test": "pnpm --filter {{fullstack.contractPackageName}} build && pnpm i18n:compile && pnpm -r --if-present test",
|
|
34
28
|
"test:watch": "pnpm -r --parallel --if-present test:watch",
|
|
35
|
-
"test:e2e": "pnpm
|
|
29
|
+
"test:e2e": "pnpm compile && node scripts/run-e2e.mjs",
|
|
36
30
|
"clean": "pnpm -r --if-present clean && rimraf node_modules .turbo"
|
|
37
31
|
},
|
|
38
32
|
"dependencies": {
|
|
@@ -41,6 +35,7 @@
|
|
|
41
35
|
"{{fullstack.sharedPackageName}}": "workspace:*"
|
|
42
36
|
},
|
|
43
37
|
"devDependencies": {
|
|
38
|
+
"@h-ai/cli": "{{haiPackageSpecifier}}",
|
|
44
39
|
{{#if features.deploy}}
|
|
45
40
|
"@h-ai/deploy": "{{haiPackageSpecifier}}",
|
|
46
41
|
{{/if}}
|
|
@@ -1,8 +1,18 @@
|
|
|
1
1
|
# {{fullstack.contractPackageName}}
|
|
2
2
|
|
|
3
|
+
## 简介
|
|
4
|
+
|
|
3
5
|
共享 API 契约包,包含 Zod Schema、`apiContract.route()` 业务接口定义与工程级 contract。业务 schema、角色、流程及限额是模板初始方案,按用户需求修改,并同步服务端授权、调用方、测试与说明;不要求需求沿用默认业务。
|
|
4
6
|
|
|
5
|
-
##
|
|
7
|
+
## 核心功能清单
|
|
8
|
+
|
|
9
|
+
- 共享接口路径、输入输出 Schema 和类型。
|
|
10
|
+
- 客户端与服务端使用同一份 contract。
|
|
11
|
+
- 不包含服务端业务实现,也不加载私有配置。
|
|
12
|
+
|
|
13
|
+
## 使用入门
|
|
14
|
+
|
|
15
|
+
### 骨架说明
|
|
6
16
|
|
|
7
17
|
- `src/app-schemas.ts`:跨接口、跨层复用的输入/数据 Schema 与类型。
|
|
8
18
|
- `src/app-contract.ts`:app 领域 contract;每个业务 path 直接写在对应接口定义中。
|
|
@@ -13,7 +23,18 @@
|
|
|
13
23
|
API 前缀、健康检查与跨端 Header 等传输配置位于 serv 包的 `config/_serv.yml`。
|
|
14
24
|
`SUPPORTED_LOCALES` 为生成时实际编译的语言列表;`app.info` 中名称、版本、环境、默认语言来自后端 core 配置。更新公开契约时同时更新 serv、shared typed API client、各端 UI 与测试。
|
|
15
25
|
|
|
16
|
-
|
|
26
|
+
### 开发
|
|
17
27
|
|
|
18
28
|
- `pnpm --filter {{fullstack.contractPackageName}} typecheck`
|
|
19
29
|
- `pnpm --filter {{fullstack.contractPackageName}} test`
|
|
30
|
+
|
|
31
|
+
## 开发测试
|
|
32
|
+
|
|
33
|
+
```sh
|
|
34
|
+
pnpm --filter {{fullstack.contractPackageName}} typecheck
|
|
35
|
+
pnpm --filter {{fullstack.contractPackageName}} test
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## 许可证
|
|
39
|
+
|
|
40
|
+
Apache-2.0
|
|
@@ -1,8 +1,18 @@
|
|
|
1
1
|
# {{fullstack.servPackageName}}
|
|
2
2
|
|
|
3
|
+
## 简介
|
|
4
|
+
|
|
3
5
|
后端 API Service,基于 `@h-ai/serv` 将共享 contract 装配为最小 HTTP App 抽象,不在应用代码中直接暴露 Hono。
|
|
4
6
|
|
|
5
|
-
##
|
|
7
|
+
## 核心功能清单
|
|
8
|
+
|
|
9
|
+
- HTTP 服务、健康检查和共享 contract 的 procedure 实现。
|
|
10
|
+
- 用户、角色、审计与个人设置的示例业务。
|
|
11
|
+
- 模块配置、加密传输与服务端权限校验。
|
|
12
|
+
|
|
13
|
+
## 使用入门
|
|
14
|
+
|
|
15
|
+
### 默认功能与配置
|
|
6
16
|
|
|
7
17
|
以下是可运行示例的初始实现,角色/权限、管理流程、头像限额、分页、审计策略及部署方案均可按用户需求调整。需求与代码或模板规则不一致时,以需求为业务基线同步实现、测试与过期说明;实际框架 API 契约和服务端安全校验仍需满足。
|
|
8
18
|
|
|
@@ -24,7 +34,7 @@ CLI 已生成根目录 `.env.local`,`HAI_ADMIN_DEFAULT_PASSWORD` 为每个项
|
|
|
24
34
|
|
|
25
35
|
业务修改提交后的审计记录采用最大努力策略:写入失败输出包含操作及资源的结构化错误日志,业务仍返回原操作结果,避免将已经完成的变更误报为失败;应用启动时 audit 初始化失败则停止启动。
|
|
26
36
|
|
|
27
|
-
|
|
37
|
+
### 开发
|
|
28
38
|
|
|
29
39
|
所有配置字段以模块 Schema 为准;未使用字段和替代 Provider 用注释保留。叶子环境变量只覆盖已有 YAML 路径,先取消相应键的注释,或用 `HAI_DB` / `HAI_CACHE` 等整体对象覆盖;`connectTimeout` 对应 `HAI_CACHE_CONNECTTIMEOUT`。凭据示例在根 `.env.example`,实际密钥保存在忽略的 `.env.local` 或部署环境。`.gitignore` 的 `**/data/` 忽略所有运行数据及 `.gitkeep`;运行时会创建目录。`crypto.init()` 无配置参数,datapipe 为纯函数,不提供虚构配置。
|
|
30
40
|
|
|
@@ -33,20 +43,31 @@ CLI 已生成根目录 `.env.local`,`HAI_ADMIN_DEFAULT_PASSWORD` 为每个项
|
|
|
33
43
|
- `pnpm --filter {{fullstack.servPackageName}} dev`
|
|
34
44
|
- `pnpm --filter {{fullstack.servPackageName}} test`
|
|
35
45
|
|
|
36
|
-
|
|
46
|
+
### 容器运行
|
|
47
|
+
|
|
48
|
+
从工程根运行 `pnpm build --target docker` 导出镜像。默认带部署工具,可运行 `pnpm run deploy --env test --build` 一次完成构建发布,或 `pnpm run deploy --env prod --bundle-only` 交付已有镜像及跨平台脚本。完整包包含自建中间件,人工部署与按服务更新见工程根 README。
|
|
37
49
|
|
|
38
|
-
- 根目录 `pnpm docker:build` / `pnpm podman:build` 直接生成本服务镜像;Web 使用独立的 `docker:build:web` / `podman:build:web` 镜像。
|
|
39
50
|
- 服务镜像直接由 `@h-ai/serv` 对外监听,配置挂载点为 `/app/config`,数据目录为 `/app/data`,不包含额外转发进程。
|
|
40
51
|
- Web 镜像不烘焙 API 地址;启动时使用 `-e PUBLIC_API_BASE=https://api.example.com` 注入,同一镜像可部署到不同环境。未设置时 Web 默认请求自身 origin。
|
|
41
52
|
- YAML 配置使用 `${ENV_NAME:default}` 接收运行时环境变量;不要把密钥写入镜像。
|
|
42
|
-
- 将 SQLite/本地存储放入本目录 `data/` 后运行 `pnpm docker:build:data` / `pnpm podman:build:data`,可生成带初始数据的镜像,持续写入仍应使用 volume。
|
|
43
53
|
|
|
44
|
-
|
|
54
|
+
### IAM 认证配置
|
|
45
55
|
|
|
46
|
-
服务端 `config/_iam.yml` 统一控制登录、注册、重置、协议与 `ui` 品牌。邮件/短信可用 `_reach.yml` 中的 Provider/标准模板或自定义发送回调。`AuthPanel` 使用安全公开配置及框架认证动作;无需在页面适配 Token
|
|
56
|
+
服务端 `config/_iam.yml` 统一控制登录、注册、重置、协议与 `ui` 品牌。邮件/短信可用 `_reach.yml` 中的 Provider/标准模板或自定义发送回调。`AuthPanel` 使用安全公开配置及框架认证动作;无需在页面适配 Token、验证码倒计时和错误提示。单实例可使用 Memory;多实例需共享数据库与 Redis,并按工程根 README 配置写入路由和传输会话。
|
|
47
57
|
|
|
48
58
|
LDAP/Reach 均可选:基础认证无需安装。默认 LDAP 客户端需在 IAM 服务端 workspace 安装 ldapts,再配置 login.ldap 与连接信息。自定义 onOtpSendEmail/onOtpSendSms/onPasswordResetRequest 回调可独立于 Reach 使用,投递失败须拒绝 Promise。协议在 Modal 显示;LoginForm/RegisterForm 提供 header/extraFields/footer,AuthPanel 透传 loginHeader/loginFields/loginFooter 和 registerHeader/registerFields/registerFooter。配置及例子见生成项目的 hai-iam/hai-ui skills。
|
|
49
59
|
|
|
50
60
|
协议支持配置中的 userAgreementMarkdown/privacyPolicyMarkdown 正文及 URL;同主题优先 Markdown,由安全渲染器在弹窗显示。
|
|
51
61
|
|
|
52
62
|
完整契约见生成应用的 `.agents/skills/hai-iam/reference.md`。
|
|
63
|
+
|
|
64
|
+
## 开发测试
|
|
65
|
+
|
|
66
|
+
```sh
|
|
67
|
+
pnpm --filter {{fullstack.servPackageName}} typecheck
|
|
68
|
+
pnpm --filter {{fullstack.servPackageName}} test
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## 许可证
|
|
72
|
+
|
|
73
|
+
Apache-2.0
|