@h-ai/cli 0.1.0-alpha.66 → 0.1.0-alpha.67
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +66 -257
- package/README.ja.md +66 -257
- package/README.md +66 -257
- package/dist/{chunk-QYX6V7H2.js → chunk-7DSURTJ4.js} +22 -79
- package/dist/chunk-7DSURTJ4.js.map +1 -0
- package/dist/{chunk-FWN5B54L.js → chunk-AOQ4N657.js} +182 -10
- package/dist/chunk-AOQ4N657.js.map +1 -0
- package/dist/cli-config-templates-6AHNUGBN.js +5 -0
- package/dist/{cli-config-templates-3XSFIXBP.js.map → cli-config-templates-6AHNUGBN.js.map} +1 -1
- package/dist/{cli-skill-templates-TSYEQTCY.js → cli-skill-templates-FC3QRKDO.js} +3 -3
- package/dist/{cli-skill-templates-TSYEQTCY.js.map → cli-skill-templates-FC3QRKDO.js.map} +1 -1
- package/dist/index.js +435 -247
- package/dist/index.js.map +1 -1
- package/dist/{src-RDQ2EIBT.js → src-2V6VP4I5.js} +553 -119
- package/dist/src-2V6VP4I5.js.map +1 -0
- package/package.json +8 -8
- package/templates/README.md +48 -145
- package/templates/apps/admin/src/routes/admin/+layout.svelte.hbs +1 -1
- package/templates/apps/api/.dockerignore.hbs +14 -7
- package/templates/apps/api/.gitignore.hbs +7 -0
- package/templates/apps/api/apps/{{projectName}}-contract/README.md.hbs +23 -2
- package/templates/apps/api/apps/{{projectName}}-service/README.md.hbs +28 -7
- package/templates/apps/api/apps/{{projectName}}-service/package.json.hbs +1 -2
- package/templates/apps/api/hai.build.json.hbs +1 -1
- package/templates/apps/api/images/app.Dockerfile.hbs +35 -0
- package/templates/apps/api/package.json.hbs +5 -0
- package/templates/apps/fullstack/.dockerignore.hbs +14 -9
- package/templates/apps/fullstack/.gitignore.hbs +7 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/README.md.hbs +23 -5
- package/templates/apps/fullstack/apps/{{projectName}}-app/index.html.hbs +1 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/public/runtime-config.js.hbs +4 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/vite.config.ts.hbs +1 -1
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/README.md.hbs +22 -4
- package/templates/apps/fullstack/apps/{{projectName}}-miniapp/README.md.hbs +16 -6
- package/templates/apps/fullstack/apps/{{projectName}}-web/README.md.hbs +23 -5
- package/templates/apps/fullstack/apps/{{projectName}}-web/vite.config.ts.hbs +1 -1
- package/templates/apps/fullstack/e2e/fullstack.spec.ts.hbs +11 -1
- package/templates/apps/fullstack/hai.build.json.hbs +2 -2
- package/templates/apps/fullstack/images/app.Dockerfile.hbs +36 -0
- package/templates/apps/fullstack/images/web.Dockerfile.hbs +34 -0
- package/templates/apps/fullstack/package.json.hbs +2 -0
- package/templates/apps/fullstack/packages/{{projectName}}-contract/README.md.hbs +23 -2
- package/templates/apps/fullstack/packages/{{projectName}}-serv/README.md.hbs +27 -6
- package/templates/apps/fullstack/packages/{{projectName}}-shared/.gitignore.hbs +7 -0
- package/templates/apps/fullstack/packages/{{projectName}}-shared/README.md.hbs +32 -11
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/AppShell.svelte.hbs +2 -2
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/settings.svelte.hbs +3 -3
- package/templates/apps/fullstack/packages/{{projectName}}-shared/tests/api-client.test.ts.hbs +2 -2
- package/templates/apps/h5/src/routes/+layout.svelte.hbs +2 -2
- package/templates/apps/mobile-app/src/App.svelte.hbs +2 -2
- package/templates/apps/mobile-app/src/lib/pages/home.svelte.hbs +2 -2
- package/templates/apps/website/src/routes/+layout.svelte.hbs +3 -3
- package/templates/apps/website/src/routes/services/+page.svelte.hbs +1 -1
- package/templates/base/.dockerignore.hbs +14 -8
- package/templates/base/.gitignore.hbs +7 -0
- package/templates/base/e2e/app.spec.ts.hbs +10 -0
- package/templates/base/hai.build.json.hbs +1 -1
- package/templates/base/{Dockerfile.hbs → images/app.Dockerfile.hbs} +19 -17
- package/templates/base/package.json.hbs +8 -3
- package/templates/base/vite.config.ts.hbs +3 -1
- package/templates/shared/.gitattributes +6 -0
- package/templates/skills/AGENTS.md +3 -3
- package/templates/skills/bridges/admin/AGENTS.md +1 -1
- package/templates/skills/bridges/api/AGENTS.md +1 -1
- package/templates/skills/bridges/fullstack/AGENTS.md.hbs +1 -1
- package/templates/skills/bridges/h5/AGENTS.md +1 -1
- package/templates/skills/bridges/mobile-app/AGENTS.md +1 -1
- package/templates/skills/bridges/website/AGENTS.md +1 -1
- package/templates/skills/hai-api-client/SKILL.md +14 -161
- package/templates/skills/hai-api-client/reference.md +166 -0
- package/templates/skills/hai-api-contract/SKILL.md +15 -273
- package/templates/skills/hai-api-contract/reference.md +278 -0
- package/templates/skills/hai-app-create/SKILL.md +2 -2
- package/templates/skills/hai-app-create/development.md +4 -4
- package/templates/skills/hai-app-create/reference.md +5 -4
- package/templates/skills/hai-app-review/SKILL.md +1 -1
- package/templates/skills/hai-app-tests/SKILL.md +4 -3
- package/templates/skills/hai-app-tests/reference.md +2 -2
- package/templates/skills/hai-app-tests/validation.md +31 -0
- package/templates/skills/hai-audit/SKILL.md +17 -142
- package/templates/skills/hai-audit/reference.md +145 -0
- package/templates/skills/hai-build/SKILL.md +14 -8
- package/templates/skills/hai-build/reference.md +10 -9
- package/templates/skills/hai-cache/SKILL.md +15 -182
- package/templates/skills/hai-cache/reference.md +187 -0
- package/templates/skills/hai-capacitor/SKILL.md +4 -3
- package/templates/skills/hai-capacitor/reference.md +6 -4
- package/templates/skills/hai-ci/SKILL.md +2 -0
- package/templates/skills/hai-core/SKILL.md +3 -3
- package/templates/skills/hai-crypto/SKILL.md +16 -252
- package/templates/skills/hai-crypto/reference.md +256 -0
- package/templates/skills/hai-datapipe/SKILL.md +15 -196
- package/templates/skills/hai-datapipe/reference.md +202 -0
- package/templates/skills/hai-deploy/SKILL.md +20 -55
- package/templates/skills/hai-deploy/middleware.md +5 -11
- package/templates/skills/hai-framework-sync/SKILL.md +2 -2
- package/templates/skills/hai-framework-sync/sync.md +1 -1
- package/templates/skills/hai-iam/SKILL.md +1 -3
- package/templates/skills/hai-kit/SKILL.md +12 -167
- package/templates/skills/hai-kit/reference.md +172 -0
- package/templates/skills/hai-payment/SKILL.md +19 -251
- package/templates/skills/hai-payment/reference.md +256 -0
- package/templates/skills/hai-pr-review/SKILL.md +4 -4
- package/templates/skills/hai-reach/SKILL.md +2 -2
- package/templates/skills/hai-reldb/SKILL.md +1 -1
- package/templates/skills/hai-scheduler/SKILL.md +15 -195
- package/templates/skills/hai-scheduler/reference.md +202 -0
- package/templates/skills/hai-serv/SKILL.md +2 -2
- package/templates/skills/hai-spec-implement/SKILL.md +1 -1
- package/templates/skills/hai-spec-implement/workflow.md +3 -3
- package/templates/skills/hai-storage/SKILL.md +17 -238
- package/templates/skills/hai-storage/reference.md +243 -0
- package/templates/skills/hai-ui/SKILL.md +1 -1
- package/templates/skills/hai-ui/reference.md +9 -5
- package/templates/skills/hai-vecdb/SKILL.md +2 -2
- package/dist/chunk-FWN5B54L.js.map +0 -1
- package/dist/chunk-QYX6V7H2.js.map +0 -1
- package/dist/cli-config-templates-3XSFIXBP.js +0 -5
- package/dist/src-RDQ2EIBT.js.map +0 -1
- package/templates/apps/api/Dockerfile.hbs +0 -35
- package/templates/apps/fullstack/Dockerfile.hbs +0 -57
- /package/templates/apps/fullstack/{docker → images/web}/19-runtime-config.envsh.hbs +0 -0
- /package/templates/apps/fullstack/{docker → images/web}/nginx.conf.hbs +0 -0
- /package/templates/apps/fullstack/{docker → images/web}/runtime-config.js.template.hbs +0 -0
- /package/templates/base/{docker → images/web}/nginx.conf.hbs +0 -0
|
@@ -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}}
|
|
@@ -32,13 +42,21 @@ pnpm build --target desktop --platform windows --arch x64
|
|
|
32
42
|
|
|
33
43
|
打包前需要安装 Rust 工具链和 Tauri 平台依赖。构建不读取 .env.prod/.env.local;启动已安装程序前设置进程 PUBLIC_API_BASE(HTTP(S),不含凭据、查询和片段)。Rust 会在页面加载前注入公开地址,仅将该 Origin 加入 CSP;不需要为 test/prod 重打安装包。缺省连接 http://localhost:3000/api/v1。
|
|
34
44
|
|
|
35
|
-
|
|
45
|
+
### IAM 认证配置
|
|
36
46
|
|
|
37
|
-
服务端 `config/_iam.yml` 统一控制登录、注册、重置、协议与 `ui` 品牌。邮件/短信可用 `_reach.yml` 中的 Provider/标准模板或自定义发送回调。`AuthPanel` 使用安全公开配置及框架认证动作;无需在页面适配 Token
|
|
47
|
+
服务端 `config/_iam.yml` 统一控制登录、注册、重置、协议与 `ui` 品牌。邮件/短信可用 `_reach.yml` 中的 Provider/标准模板或自定义发送回调。`AuthPanel` 使用安全公开配置及框架认证动作;无需在页面适配 Token、验证码倒计时和错误提示。单实例可使用 Memory;多实例需共享数据库与 Redis,并按工程根 README 配置写入路由和传输会话。
|
|
38
48
|
|
|
39
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。
|
|
40
50
|
|
|
41
51
|
协议支持配置中的 userAgreementMarkdown/privacyPolicyMarkdown 正文及 URL;同主题优先 Markdown,由安全渲染器在弹窗显示。
|
|
42
52
|
|
|
43
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
|
|
44
62
|
{{/if}}
|
|
@@ -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}}
|
|
@@ -22,15 +32,23 @@ pnpm --filter {{projectName}}-web dev
|
|
|
22
32
|
pnpm --filter {{projectName}}-web build
|
|
23
33
|
```
|
|
24
34
|
|
|
25
|
-
从工程根运行 `pnpm build --target docker`
|
|
35
|
+
从工程根运行 `pnpm build --target docker` 导出镜像。默认带部署工具,可运行 `pnpm run deploy --env test --build` 一次完成构建发布,或 `pnpm run deploy --env prod --bundle-only` 交付已有镜像及跨平台脚本。完整包包含自建中间件,人工部署与按服务更新见工程根 README。
|
|
26
36
|
|
|
27
|
-
|
|
37
|
+
### IAM 认证配置
|
|
28
38
|
|
|
29
|
-
服务端 `config/_iam.yml` 统一控制登录、注册、重置、协议与 `ui` 品牌。邮件/短信可用 `_reach.yml` 中的 Provider/标准模板或自定义发送回调。`AuthPanel` 使用安全公开配置及框架认证动作;无需在页面适配 Token
|
|
39
|
+
服务端 `config/_iam.yml` 统一控制登录、注册、重置、协议与 `ui` 品牌。邮件/短信可用 `_reach.yml` 中的 Provider/标准模板或自定义发送回调。`AuthPanel` 使用安全公开配置及框架认证动作;无需在页面适配 Token、验证码倒计时和错误提示。单实例可使用 Memory;多实例需共享数据库与 Redis,并按工程根 README 配置写入路由和传输会话。
|
|
30
40
|
|
|
31
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。
|
|
32
42
|
|
|
33
43
|
协议支持配置中的 userAgreementMarkdown/privacyPolicyMarkdown 正文及 URL;同主题优先 Markdown,由安全渲染器在弹窗显示。
|
|
34
44
|
|
|
35
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
|
|
36
54
|
{{/if}}
|
|
@@ -51,7 +51,7 @@ export default defineConfig(({ mode, command }) => {
|
|
|
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()
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"images": [
|
|
3
|
-
{ "name": "app", "
|
|
4
|
-
{ "name": "web", "
|
|
3
|
+
{ "name": "app", "dockerfile": "images/app.Dockerfile", "containerPort": 3000 }{{#if fullstack.frontends.web}},
|
|
4
|
+
{ "name": "web", "dockerfile": "images/web.Dockerfile", "containerPort": 8080 }{{/if}}
|
|
5
5
|
]{{#if fullstack.frontends.desktop}},
|
|
6
6
|
"desktop": ["apps/{{projectName}}-desktop"]{{/if}}
|
|
7
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}}
|
|
@@ -17,7 +17,9 @@
|
|
|
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
18
|
"compile": "pnpm --filter {{fullstack.contractPackageName}} build && pnpm i18n:compile && pnpm -r --if-present build",
|
|
19
19
|
"build": "hai build",
|
|
20
|
+
{{#if features.deploy}}
|
|
20
21
|
"deploy": "hai deploy packages/{{projectName}}-serv",
|
|
22
|
+
{{/if}}
|
|
21
23
|
"check": "pnpm typecheck",
|
|
22
24
|
"typecheck": "pnpm --filter {{fullstack.contractPackageName}} build && pnpm i18n:compile && pnpm -r --if-present typecheck",
|
|
23
25
|
"lint": "eslint *.js *.ts e2e && pnpm -r --if-present lint",
|
|
@@ -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
|
+
### 容器运行
|
|
37
47
|
|
|
38
|
-
从工程根运行 `pnpm build --target docker`
|
|
48
|
+
从工程根运行 `pnpm build --target docker` 导出镜像。默认带部署工具,可运行 `pnpm run deploy --env test --build` 一次完成构建发布,或 `pnpm run deploy --env prod --bundle-only` 交付已有镜像及跨平台脚本。完整包包含自建中间件,人工部署与按服务更新见工程根 README。
|
|
39
49
|
|
|
40
50
|
- 服务镜像直接由 `@h-ai/serv` 对外监听,配置挂载点为 `/app/config`,数据目录为 `/app/data`,不包含额外转发进程。
|
|
41
51
|
- Web 镜像不烘焙 API 地址;启动时使用 `-e PUBLIC_API_BASE=https://api.example.com` 注入,同一镜像可部署到不同环境。未设置时 Web 默认请求自身 origin。
|
|
42
52
|
- YAML 配置使用 `${ENV_NAME:default}` 接收运行时环境变量;不要把密钥写入镜像。
|
|
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
|
|
@@ -1,35 +1,48 @@
|
|
|
1
1
|
# {{fullstack.sharedPackageName}}
|
|
2
2
|
|
|
3
|
+
## Introduction
|
|
4
|
+
|
|
3
5
|
Shared mini administration workspace for **{{projectName}}**. Web, Capacitor App and Tauri desktop use the same `AdminWorkspace`, responsive `AppShell`, typed API client and management store.
|
|
4
6
|
|
|
7
|
+
## Core features
|
|
8
|
+
|
|
9
|
+
- Shared management pages, forms, navigation and application state.
|
|
10
|
+
- Typed encrypted API client and platform-specific token storage.
|
|
11
|
+
- Themes, language preferences, onboarding and help.
|
|
12
|
+
|
|
13
|
+
## Getting started
|
|
14
|
+
|
|
15
|
+
### Behavior and configuration
|
|
16
|
+
|
|
5
17
|
The behavior below describes the initial example. Roles, permissions, workflows, limits, preferences and audit policies can change with the user's requirements. Update conflicting implementation, tests and guidance together; preserve the applicable public API contracts and server security checks.
|
|
6
18
|
|
|
7
19
|
- Administrators manage users, upload user avatars, assign the built-in administrator/user roles, edit role descriptions and query audit logs.
|
|
8
20
|
- By default, ordinary users update their own profile, avatar, language, appearance and password. The server enforces the actual business permissions on every request.
|
|
21
|
+
- The workspace uses the available window width. Tables scroll within their own area; settings panels form two columns on large screens and stack on phones.
|
|
9
22
|
- `ManagementStore` owns API calls and business state; components render the state and forward user interactions.
|
|
10
23
|
- Application name, version, environment and default locale come from the backend `app.info` contract, which reads `core` configuration. Do not duplicate these values in translations or frontend constants.
|
|
11
24
|
- Requests, token refresh and JSON responses use the same `@h-ai/api-client` + `@h-ai/crypto` transport. Avatar uploads use encrypted JSON and validate PNG/JPEG/WebP content, with a 512 KiB limit.
|
|
12
25
|
- Read metadata through the same initialized `managementApi.app.info()` client; inspect `success` before using `data`.
|
|
13
26
|
|
|
14
|
-
|
|
27
|
+
Vite development and preview servers read the workspace root environment files and proxy /api to the backend. Builds do not load deployment environment files. Web containers receive their public API address at startup; native apps follow the platform configuration below.
|
|
15
28
|
|
|
16
29
|
An origin-only API base uses `/api/v1`. For a custom service `http.apiPrefix`, include that path in `PUBLIC_API_BASE` or the runtime API base; the client preserves the configured path and restricts avatar URLs to its matching resource prefix.
|
|
17
30
|
|
|
18
|
-
|
|
31
|
+
### Authentication and preferences
|
|
19
32
|
|
|
20
33
|
Web and browser previews keep access tokens in memory and refresh tokens in the server's httpOnly Cookie. Requests include credentials; reload restores the session through the encrypted refresh flow. Never store authentication tokens in localStorage.
|
|
21
34
|
|
|
22
|
-
Capacitor native uses `createCapacitorTokenStorage()` and the trusted native token channel.
|
|
35
|
+
Capacitor native uses `createCapacitorTokenStorage()` and the trusted native token channel. Set its public API address in the App's `public/runtime-config.js` before compiling and syncing native assets. Tauri uses memory token storage and requires a new login after restart; set process `PUBLIC_API_BASE` before launching the installed application, and its Rust host injects the address. Both send `clientName` and `X-HAI-Token-Transport: body`; the service verifies the trusted native origin before returning a refresh token in JSON. Missing API configuration is an error, not a successful connection.
|
|
23
36
|
|
|
24
37
|
Language and theme preferences are saved in IAM user metadata, restored after login and synchronized with the shared/host Paraglide runtimes and `core.i18n`. The small reactive theme store delegates DOM updates and safe appearance caching to `@h-ai/ui/theme` (`applyTheme` / `getSavedTheme`); the framework uses the `theme` localStorage key. Appearance selectors live in personal settings and save through the authenticated profile contract.
|
|
25
38
|
|
|
26
|
-
|
|
39
|
+
### Onboarding and help
|
|
27
40
|
|
|
28
41
|
AdminWorkspace mounts the shared `@h-ai/ui/guide` controller and GuideHost after authentication. AppShell includes a help button; first-use invitations offer start, later and dismiss. Web stores preferences in Cookies, while App/Desktop use localStorage, scoped by the current user and tour version. Help articles link to the overview and profile tours.
|
|
29
42
|
|
|
30
43
|
Extend `src/lib/stores/guide-store.ts` and the shared Chinese/English messages, then add stable data-guide attributes to existing components. Navigation uses the same ManagementStore. Keep business text as i18n callbacks and preserve the host's locale/scope binding. Browser tests cover completion, reload, replay, settings navigation and mobile help.
|
|
31
44
|
|
|
32
|
-
|
|
45
|
+
### i18n and themes
|
|
33
46
|
|
|
34
47
|
Shared page copy belongs in `messages/{zh-CN,en-US}.json`. Frontend-specific copy belongs in each app's own messages directory. Keep `baseLocale` and `locales` aligned across all enabled projects, then run root `pnpm i18n:compile`; do not edit generated Paraglide files.
|
|
35
48
|
|
|
@@ -37,22 +50,30 @@ All frontend and shared projects load the installed message-format plugin from t
|
|
|
37
50
|
|
|
38
51
|
The scaffold theme configuration determines the themes compiled into frontend CSS. Personal settings offer only those available themes. Customize the common layout in `src/lib/components/AppShell.svelte`, the management pages in adjacent components, and business behavior in `src/lib/stores/management-store.svelte.ts`.
|
|
39
52
|
|
|
40
|
-
|
|
53
|
+
### Validation
|
|
41
54
|
|
|
42
55
|
Run root `pnpm typecheck`, `pnpm lint`, `pnpm build`, `pnpm test` and `pnpm test:e2e`. Shared API tests exercise real transport key exchange, encrypted request/response handling, configured core metadata and credential forwarding. Root browser tests cover administrator and ordinary-user workflows, avatar rendering, persisted preferences, password/session invalidation and narrow-screen layout. All selected Web/App/Desktop previews also exercise login, tours, help and profile navigation. Clean screenshots, reports, temporary projects, containers and helper processes in finally/teardown, including failure paths. These browser checks do not replace native-shell or miniapp device acceptance.
|
|
43
56
|
|
|
44
|
-
|
|
57
|
+
### IAM configuration
|
|
45
58
|
|
|
46
|
-
The service's `config/_iam.yml` controls login methods, registration, password reset, agreements and `ui` branding. Email and SMS use providers/templates in `_reach.yml` or custom callbacks. `AuthPanel` uses the public configuration and framework auth actions to manage tokens, resend countdowns and errors.
|
|
59
|
+
The service's `config/_iam.yml` controls login methods, registration, password reset, agreements and `ui` branding. Email and SMS use providers/templates in `_reach.yml` or custom callbacks. `AuthPanel` uses the public configuration and framework auth actions to manage tokens, resend countdowns and errors. A single process can use memory cache. Multiple instances need shared database/cache, write routing and transport session affinity as described in the project README.
|
|
47
60
|
|
|
48
61
|
Authentication defaults to a split layout. Set `ui.variant`, `title`, `subtitle`, `brandTitle`, `brandText`, `logoUrl` and `illustrationUrl` to customize login and registration. Without a `brandTitle`, the displayed application name comes from `app.info` and follows `core` configuration.
|
|
49
62
|
|
|
50
63
|
LDAP and Reach are optional. Install ldapts in the IAM service workspace only when using its default LDAP client, then enable login.ldap and set connection settings. Custom onOtpSendEmail/onOtpSendSms/onPasswordResetRequest callbacks work without Reach and must reject on delivery failure. Agreements open in a Modal. LoginForm/RegisterForm support header/extraFields/footer snippets; AuthPanel forwards loginHeader/loginFields/loginFooter and registerHeader/registerFields/registerFooter. See the generated hai-iam/hai-ui skills for configuration and examples.
|
|
51
64
|
|
|
52
|
-
|
|
65
|
+
Agreements accept Markdown text or a URL. Markdown takes precedence and opens in a safely rendered dialog.
|
|
53
66
|
|
|
54
67
|
See `.agents/skills/hai-iam/reference.md` in the generated application for the complete contract.
|
|
55
68
|
|
|
56
|
-
|
|
69
|
+
### Form layouts
|
|
70
|
+
|
|
71
|
+
The user editor uses `FormLayout columns={2} labelPosition="left"`. Personal settings fill the available width and use two columns on wide screens, with stacked labels and a single avatar editor. Narrow containers automatically stack cards, fields and labels. UI controls and CRUD forms show client validation on blur, update errors while correcting input and validate untouched fields on submit. Field errors follow the current UI language. The management store syncs locale through the lightweight `@h-ai/ui/i18n` entry, without loading components or tour CSS. Configure `colSpan` on FormField to override automatic spans for textareas, uploads and multiple choices.
|
|
72
|
+
|
|
73
|
+
## Development and testing
|
|
74
|
+
|
|
75
|
+
Run root `pnpm typecheck`, `pnpm lint`, `pnpm build`, `pnpm test` and `pnpm test:e2e`. Browser checks cover shared workflows; test native shells on their target platforms separately.
|
|
76
|
+
|
|
77
|
+
## License
|
|
57
78
|
|
|
58
|
-
|
|
79
|
+
Apache-2.0
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
{#if menuOpen}
|
|
35
35
|
<BareButton class="fixed inset-0 z-30 bg-black/30 lg:hidden" ariaLabel={m.close_menu()} onclick={() => { menuOpen = false }} />
|
|
36
36
|
{/if}
|
|
37
|
-
<aside class="fixed inset-y-0 left-0 z-40 flex w-60 flex-col border-r border-base-content/8 bg-base-100 transition-transform duration-200 lg:sticky lg:top-0 lg:h-dvh lg:translate-x-0 {menuOpen ? 'translate-x-0' : '-translate-x-full'}">
|
|
37
|
+
<aside class="fixed inset-y-0 left-0 z-40 flex w-60 shrink-0 flex-col border-r border-base-content/8 bg-base-100 transition-transform duration-200 lg:sticky lg:top-0 lg:h-dvh lg:translate-x-0 {menuOpen ? 'translate-x-0' : '-translate-x-full'}">
|
|
38
38
|
<div class="flex h-16 items-center gap-3 border-b border-base-content/8 px-5">
|
|
39
39
|
<span class="grid size-8 shrink-0 place-items-center rounded-lg bg-primary text-primary-content"><span class="icon-[tabler--hexagon-letter-h] size-5"></span></span>
|
|
40
40
|
<span class="truncate text-sm font-semibold" data-testid="app-name">{store.info?.name}</span>
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
<Button variant="ghost" size="sm" loading={store.saving} onclick={() => void store.logout()} data-testid="logout">{m.logout()}</Button>
|
|
67
67
|
</div>
|
|
68
68
|
</header>
|
|
69
|
-
<main class="
|
|
69
|
+
<main class="w-full min-w-0 flex-1 p-4 pb-8 sm:p-6 lg:p-8" data-guide="admin-content">{@render children()}</main>
|
|
70
70
|
<footer class="border-t border-base-content/8 px-6 py-3 text-center text-xs text-base-content/35">{store.info?.name} · v{store.info?.version}</footer>
|
|
71
71
|
</div>
|
|
72
72
|
</div>
|
package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/settings.svelte.hbs
CHANGED
|
@@ -14,13 +14,13 @@
|
|
|
14
14
|
const themeOptions = AVAILABLE_THEMES.map(theme => ({ value: theme, label: getThemeInfo(theme)?.name ?? theme }))
|
|
15
15
|
</script>
|
|
16
16
|
|
|
17
|
-
<section class='
|
|
18
|
-
<div>
|
|
17
|
+
<section class='grid w-full min-w-0 gap-6 xl:grid-cols-2' data-testid='settings-page'>
|
|
18
|
+
<div class='xl:col-span-2'>
|
|
19
19
|
<h1 class='text-xl font-semibold tracking-tight'>{m.settings_title()}</h1>
|
|
20
20
|
<p class='mt-1 text-sm text-base-content/50'>{m.settings_description()}</p>
|
|
21
21
|
</div>
|
|
22
22
|
{#if store.user}
|
|
23
|
-
<Card title={m.profile_title()} bordered shadow={false} data-guide='admin-profile-form'>
|
|
23
|
+
<Card class='xl:row-span-2' title={m.profile_title()} bordered shadow={false} data-guide='admin-profile-form'>
|
|
24
24
|
<div class='mb-5 flex flex-wrap items-center gap-3 border-b border-base-content/8 pb-4'>
|
|
25
25
|
<p class='min-w-0 break-all text-sm text-base-content/55'>@{store.user.username}</p>
|
|
26
26
|
<Badge size='sm' variant='ghost'>{store.isAdmin ? m.role_admin() : m.role_user()}</Badge>
|
package/templates/apps/fullstack/packages/{{projectName}}-shared/tests/api-client.test.ts.hbs
CHANGED
|
@@ -75,11 +75,11 @@ describe('shared API transport', () => {
|
|
|
75
75
|
expect(server.requests.at(-1)?.headers.get('x-request-client')).toBe('configured-client')
|
|
76
76
|
})
|
|
77
77
|
|
|
78
|
-
it('uses the API origin
|
|
78
|
+
it.each([false, true])('uses the injected API origin when native configuration is required: %s', async (requireApiBase) => {
|
|
79
79
|
Object.assign(globalThis, { __HAI_PUBLIC_CONFIG__: { apiBase: 'https://runtime.example.com' } })
|
|
80
80
|
const server = createServerFetch()
|
|
81
81
|
try {
|
|
82
|
-
expect((await initializeManagementApi({ fetchFn: server.fetchFn })).success).toBe(true)
|
|
82
|
+
expect((await initializeManagementApi({ fetchFn: server.fetchFn, requireApiBase })).success).toBe(true)
|
|
83
83
|
expect((await managementApi.app.info()).success).toBe(true)
|
|
84
84
|
expect(server.requests.every(request => request.url.startsWith('https://runtime.example.com/api/v1/'))).toBe(true)
|
|
85
85
|
}
|
|
@@ -61,14 +61,14 @@
|
|
|
61
61
|
</svelte:head>
|
|
62
62
|
|
|
63
63
|
<div class='hai-mobile-viewport'>
|
|
64
|
-
<div class='hai-mobile-shell
|
|
64
|
+
<div class='hai-mobile-shell min-w-0'>
|
|
65
65
|
<AppBar title={page.data.appConfig.name} safeArea fixed={false} />
|
|
66
66
|
<main class='hai-mobile-main'>{@render children()}</main>
|
|
67
67
|
<footer class='border-t border-base-300 bg-base-100 p-2 text-center text-xs text-base-content/60'>
|
|
68
68
|
{page.data.appConfig.name} · {page.data.appConfig.version} · {page.data.appConfig.env}
|
|
69
69
|
</footer>
|
|
70
70
|
{#if !isAuthPage}
|
|
71
|
-
<BottomNav items={navItems} active={activeNav} onchange={handleNavigation} centered maxWidth='
|
|
71
|
+
<BottomNav items={navItems} active={activeNav} onchange={handleNavigation} centered maxWidth='none' safeArea />
|
|
72
72
|
{/if}
|
|
73
73
|
</div>
|
|
74
74
|
</div>
|
|
@@ -100,7 +100,7 @@
|
|
|
100
100
|
</script>
|
|
101
101
|
|
|
102
102
|
<div class='hai-mobile-viewport'>
|
|
103
|
-
<div class='hai-mobile-shell
|
|
103
|
+
<div class='hai-mobile-shell min-w-0'>
|
|
104
104
|
<AppBar title={appConfig.name} safeArea fixed={false} class='bg-base-100/95 backdrop-blur border-base-content/8' />
|
|
105
105
|
<main class='hai-mobile-main'>
|
|
106
106
|
{#if booting}
|
|
@@ -129,7 +129,7 @@
|
|
|
129
129
|
{{#if features.iam}}
|
|
130
130
|
{#if authenticated && !booting && !bootError}
|
|
131
131
|
{{/if}}
|
|
132
|
-
<BottomNav items={navItems} active={router.path.slice(1)} onchange={handleTabChange} safeArea centered maxWidth='
|
|
132
|
+
<BottomNav items={navItems} active={router.path.slice(1)} onchange={handleTabChange} safeArea centered maxWidth='none' />
|
|
133
133
|
{{#if features.iam}}
|
|
134
134
|
{/if}
|
|
135
135
|
{{/if}}
|
|
@@ -12,8 +12,8 @@
|
|
|
12
12
|
const { nativeRuntime, nativeTokenStorage }: Props = $props()
|
|
13
13
|
</script>
|
|
14
14
|
|
|
15
|
-
<section class='
|
|
16
|
-
<div class='bg-primary text-primary-content rounded-2xl p-5 shadow-sm'>
|
|
15
|
+
<section class='grid gap-4 p-4 pb-6 lg:grid-cols-2'>
|
|
16
|
+
<div class='lg:col-span-2 bg-primary text-primary-content rounded-2xl p-5 shadow-sm'>
|
|
17
17
|
<p class='text-sm opacity-75'>{m.hero_eyebrow()}</p>
|
|
18
18
|
<h1 class='mt-1 text-2xl font-bold'>{m.hero_title()}</h1>
|
|
19
19
|
<p class='mt-2 text-sm opacity-80'>{m.hero_subtitle()}</p>
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
</svelte:head>
|
|
50
50
|
|
|
51
51
|
<div class='min-h-screen flex flex-col'>
|
|
52
|
-
<header class='navbar bg-base-100 shadow-sm'>
|
|
52
|
+
<header class='navbar flex-wrap bg-base-100 shadow-sm'>
|
|
53
53
|
<div class='flex-1'>
|
|
54
54
|
{{#if hasI18n}}
|
|
55
55
|
<a href={resolve('/', {})} class='btn btn-ghost text-xl font-black tracking-tight'>{page.data.appConfig.name}</a>
|
|
@@ -57,8 +57,8 @@
|
|
|
57
57
|
<a href={resolve('/', {})} class='btn btn-ghost text-xl font-black tracking-tight'>{page.data.appConfig.name}</a>
|
|
58
58
|
{{/if}}
|
|
59
59
|
</div>
|
|
60
|
-
<nav class='flex-none'>
|
|
61
|
-
<ul class='menu menu-horizontal px-1'>
|
|
60
|
+
<nav class='w-full flex-none sm:w-auto'>
|
|
61
|
+
<ul class='menu menu-horizontal flex-wrap px-1'>
|
|
62
62
|
{#each navItems as item (item.path)}
|
|
63
63
|
<li><a href={resolve(item.path, {})}>{item.label}</a></li>
|
|
64
64
|
{/each}
|