@h-ai/cli 0.1.0-alpha9 → 1.0.0-beta.2
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 +229 -0
- package/README.ja.md +229 -0
- package/README.md +175 -113
- package/dist/chunk-AKV2CQZV.js +1497 -0
- package/dist/chunk-AKV2CQZV.js.map +1 -0
- package/dist/chunk-EF7WOFWZ.js +7557 -0
- package/dist/chunk-EF7WOFWZ.js.map +1 -0
- package/dist/chunk-MUHRBJSW.js +29272 -0
- package/dist/chunk-MUHRBJSW.js.map +1 -0
- package/dist/chunk-ZSDEVXZ7.js +38 -0
- package/dist/{chunk-2H7UOFLK.js.map → chunk-ZSDEVXZ7.js.map} +1 -1
- package/dist/cli-config-templates-HHFHNXBN.js +5 -0
- package/dist/cli-config-templates-HHFHNXBN.js.map +1 -0
- package/dist/cli-skill-templates-PN4FLGBC.js +5 -0
- package/dist/cli-skill-templates-PN4FLGBC.js.map +1 -0
- package/dist/index.js +4444 -554
- package/dist/index.js.map +1 -1
- package/dist/metafile-esm.json +1 -0
- package/dist/src-TEBHR5I5.js +5 -0
- package/dist/src-TEBHR5I5.js.map +1 -0
- package/package.json +35 -15
- package/templates/README.md +56 -114
- package/templates/apps/admin/messages/en-US.json +7 -0
- package/templates/apps/admin/messages/ja-JP.json +40 -0
- package/templates/apps/admin/messages/zh-CN.json +7 -0
- package/templates/apps/admin/src/routes/+layout.svelte.hbs +65 -8
- package/templates/apps/admin/src/routes/+page.svelte.hbs +35 -14
- package/templates/apps/admin/src/routes/admin/+layout.svelte.hbs +54 -22
- package/templates/apps/admin/src/routes/admin/+page.svelte.hbs +33 -24
- package/templates/apps/admin/src/routes/admin/settings/+page.svelte.hbs +16 -13
- package/templates/apps/api/.dockerignore.hbs +25 -0
- package/templates/apps/api/.gitignore.hbs +22 -0
- package/templates/apps/api/.npmrc.hbs +1 -0
- package/templates/apps/api/apps/{{projectName}}-contract/README.md.hbs +39 -0
- package/templates/apps/api/apps/{{projectName}}-contract/package.json.hbs +41 -0
- package/templates/apps/api/apps/{{projectName}}-contract/src/app-contract.ts.hbs +27 -0
- package/templates/apps/api/apps/{{projectName}}-contract/src/app-schemas.ts.hbs +28 -0
- package/templates/apps/api/apps/{{projectName}}-contract/src/index.ts.hbs +5 -0
- package/templates/apps/api/apps/{{projectName}}-contract/src/{{projectName}}-contract.ts.hbs +16 -0
- package/templates/apps/api/apps/{{projectName}}-contract/tests/contract.test.ts.hbs +18 -0
- package/templates/apps/api/apps/{{projectName}}-contract/tsconfig.json.hbs +12 -0
- package/templates/apps/api/apps/{{projectName}}-contract/tsup.config.ts.hbs +11 -0
- package/templates/apps/api/apps/{{projectName}}-contract/vitest.config.ts.hbs +9 -0
- package/templates/apps/api/apps/{{projectName}}-serv/README.md.hbs +60 -0
- package/templates/apps/api/apps/{{projectName}}-serv/config/_core.yml.hbs +17 -0
- package/templates/apps/api/apps/{{projectName}}-serv/config/_serv.yml.hbs +54 -0
- package/templates/apps/api/apps/{{projectName}}-serv/data/.gitkeep.hbs +1 -0
- package/templates/apps/api/apps/{{projectName}}-serv/package.json.hbs +66 -0
- package/templates/apps/api/apps/{{projectName}}-serv/src/app.ts.hbs +119 -0
- package/templates/apps/api/apps/{{projectName}}-serv/src/build-metadata.ts.hbs +7 -0
- package/templates/apps/api/apps/{{projectName}}-serv/src/index.ts.hbs +41 -0
- package/templates/apps/api/apps/{{projectName}}-serv/src/lib/server/init.ts.hbs +165 -0
- package/templates/apps/api/apps/{{projectName}}-serv/src/server/procedures/app-procedures.ts.hbs +57 -0
- package/templates/apps/api/apps/{{projectName}}-serv/src/server/procedures/index.ts.hbs +41 -0
- package/templates/apps/api/apps/{{projectName}}-serv/tests/service.test.ts.hbs +176 -0
- package/templates/apps/api/apps/{{projectName}}-serv/tsconfig.json.hbs +16 -0
- package/templates/apps/api/apps/{{projectName}}-serv/tsup.config.ts.hbs +20 -0
- package/templates/apps/api/apps/{{projectName}}-serv/vitest.config.ts.hbs +12 -0
- package/templates/apps/api/e2e/api-serv.spec.ts.hbs +113 -0
- package/templates/apps/api/e2e/capture-evidence.ts.hbs +23 -0
- package/templates/apps/api/eslint.config.js.hbs +30 -0
- package/templates/apps/api/hai.json.hbs +4 -0
- package/templates/apps/api/images/serv.Dockerfile.hbs +37 -0
- package/templates/apps/api/package.json.hbs +53 -0
- package/templates/apps/api/playwright.config.ts.hbs +67 -0
- package/templates/apps/api/pnpm-workspace.yaml.hbs +40 -0
- package/templates/apps/api/tsconfig.base.json +14 -0
- package/templates/apps/fullstack/.dockerignore.hbs +25 -0
- package/templates/apps/fullstack/.gitignore.hbs +38 -0
- package/templates/apps/fullstack/.npmrc.hbs +1 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/.env.example.hbs +10 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/README.md.hbs +67 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/capacitor.config.ts.hbs +20 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/eslint.config.js.hbs +20 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/index.html.hbs +22 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/messages/en-US.json.hbs +7 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/messages/ja-JP.json.hbs +7 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/messages/zh-CN.json.hbs +7 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/package.json.hbs +86 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/project.inlang/settings.json.hbs +17 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/public/runtime-config.js.hbs +4 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/src/App.svelte.hbs +59 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/src/app.css.hbs +20 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/src/app.d.ts.hbs +8 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/src/build-metadata.ts.hbs +9 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/src/lib/capacitor.ts.hbs +19 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/src/main.ts.hbs +21 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/svelte.config.js.hbs +16 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/tsconfig.json.hbs +23 -0
- package/templates/apps/fullstack/apps/{{projectName}}-app/vite.config.ts.hbs +74 -0
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/.env.example.hbs +6 -0
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/README.md.hbs +64 -0
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/eslint.config.js.hbs +20 -0
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/index.html.hbs +20 -0
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/messages/en-US.json.hbs +7 -0
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/messages/ja-JP.json.hbs +7 -0
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/messages/zh-CN.json.hbs +7 -0
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/package.json.hbs +66 -0
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/project.inlang/settings.json.hbs +17 -0
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/src/App.svelte.hbs +25 -0
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/src/app.css.hbs +18 -0
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/src/app.d.ts.hbs +8 -0
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/src/build-metadata.ts.hbs +9 -0
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/src/main.ts.hbs +20 -0
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/Cargo.toml.hbs +33 -0
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/build.rs.hbs +6 -0
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/capabilities/default.json.hbs +16 -0
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/icons/128x128.png +0 -0
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/icons/128x128@2x.png +0 -0
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/icons/32x32.png +0 -0
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/icons/icon.icns +0 -0
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/icons/icon.ico +0 -0
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/src/lib.rs.hbs +91 -0
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/src/main.rs.hbs +8 -0
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/tauri.conf.json.hbs +46 -0
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/svelte.config.js.hbs +14 -0
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/tsconfig.json.hbs +22 -0
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/vite.config.ts.hbs +89 -0
- package/templates/apps/fullstack/apps/{{projectName}}-miniapp/README.md.hbs +25 -0
- package/templates/apps/fullstack/apps/{{projectName}}-miniapp/messages/en-US.json.hbs +12 -0
- package/templates/apps/fullstack/apps/{{projectName}}-miniapp/messages/ja-JP.json.hbs +12 -0
- package/templates/apps/fullstack/apps/{{projectName}}-miniapp/messages/zh-CN.json.hbs +12 -0
- package/templates/apps/fullstack/apps/{{projectName}}-miniapp/project.inlang/settings.json.hbs +17 -0
- package/templates/apps/fullstack/apps/{{projectName}}-web/README.md.hbs +54 -0
- package/templates/apps/fullstack/apps/{{projectName}}-web/eslint.config.js.hbs +20 -0
- package/templates/apps/fullstack/apps/{{projectName}}-web/index.html.hbs +21 -0
- package/templates/apps/fullstack/apps/{{projectName}}-web/messages/en-US.json.hbs +7 -0
- package/templates/apps/fullstack/apps/{{projectName}}-web/messages/ja-JP.json.hbs +7 -0
- package/templates/apps/fullstack/apps/{{projectName}}-web/messages/zh-CN.json.hbs +7 -0
- package/templates/apps/fullstack/apps/{{projectName}}-web/package.json.hbs +53 -0
- package/templates/apps/fullstack/apps/{{projectName}}-web/project.inlang/settings.json.hbs +17 -0
- package/templates/apps/fullstack/apps/{{projectName}}-web/public/runtime-config.js.hbs +1 -0
- package/templates/apps/fullstack/apps/{{projectName}}-web/src/App.svelte.hbs +18 -0
- package/templates/apps/fullstack/apps/{{projectName}}-web/src/app.css.hbs +18 -0
- package/templates/apps/fullstack/apps/{{projectName}}-web/src/app.d.ts.hbs +8 -0
- package/templates/apps/fullstack/apps/{{projectName}}-web/src/build-metadata.ts.hbs +9 -0
- package/templates/apps/fullstack/apps/{{projectName}}-web/src/main.ts.hbs +14 -0
- package/templates/apps/fullstack/apps/{{projectName}}-web/svelte.config.js.hbs +16 -0
- package/templates/apps/fullstack/apps/{{projectName}}-web/tsconfig.json.hbs +23 -0
- package/templates/apps/fullstack/apps/{{projectName}}-web/vite.config.ts.hbs +76 -0
- package/templates/apps/fullstack/docs/security-configuration.md.hbs +349 -0
- package/templates/apps/fullstack/e2e/fullstack.spec.ts.hbs +988 -0
- package/templates/apps/fullstack/e2e/session-close.spec.ts.hbs +119 -0
- package/templates/apps/fullstack/eslint.config.js.hbs +41 -0
- package/templates/apps/fullstack/hai.json.hbs +11 -0
- package/templates/apps/fullstack/images/serv.Dockerfile.hbs +38 -0
- package/templates/apps/fullstack/images/web/19-runtime-config.envsh.hbs +5 -0
- package/templates/apps/fullstack/images/web/nginx.conf.hbs +24 -0
- package/templates/apps/fullstack/images/web/runtime-config.js.template.hbs +3 -0
- package/templates/apps/fullstack/images/web.Dockerfile.hbs +37 -0
- package/templates/apps/fullstack/package.json.hbs +61 -0
- package/templates/apps/fullstack/packages/{{projectName}}-contract/README.md.hbs +40 -0
- package/templates/apps/fullstack/packages/{{projectName}}-contract/package.json.hbs +30 -0
- package/templates/apps/fullstack/packages/{{projectName}}-contract/src/admin-contract.ts.hbs +78 -0
- package/templates/apps/fullstack/packages/{{projectName}}-contract/src/admin-schemas.ts.hbs +134 -0
- package/templates/apps/fullstack/packages/{{projectName}}-contract/src/app-contract.ts.hbs +20 -0
- package/templates/apps/fullstack/packages/{{projectName}}-contract/src/app-schemas.ts.hbs +35 -0
- package/templates/apps/fullstack/packages/{{projectName}}-contract/src/index.ts.hbs +7 -0
- package/templates/apps/fullstack/packages/{{projectName}}-contract/src/{{projectName}}-contract.ts.hbs +23 -0
- package/templates/apps/fullstack/packages/{{projectName}}-contract/tests/contract.test.ts.hbs +31 -0
- package/templates/apps/fullstack/packages/{{projectName}}-contract/tsconfig.json.hbs +9 -0
- package/templates/apps/fullstack/packages/{{projectName}}-contract/tsup.config.ts.hbs +9 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/README.md.hbs +84 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_ai.yml.hbs +1 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_audit.yml.hbs +1 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_cache.yml.hbs +1 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_core.yml.hbs +22 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_db.yml.hbs +1 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_feedback.yml.hbs +1 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_iam.yml.hbs +1 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_payment.yml.hbs +1 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_reach.yml.hbs +60 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_scheduler.yml.hbs +1 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_serv.yml.hbs +64 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_storage.yml.hbs +1 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_vecdb.yml.hbs +1 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/data/.gitkeep.hbs +1 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/package.json.hbs +62 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/app-server.ts.hbs +115 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/build-metadata.ts.hbs +7 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/index.ts.hbs +40 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/admin-service.ts.hbs +294 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/audit-jobs.ts.hbs +28 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/business-settings.ts.hbs +50 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/messages.ts.hbs +50 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/procedures/admin-procedures.ts.hbs +106 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/procedures/app-procedures.ts.hbs +49 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/procedures/index.ts.hbs +34 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/runtime.ts.hbs +159 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/security-roles.ts.hbs +73 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/tests/app-server.test.ts.hbs +505 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/tests/runtime.test.ts.hbs +72 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/tests/security.test.ts.hbs +183 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/tsconfig.json.hbs +10 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/tsup.config.ts.hbs +18 -0
- package/templates/apps/fullstack/packages/{{projectName}}-serv/vitest.config.ts.hbs +12 -0
- package/templates/apps/fullstack/packages/{{projectName}}-shared/.gitignore.hbs +10 -0
- package/templates/apps/fullstack/packages/{{projectName}}-shared/README.md.hbs +97 -0
- package/templates/apps/fullstack/packages/{{projectName}}-shared/messages/en-US.json.hbs +164 -0
- package/templates/apps/fullstack/packages/{{projectName}}-shared/messages/ja-JP.json.hbs +164 -0
- package/templates/apps/fullstack/packages/{{projectName}}-shared/messages/zh-CN.json.hbs +164 -0
- package/templates/apps/fullstack/packages/{{projectName}}-shared/package.json.hbs +55 -0
- package/templates/apps/fullstack/packages/{{projectName}}-shared/project.inlang/settings.json.hbs +11 -0
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/index.ts.hbs +7 -0
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/api/api-client.ts.hbs +94 -0
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/api/feedback-client.ts.hbs +36 -0
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/api/sensitive-actions.svelte.ts.hbs +16 -0
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/api/session-recovery.svelte.ts.hbs +4 -0
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/AdminWorkspace.svelte.hbs +122 -0
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/AppShell.svelte.hbs +90 -0
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/FeedbackEntry.svelte.hbs +86 -0
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/audit.svelte.hbs +94 -0
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/configuration.svelte.hbs +25 -0
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/feedback.svelte.hbs +157 -0
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/roles.svelte.hbs +77 -0
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/settings.svelte.hbs +44 -0
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/users.svelte.hbs +216 -0
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/security/role-label.ts.hbs +12 -0
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/stores/guide-store.ts.hbs +55 -0
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/stores/management-store.svelte.ts.hbs +614 -0
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/stores/theme-store.svelte.ts.hbs +23 -0
- package/templates/apps/fullstack/packages/{{projectName}}-shared/svelte.config.js.hbs +1 -0
- package/templates/apps/fullstack/packages/{{projectName}}-shared/tests/api-client.test.ts.hbs +150 -0
- package/templates/apps/fullstack/packages/{{projectName}}-shared/tests/management-store.test.ts.hbs +253 -0
- package/templates/apps/fullstack/packages/{{projectName}}-shared/tsconfig.json.hbs +17 -0
- package/templates/apps/fullstack/packages/{{projectName}}-shared/vitest.config.ts.hbs +7 -0
- package/templates/apps/fullstack/playwright.config.ts.hbs +114 -0
- package/templates/apps/fullstack/playwright.session-close.config.ts.hbs +18 -0
- package/templates/apps/fullstack/pnpm-workspace.yaml.hbs +38 -0
- package/templates/apps/fullstack/tsconfig.base.json +14 -0
- package/templates/apps/h5/messages/en-US.json +3 -0
- package/templates/apps/h5/messages/ja-JP.json +40 -0
- package/templates/apps/h5/messages/zh-CN.json +3 -0
- package/templates/apps/h5/src/routes/+layout.svelte.hbs +85 -30
- package/templates/apps/h5/src/routes/+page.svelte.hbs +20 -8
- package/templates/apps/h5/src/routes/discover/+page.svelte.hbs +17 -6
- package/templates/apps/h5/src/routes/profile/+page.svelte.hbs +17 -6
- package/templates/apps/mobile-app/.env.hbs +3 -0
- package/templates/apps/{android-app → mobile-app}/capacitor.config.ts.hbs +2 -2
- package/templates/apps/mobile-app/index.html.hbs +14 -0
- package/templates/apps/mobile-app/messages/en-US.json +36 -0
- package/templates/apps/mobile-app/messages/ja-JP.json +36 -0
- package/templates/apps/mobile-app/messages/zh-CN.json +36 -0
- package/templates/apps/mobile-app/public/runtime-config.js +1 -0
- package/templates/apps/mobile-app/src/App.svelte.hbs +153 -0
- package/templates/apps/mobile-app/src/lib/api.ts.hbs +213 -0
- package/templates/apps/mobile-app/src/lib/app-config.ts.hbs +14 -0
- package/templates/apps/{android-app → mobile-app}/src/lib/capacitor.ts +6 -7
- package/templates/apps/mobile-app/src/lib/pages/home.svelte.hbs +58 -0
- package/templates/apps/mobile-app/src/lib/pages/profile.svelte.hbs +13 -0
- package/templates/apps/mobile-app/src/lib/pages/tasks.svelte.hbs +16 -0
- package/templates/apps/mobile-app/src/main.ts.hbs +11 -0
- package/templates/apps/website/messages/en-US.json +4 -2
- package/templates/apps/website/messages/ja-JP.json +34 -0
- package/templates/apps/website/messages/zh-CN.json +4 -2
- package/templates/apps/website/scripts/static-website.mjs.hbs +67 -0
- package/templates/apps/website/src/routes/+layout.svelte.hbs +77 -26
- package/templates/apps/website/src/routes/+layout.ts +3 -0
- package/templates/apps/website/src/routes/+page.svelte.hbs +31 -12
- package/templates/apps/website/src/routes/about/+page.svelte.hbs +11 -6
- package/templates/apps/website/src/routes/api/health/+server.ts +3 -0
- package/templates/apps/website/src/routes/contact/+page.svelte.hbs +44 -34
- package/templates/apps/website/src/routes/services/+page.svelte.hbs +27 -40
- package/templates/base/.dockerignore.hbs +27 -0
- package/templates/base/.gitignore.hbs +41 -0
- package/templates/base/.npmrc.hbs +1 -0
- package/templates/base/e2e/app.spec.ts.hbs +213 -0
- package/templates/base/e2e/helpers.ts +1 -1
- package/templates/base/eslint-rules.js +45 -0
- package/templates/base/eslint.config.js.hbs +34 -0
- package/templates/base/hai.json.hbs +4 -0
- package/templates/base/images/web/nginx.conf.hbs +24 -0
- package/templates/base/images/web.Dockerfile.hbs +72 -0
- package/templates/base/package.json.hbs +125 -49
- package/templates/base/playwright.config.ts +44 -6
- package/templates/base/pnpm-workspace.yaml.hbs +40 -0
- package/templates/base/src/app.css.hbs +40 -0
- package/templates/base/src/app.d.ts.hbs +14 -1
- package/templates/base/src/app.html.hbs +13 -0
- package/templates/base/src/hooks.server.ts.hbs +115 -43
- package/templates/base/src/lib/build-metadata.ts.hbs +7 -0
- package/templates/base/src/lib/server/init.ts.hbs +125 -152
- package/templates/base/src/lib/server/kit-config.ts.hbs +40 -0
- package/templates/base/src/routes/+layout.server.ts.hbs +35 -0
- package/templates/base/static/favicon.png +0 -0
- package/templates/base/svelte.config.js.hbs +84 -5
- package/templates/base/tests/scaffold.test.ts.hbs +102 -0
- package/templates/base/tsconfig.json.hbs +50 -0
- package/templates/base/vite.config.ts.hbs +117 -12
- package/templates/base/vitest.config.ts.hbs +19 -0
- package/templates/features/ai/routes/api/chat/+server.ts +6 -7
- package/templates/features/iam/routes-admin/(auth)/+layout.svelte.hbs +3 -17
- package/templates/features/iam/routes-admin/(auth)/auth/forgot-password/+page.svelte.hbs +10 -0
- package/templates/features/iam/routes-admin/(auth)/auth/login/+page.svelte.hbs +9 -96
- package/templates/features/iam/routes-admin/(auth)/auth/register/+page.svelte.hbs +9 -122
- package/templates/features/iam/routes-admin/(auth)/auth/reset-password/+page.svelte.hbs +10 -0
- package/templates/features/iam/routes-h5/auth/forgot-password/+page.svelte.hbs +10 -0
- package/templates/features/iam/routes-h5/auth/login/+page.svelte.hbs +9 -96
- package/templates/features/iam/routes-h5/auth/register/+page.svelte.hbs +9 -112
- package/templates/features/iam/routes-h5/auth/reset-password/+page.svelte.hbs +10 -0
- package/templates/features/iam/routes-shared/api/auth/captcha/+server.ts.hbs +4 -0
- package/templates/features/iam/routes-shared/api/auth/config/+server.ts +4 -0
- package/templates/features/iam/routes-shared/api/auth/login/+server.ts +1 -29
- package/templates/features/iam/routes-shared/api/auth/login/ldap/+server.ts +4 -0
- package/templates/features/iam/routes-shared/api/auth/login/otp/+server.ts +4 -0
- package/templates/features/iam/routes-shared/api/auth/logout/+server.ts +13 -6
- package/templates/features/iam/routes-shared/api/auth/otp/send/+server.ts +4 -0
- package/templates/features/iam/routes-shared/api/auth/password/renew/+server.ts +5 -0
- package/templates/features/iam/routes-shared/api/auth/password-reset/confirm/+server.ts +4 -0
- package/templates/features/iam/routes-shared/api/auth/password-reset/request/+server.ts +4 -0
- package/templates/features/iam/routes-shared/api/auth/reauthenticate/+server.ts +5 -0
- package/templates/features/iam/routes-shared/api/auth/register/+server.ts +1 -23
- package/templates/features/iam/routes-shared/api/auth/session/close/+server.ts.hbs +4 -0
- package/templates/features/iam/routes-website/auth/forgot-password/+page.svelte.hbs +10 -0
- package/templates/features/iam/routes-website/auth/login/+page.svelte.hbs +11 -0
- package/templates/features/iam/routes-website/auth/register/+page.svelte.hbs +11 -0
- package/templates/features/iam/routes-website/auth/reset-password/+page.svelte.hbs +10 -0
- package/templates/features/payment/routes/api/v1/payment/create/+server.ts +25 -23
- package/templates/features/payment/routes/api/v1/payment/notify/[provider]/+server.ts +5 -2
- package/templates/features/storage/routes/api/upload/+server.ts +10 -8
- package/templates/i18n/project.inlang/settings.json +2 -2
- package/templates/shared/.gitattributes +11 -0
- package/templates/shared/build-metadata.ts +42 -0
- package/templates/shared/container-runtime-config.sh +30 -0
- package/templates/shared/e2e-env.ts +98 -0
- package/templates/shared/hash-router.svelte.ts +83 -0
- package/templates/shared/public-base-path.mjs +19 -0
- package/templates/shared/run-e2e.mjs +104 -0
- package/templates/shared/vite-runtime-config.ts +56 -0
- package/templates/skills/AGENTS.md +75 -16
- package/templates/skills/CLAUDE.md +0 -11
- package/templates/skills/bridges/admin/AGENTS.md +89 -0
- package/templates/skills/bridges/api/AGENTS.md +83 -0
- package/templates/skills/bridges/fullstack/AGENTS.md.hbs +102 -0
- package/templates/skills/bridges/h5/AGENTS.md +88 -0
- package/templates/skills/bridges/mobile-app/AGENTS.md +100 -0
- package/templates/skills/bridges/website/AGENTS.md +88 -0
- package/templates/skills/copilot-instructions.md +3 -59
- package/templates/skills/hai-ai/SKILL.md +44 -715
- package/templates/skills/hai-ai/guide.md +457 -0
- package/templates/skills/hai-ai/reference.md +292 -0
- package/templates/skills/hai-api-client/SKILL.md +26 -339
- package/templates/skills/hai-api-client/reference.md +180 -0
- package/templates/skills/hai-api-contract/SKILL.md +36 -0
- package/templates/skills/hai-api-contract/reference.md +280 -0
- package/templates/skills/hai-app-create/SKILL.md +38 -351
- package/templates/skills/hai-app-create/collaboration.md +52 -0
- package/templates/skills/hai-app-create/development.md +243 -0
- package/templates/skills/hai-app-create/reference.md +376 -0
- package/templates/skills/hai-app-review/SKILL.md +57 -126
- package/templates/skills/hai-app-review/differential.md +34 -0
- package/templates/skills/hai-app-tests/SKILL.md +34 -454
- package/templates/skills/hai-app-tests/reference.md +462 -0
- package/templates/skills/hai-app-tests/validation.md +42 -0
- package/templates/skills/hai-audit/SKILL.md +37 -122
- package/templates/skills/hai-audit/reference.md +184 -0
- package/templates/skills/hai-build/SKILL.md +62 -315
- package/templates/skills/hai-build/paths.md +30 -0
- package/templates/skills/hai-build/reference.md +353 -0
- package/templates/skills/hai-cache/SKILL.md +26 -175
- package/templates/skills/hai-cache/reference.md +229 -0
- package/templates/skills/hai-capacitor/SKILL.md +24 -364
- package/templates/skills/hai-capacitor/reference.md +358 -0
- package/templates/skills/hai-change-plan/SKILL.md +46 -0
- package/templates/skills/hai-ci/SKILL.md +114 -0
- package/templates/skills/hai-core/SKILL.md +31 -469
- package/templates/skills/hai-core/reference.md +557 -0
- package/templates/skills/hai-core/request-access.md +65 -0
- package/templates/skills/hai-crypto/SKILL.md +29 -180
- package/templates/skills/hai-crypto/reference.md +317 -0
- package/templates/skills/hai-datapipe/SKILL.md +24 -207
- package/templates/skills/hai-datapipe/reference.md +204 -0
- package/templates/skills/hai-deploy/SKILL.md +53 -178
- package/templates/skills/hai-deploy/credentials.md +19 -0
- package/templates/skills/hai-deploy/middleware.md +129 -0
- package/templates/skills/hai-feedback/SKILL.md +85 -0
- package/templates/skills/hai-framework-sync/SKILL.md +105 -0
- package/templates/skills/hai-framework-sync/sync.md +49 -0
- package/templates/skills/hai-iam/SKILL.md +53 -325
- package/templates/skills/hai-iam/reference.md +535 -0
- package/templates/skills/hai-kit/SKILL.md +33 -329
- package/templates/skills/hai-kit/reference.md +202 -0
- package/templates/skills/hai-payment/SKILL.md +28 -262
- package/templates/skills/hai-payment/reference.md +295 -0
- package/templates/skills/hai-pr-review/SKILL.md +41 -0
- package/templates/skills/hai-reach/SKILL.md +28 -300
- package/templates/skills/hai-reach/reference.md +363 -0
- package/templates/skills/hai-reldb/SKILL.md +28 -449
- package/templates/skills/hai-reldb/reference.md +500 -0
- package/templates/skills/hai-scheduler/SKILL.md +26 -174
- package/templates/skills/hai-scheduler/reference.md +216 -0
- package/templates/skills/hai-serv/SKILL.md +60 -0
- package/templates/skills/hai-serv/reference.md +792 -0
- package/templates/skills/hai-spec-implement/SKILL.md +50 -0
- package/templates/skills/hai-spec-implement/changes.md +67 -0
- package/templates/skills/hai-spec-implement/evaluation.md +18 -0
- package/templates/skills/hai-spec-implement/handoff.md +47 -0
- package/templates/skills/hai-spec-implement/templates.md +117 -0
- package/templates/skills/hai-spec-implement/workflow.md +56 -0
- package/templates/skills/hai-spec-review/SKILL.md +57 -0
- package/templates/skills/hai-spec-review/checklist.md +71 -0
- package/templates/skills/hai-storage/SKILL.md +31 -262
- package/templates/skills/hai-storage/reference.md +284 -0
- package/templates/skills/hai-ui/SKILL.md +62 -238
- package/templates/skills/hai-ui/guide.md +81 -0
- package/templates/skills/hai-ui/reference.md +705 -0
- package/templates/skills/hai-ui-design/SKILL.md +31 -0
- package/templates/skills/hai-ui-design/patterns.md +31 -0
- package/templates/skills/hai-ui-review/SKILL.md +32 -0
- package/templates/skills/hai-ui-review/checklist.md +29 -0
- package/templates/skills/hai-vecdb/SKILL.md +25 -267
- package/templates/skills/hai-vecdb/reference.md +325 -0
- package/templates/skills/opencode.json +0 -5
- package/dist/chunk-2H7UOFLK.js +0 -10
- package/dist/chunk-LANIDJC7.js +0 -659
- package/dist/chunk-LANIDJC7.js.map +0 -1
- package/dist/chunk-YV7WPUI6.js +0 -206
- package/dist/chunk-YV7WPUI6.js.map +0 -1
- package/dist/config-templates-S5RI2MC6.js +0 -5
- package/dist/config-templates-S5RI2MC6.js.map +0 -1
- package/dist/skill-templates-N5DU5FNX.js +0 -5
- package/dist/skill-templates-N5DU5FNX.js.map +0 -1
- package/dist/src-XJ6YXNAF.js +0 -15030
- package/dist/src-XJ6YXNAF.js.map +0 -1
- package/templates/apps/android-app/.env.hbs +0 -3
- package/templates/apps/android-app/messages/en-US.json +0 -24
- package/templates/apps/android-app/messages/zh-CN.json +0 -24
- package/templates/apps/android-app/src/lib/api.ts.hbs +0 -30
- package/templates/apps/android-app/src/routes/+layout.svelte.hbs +0 -42
- package/templates/apps/android-app/src/routes/+layout.ts +0 -7
- package/templates/apps/android-app/src/routes/+page.svelte.hbs +0 -27
- package/templates/apps/api/src/routes/+layout.svelte +0 -14
- package/templates/apps/api/src/routes/+page.svelte +0 -11
- package/templates/apps/api/src/routes/api/v1/health/+server.ts +0 -14
- package/templates/apps/api/src/routes/api/v1/items/+server.ts +0 -37
- package/templates/apps/api/src/routes/api/v1/items/[id]/+server.ts +0 -30
- package/templates/base/src/app.css +0 -2
- package/templates/base/tsconfig.json +0 -8
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { apiContract } from '@h-ai/api-contract'
|
|
2
|
+
import { AppEchoInputSchema, AppEchoOutputDataSchema, AppInfoOutputDataSchema } from './app-schemas.js'
|
|
3
|
+
|
|
4
|
+
/** app 领域 API contract:路径、方法、输入输出 Schema 的单一来源。 */
|
|
5
|
+
export const appContract = {
|
|
6
|
+
info: apiContract.route({
|
|
7
|
+
method: 'POST',
|
|
8
|
+
path: '/app/info',
|
|
9
|
+
operationId: 'app.info',
|
|
10
|
+
tags: ['app'],
|
|
11
|
+
summary: 'Get application runtime information',
|
|
12
|
+
}).output(apiContract.haiResultSchema(AppInfoOutputDataSchema)),
|
|
13
|
+
echo: apiContract.route({
|
|
14
|
+
method: 'POST',
|
|
15
|
+
path: '/app/echo',
|
|
16
|
+
operationId: 'app.echo',
|
|
17
|
+
tags: ['app'],
|
|
18
|
+
summary: 'Echo a message through the generated service route',
|
|
19
|
+
}).input(AppEchoInputSchema).output(apiContract.haiResultSchema(AppEchoOutputDataSchema)),
|
|
20
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { HaiResult } from '@h-ai/core'
|
|
2
|
+
import { z } from 'zod'
|
|
3
|
+
|
|
4
|
+
export const AppFrontendTargetSchema = z.enum(['web', 'app', 'miniapp', 'desktop'])
|
|
5
|
+
|
|
6
|
+
/** 服务信息响应数据。 */
|
|
7
|
+
export const AppInfoOutputDataSchema = z.object({
|
|
8
|
+
notice: z.string().max(200),
|
|
9
|
+
name: z.string(),
|
|
10
|
+
version: z.string(),
|
|
11
|
+
env: z.string(),
|
|
12
|
+
defaultLocale: z.string(),
|
|
13
|
+
supportedLocales: z.array(z.string()),
|
|
14
|
+
startedAt: z.string().datetime(),
|
|
15
|
+
frontends: z.array(AppFrontendTargetSchema),
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
/** Echo 请求体。 */
|
|
19
|
+
export const AppEchoInputSchema = z.object({
|
|
20
|
+
message: z.string().min(1).max(200),
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
/** Echo 响应数据。 */
|
|
24
|
+
export const AppEchoOutputDataSchema = z.object({
|
|
25
|
+
message: z.string(),
|
|
26
|
+
echoedAt: z.string().datetime(),
|
|
27
|
+
requestId: z.string(),
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
export type AppFrontendTarget = z.infer<typeof AppFrontendTargetSchema>
|
|
31
|
+
export type AppInfoOutputData = z.infer<typeof AppInfoOutputDataSchema>
|
|
32
|
+
export type AppInfoOutput = HaiResult<AppInfoOutputData>
|
|
33
|
+
export type AppEchoInput = z.infer<typeof AppEchoInputSchema>
|
|
34
|
+
export type AppEchoOutputData = z.infer<typeof AppEchoOutputDataSchema>
|
|
35
|
+
export type AppEchoOutput = HaiResult<AppEchoOutputData>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { apiContract } from '@h-ai/api-contract'
|
|
2
|
+
import { adminContract, authContract, profileContract } from './admin-contract.js'
|
|
3
|
+
import { appContract } from './app-contract.js'
|
|
4
|
+
|
|
5
|
+
// @hai-generator:contracts:imports
|
|
6
|
+
|
|
7
|
+
/** 当前工程启用的前端目标。 */
|
|
8
|
+
export const ENABLED_FRONTENDS = {{{fullstack.selectedFrontendsJson}}} as const
|
|
9
|
+
export const SUPPORTED_LOCALES = [{{#each fullstack.i18n.locales}}'{{this}}'{{#unless @last}}, {{/unless}}{{/each}}] as const
|
|
10
|
+
|
|
11
|
+
/** 工程级 API contract,前端 typed client 与后端 service 共用。 */
|
|
12
|
+
export const {{fullstack.contractExportName}} = apiContract.create({
|
|
13
|
+
// @hai-generator:contracts:entries
|
|
14
|
+
app: appContract,
|
|
15
|
+
iam: { auth: authContract },
|
|
16
|
+
admin: adminContract,
|
|
17
|
+
profile: profileContract,
|
|
18
|
+
{{#if features.feedback}}
|
|
19
|
+
feedback: apiContract.feedback,
|
|
20
|
+
{{/if}}
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
export type FullstackContract = typeof {{fullstack.contractExportName}}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { apiContract } from '@h-ai/api-contract'
|
|
2
|
+
import { describe, expect, it } from 'vitest'
|
|
3
|
+
import * as contracts from '../src/index.js'
|
|
4
|
+
|
|
5
|
+
describe('{{fullstack.contractPackageName}}', () => {
|
|
6
|
+
it('defines app contract routes', () => {
|
|
7
|
+
expect(contracts.{{fullstack.contractExportName}}.app).toBeDefined()
|
|
8
|
+
expect(contracts.appContract.info).toBeDefined()
|
|
9
|
+
expect(apiContract.pathOf(contracts.appContract.info)).toBe('/app/info')
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
it('validates app info payload', () => {
|
|
13
|
+
const result = contracts.AppInfoOutputDataSchema.safeParse({
|
|
14
|
+
name: '{{fullstack.servPackageName}}',
|
|
15
|
+
version: '0.1.0',
|
|
16
|
+
env: 'test',
|
|
17
|
+
defaultLocale: 'zh-CN',
|
|
18
|
+
supportedLocales: ['zh-CN', 'en-US'],
|
|
19
|
+
startedAt: '2026-01-01T00:00:00.000Z',
|
|
20
|
+
frontends: [...contracts.ENABLED_FRONTENDS],
|
|
21
|
+
notice: '',
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
expect(result.success).toBe(true)
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
it('validates echo input boundaries', () => {
|
|
28
|
+
expect(contracts.AppEchoInputSchema.safeParse({ message: 'hello' }).success).toBe(true)
|
|
29
|
+
expect(contracts.AppEchoInputSchema.safeParse({ message: '' }).success).toBe(false)
|
|
30
|
+
})
|
|
31
|
+
})
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# {{fullstack.servPackageName}}
|
|
2
|
+
|
|
3
|
+
## 简介
|
|
4
|
+
|
|
5
|
+
后端 API Service,基于 `@h-ai/serv` 将共享 contract 装配为最小 HTTP App 抽象,不在应用代码中直接暴露 Hono。
|
|
6
|
+
|
|
7
|
+
## 核心功能清单
|
|
8
|
+
|
|
9
|
+
- HTTP 服务、健康检查和共享 contract 的 procedure 实现。
|
|
10
|
+
- 用户、角色、审计与个人设置的示例业务。
|
|
11
|
+
- 模块配置、加密传输与服务端权限校验。
|
|
12
|
+
|
|
13
|
+
## 使用入门
|
|
14
|
+
|
|
15
|
+
### 默认功能与配置
|
|
16
|
+
|
|
17
|
+
以下是可运行示例的初始实现,角色/权限、管理流程、头像限额、分页、审计策略及部署方案均可按用户需求调整。需求与代码或模板规则不一致时,以需求为业务基线同步实现、测试与过期说明;实际框架 API 契约和服务端安全校验仍需满足。
|
|
18
|
+
|
|
19
|
+
- `config/_core.yml`:应用元信息(名称 / 版本 / 环境 / 语言),启动时由 `core.config` 加载。
|
|
20
|
+
- `config/_serv.yml`:服务端运行时设置(CORS 来源等)。
|
|
21
|
+
- `config/_db.yml` / `_cache.yml` / `_iam.yml` / `_storage.yml` / `_audit.yml` / `_reach.yml`:管理及开发通知配置,初始化前使用模块公开 Schema 验证。Reach 默认控制台 Provider,生产按注释替换为 SMTP/短信/API。
|
|
22
|
+
- `config/_ai.yml` / `_vecdb.yml` / `_payment.yml` / `_scheduler.yml`:保留未启用模块的完整注释示例;配置文件存在不代表模块已接入。先在本 workspace 安装对应依赖,再在 `src/server/runtime.ts` 按公开 Schema 校验、初始化并反序关闭,接入实际业务后按需取消注释。整份注释先去掉一层外层注释,可选字段继续按需开启。
|
|
23
|
+
- `src/index.ts`:进程入口,先 `core.init({ configDir: './config' })`,再初始化 reldb → cache → crypto → storage → reach(有配置时)→ audit → iam → scheduler(有任务时);启动失败及退出反序释放。
|
|
24
|
+
- 选择 AI 时自动安装 reldb/vecdb/AI,并在 reldb/cache 之后初始化 vecdb、在其余依赖之后初始化 AI;关闭时先释放 AI,最后释放 vecdb/关系库,关闭失败会返回错误并保留重试义务。生产环境必须使用共享后端,根 `.env.prod` 默认连接 Compose `db/cache/storage/vecdb`,凭据和真实 Reach Provider JSON 私有注入。
|
|
25
|
+
- `src/app-server.ts`:服务装配入口,挂载健康检查、OpenAPI、docs 与 procedures;应用名称 / 版本与 CORS 来源均取自上述配置。
|
|
26
|
+
- `src/server/procedures/app-procedures.ts`:app 领域 procedures,对应 contract 中的 `app.info` / `app.echo` 路由。
|
|
27
|
+
- `src/server/procedures/admin-procedures.ts`:认证、管理员用户/角色/审计管理和个人设置,使用 `.auth()` / `.permission(...)` 守卫。每个请求读取用户最新启用状态、角色和权限。
|
|
28
|
+
- 标准认证 procedures 复用 `@h-ai/serv/features/iam`,注册、登录方式、重置与协议由 `_iam.yml` 控制;登录、退出、修改密码追加业务审计。HTTP App 类型复用公开 `ServHttpApp`。
|
|
29
|
+
- `src/server/admin-service.ts`:角色分配、头像上传和审计编排,全部底层操作复用 hai 模块。初始使用 admin、audit_admin、config_admin、business_operator 和 user 五种角色,四类职责及基础成员权限由 security-roles.ts 配置;锁内重新鉴权,默认禁止管理员自删、自禁用、自降级,并补偿用户更新失败后的角色变更。业务规则可按需求替换,保留的不变量需验证并发与失败路径。
|
|
30
|
+
- `tests/app-server.test.ts`:真实加密 HTTP 测试,覆盖 Cookie/受信原生刷新、RBAC、旧会话权限、并发管理操作、补偿、头像安全、偏好、审计及会话失效。
|
|
31
|
+
|
|
32
|
+
CLI 已在根目录 `.env.dev/.env.test/.env.prod` 为每个环境的 admin、audit_admin、config_admin、business_operator 分别生成独立随机强密码,对应 `HAI_ADMIN_DEFAULT_PASSWORD`、`HAI_AUDIT_ADMIN_PASSWORD`、`HAI_CONFIG_ADMIN_PASSWORD`、`HAI_BUSINESS_OPERATOR_PASSWORD`。已有账号不改密或重新分配职责;未配置密码的独立职责账号不创建;`dev`/`start` 自动读取,首次登录使用用户名 `admin` 和该密码。仅在没有用户时创建管理员,不重设已有用户密码。密码不符合 `_iam.yml` 策略时,启动日志显示具体原因及错误码。环境文件随项目提交供克隆后共享;私有文件或进程变量可覆盖,`.env.example` 不含真实密码。所有业务 JSON 请求和响应强制加密;健康检查和随机路径的只读头像图片可直接访问。头像支持 PNG/JPEG/WebP,最大 512 KiB,校验内容特征和大小后由服务端生成 key,不能指定任意文件路径。
|
|
33
|
+
|
|
34
|
+
默认 SQLite + 本地 storage + memory cache 仅适合本地开发与显式测试,管理写入仅支持单写实例:进程内保护无过期时间,共享 cache 锁定期续租,丢失锁或释放失败会记录错误。多实例读取可使用共享数据库、Redis cache 和 S3 storage,但管理写请求必须统一路由到同一写实例;仅更换 Redis 不提供任意多写副本的最后管理员原子保证。传输在所有节点共享受管版本化密钥环与 registry,不依赖会话粘性;重启加载原 ring,轮换先分发新旧版本再切 activeKeyId。Web 使用 HttpOnly refresh Cookie + 内存 access token;仅同时满足客户端标识、body 通道头与受信原生 Origin 的 App/Desktop 请求返回 refresh token body。
|
|
35
|
+
|
|
36
|
+
业务公告配置与审计在同一事务提交,失败回滚。其他已经提交的管理修改,其补充审计记录采用最大努力策略:写入失败输出包含操作及资源的结构化错误日志,业务仍返回原操作结果,避免将已经完成的变更误报为失败;应用启动时 audit 初始化失败则停止启动。
|
|
37
|
+
|
|
38
|
+
### 开发
|
|
39
|
+
|
|
40
|
+
所有配置字段以模块 Schema 为准;未使用字段和替代 Provider 用注释保留。叶子环境变量只覆盖已有 YAML 路径,先取消相应键的注释,或用 `HAI_DB` / `HAI_CACHE` 等整体对象覆盖;`connectTimeout` 对应 `HAI_CACHE_CONNECTTIMEOUT`。凭据示例在根 `.env.example`,初始管理员密码和传输密钥环按环境生成到 `.env.dev/.env.test/.env.prod`;其他私有值或覆盖值保存在忽略的 `.env.local` 或部署环境。`.gitignore` 的 `**/data/` 忽略所有运行数据及 `.gitkeep`;运行时会创建目录。`crypto.init()` 无配置参数,datapipe 为纯函数,不提供虚构配置。
|
|
41
|
+
|
|
42
|
+
CLI 同时在根 `.env.dev/.env.test/.env.prod` 分别生成独立的 `HAI_CRYPTO_TRANSPORT_SERVERKEYRING`,结构为 `{activeKeyId,keys:{id:{publicKey,privateKey}}}`,运行时不隐式生成。同一环境所有节点从环境文件加载相同 ring,也可由 Secret Manager 统一覆盖,客户端 registry 复用共享 reldb。私钥不进入普通 YAML、PUBLIC_/VITE_、日志或公开响应。轮换先分发含新旧版本的 ring,再切 activeKeyId 并滚动重建;过渡期后删除旧 ID,旧客户端 destroy 后重协商。没有 sticky session 依赖。
|
|
43
|
+
|
|
44
|
+
自动生成的 `.env` 包含已启用模块的环境参数;YAML 显式引用相同变量并保留本地回退。使用 `start:test` / `start:prod` 加载对应环境覆盖。
|
|
45
|
+
|
|
46
|
+
- `pnpm --filter {{fullstack.servPackageName}} dev`
|
|
47
|
+
- `pnpm --filter {{fullstack.servPackageName}} test`
|
|
48
|
+
|
|
49
|
+
### 容器运行
|
|
50
|
+
|
|
51
|
+
从工程根运行 `pnpm build --target docker` 导出镜像。默认带部署工具,可运行 `pnpm run deploy --env test --build` 一次完成构建发布,或 `pnpm run deploy --env prod --bundle-only` 交付已有镜像及跨平台脚本。完整包包含自建中间件,人工部署与按服务更新见工程根 README。
|
|
52
|
+
|
|
53
|
+
- 服务镜像直接由 `@h-ai/serv` 对外监听,配置挂载点为 `/app/config`,数据目录为 `/app/data`,不包含额外转发进程。
|
|
54
|
+
- Web 镜像不烘焙 API 地址;启动时使用 `-e PUBLIC_API_BASE=https://api.example.com` 注入,同一镜像可部署到不同环境。未设置时 Web 默认请求自身 origin。
|
|
55
|
+
- YAML 配置使用 `${ENV_NAME:default}` 接收运行时环境变量;不要把密钥写入镜像。
|
|
56
|
+
|
|
57
|
+
### IAM 认证配置
|
|
58
|
+
|
|
59
|
+
服务端 `config/_iam.yml` 统一控制登录、注册、重置、协议与 `ui` 品牌。邮件/短信可用 `_reach.yml` 中的 Provider/标准模板或自定义发送回调。`AuthPanel` 使用安全公开配置及框架认证动作;无需在页面适配 Token、验证码倒计时和错误提示。Memory 仅用于本地开发和显式测试;生产环境即使只有一个实例,也须使用框架支持的共享数据库与 Redis,并按工程根 README 配置写入路由和传输会话。
|
|
60
|
+
|
|
61
|
+
LDAP/Reach 均可选:基础认证无需安装。默认 LDAP 客户端需在 IAM 服务端 workspace 安装 ldapts,再配置 login.ldap 与连接信息。自定义 onOtpSendEmail/onOtpSendSms/onPasswordResetRequest 回调可独立于 Reach 使用,投递失败须拒绝 Promise;回调第三参数 context 提供 expiresAt/idempotencyKey,发送服务按稳定键持久化去重,异常/超时为结果未知且不自动重发。协议在 Modal 显示;LoginForm/RegisterForm 提供 header/extraFields/footer,AuthPanel 透传 loginHeader/loginFields/loginFooter 和 registerHeader/registerFields/registerFooter。配置及例子见生成项目的 hai-iam/hai-ui skills。
|
|
62
|
+
|
|
63
|
+
协议支持配置中的 userAgreementMarkdown/privacyPolicyMarkdown 正文及 URL;同主题优先 Markdown,由安全渲染器在弹窗显示。
|
|
64
|
+
|
|
65
|
+
增强安全按 _iam.yml/_audit.yml 启用,包含账号生命周期、密码期限/复杂度、ALTCHA、失败窗口锁定、一次性邀请、首次改密、用途绑定再认证、时段/IP、闲置/绝对期限和并发会话策略。页面离开撤销默认关闭且只最佳努力。审计查询支持分类、结果、等级、排序及最近7天统计;私有备份返回 key/数量/SHA-256 回执,失败复用编号,不通过头像资源路由公开。自动备份与轻量告警复用 Scheduler/Reach;归档不是数据库原子快照,恢复在独立环境进行。
|
|
66
|
+
|
|
67
|
+
完整契约见生成应用的 `.agents/skills/hai-iam/reference.md`。
|
|
68
|
+
|
|
69
|
+
## 反馈现场与交付检查
|
|
70
|
+
|
|
71
|
+
feedback.init(config, { collectEvidence }) 支持等待业务 text/data 与文本/ZIP/GZIP 附件;_feedback.yml 提供业务采集超时和 Webhook URL/签名密钥/请求超时。密钥仅放服务端私有环境。Webhook 持久重试,详情展示状态并允许管理员重试。
|
|
72
|
+
|
|
73
|
+
反馈的 console/disabled/reach 独立选择;SDK getView/ratio/saveDraft 与 createFeedbackReview 统一预览、标注、保存及轮询。退出身份清理草稿和观察器。宿主生命周期由 core.module.createRuntime 处理,框架表升级前备份停旧写入并运行各模块 dryRun 预检。
|
|
74
|
+
|
|
75
|
+
## 开发测试
|
|
76
|
+
|
|
77
|
+
```sh
|
|
78
|
+
pnpm --filter {{fullstack.servPackageName}} typecheck
|
|
79
|
+
pnpm --filter {{fullstack.servPackageName}} test
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## 许可证
|
|
83
|
+
|
|
84
|
+
Apache-2.0
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{{{moduleConfig 'ai' features.ai}}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{{{moduleConfig 'audit' true}}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{{{moduleConfig 'cache' true}}}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# =============================================================================
|
|
2
|
+
# {{fullstack.servPackageName}} - 应用配置
|
|
3
|
+
# =============================================================================
|
|
4
|
+
#
|
|
5
|
+
# 后端服务的应用级配置,由 core.config 在启动时加载。
|
|
6
|
+
# 每个叶子项都可由约定式 HAI 环境变量覆盖,敏感值不要写入文件。
|
|
7
|
+
# =============================================================================
|
|
8
|
+
|
|
9
|
+
{{yaml 'name' fullstack.appName}}
|
|
10
|
+
version: 0.1.0
|
|
11
|
+
env: development
|
|
12
|
+
|
|
13
|
+
defaultLocale: {{fullstack.i18n.baseLocale}}
|
|
14
|
+
logging:
|
|
15
|
+
level: info
|
|
16
|
+
format: pretty
|
|
17
|
+
# context:
|
|
18
|
+
# service: api
|
|
19
|
+
redact:
|
|
20
|
+
- password
|
|
21
|
+
- token
|
|
22
|
+
- secret
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{{{moduleConfig 'db' true}}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{{{moduleConfig 'feedback' features.feedback}}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{{{moduleConfig 'iam' true}}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{{{moduleConfig 'payment' features.payment}}}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# 开发触达示例;生产环境改为 SMTP、短信或 API Provider。
|
|
2
|
+
providers:
|
|
3
|
+
- name: email
|
|
4
|
+
type: console
|
|
5
|
+
- name: sms
|
|
6
|
+
type: console
|
|
7
|
+
# SMTP 替换 email 控制台 Provider;同名 Provider 只保留一个。
|
|
8
|
+
# - name: email
|
|
9
|
+
# type: smtp
|
|
10
|
+
# host: ${HAI_REACH_PROVIDERS_0_HOST:smtp.example.com}
|
|
11
|
+
# port: ${HAI_REACH_PROVIDERS_0_PORT:465}
|
|
12
|
+
# secure: true
|
|
13
|
+
# user: ${HAI_REACH_PROVIDERS_0_USER:}
|
|
14
|
+
# pass: ${HAI_REACH_PROVIDERS_0_PASS:}
|
|
15
|
+
# from: ${HAI_REACH_PROVIDERS_0_FROM:noreply@example.com}
|
|
16
|
+
# 阿里云短信替换 sms 控制台 Provider。
|
|
17
|
+
# - name: sms
|
|
18
|
+
# type: aliyun-sms
|
|
19
|
+
# accessKeyId: ${HAI_REACH_PROVIDERS_0_ACCESSKEYID}
|
|
20
|
+
# accessKeySecret: ${HAI_REACH_PROVIDERS_0_ACCESSKEYSECRET}
|
|
21
|
+
# signName: MyApp
|
|
22
|
+
# endpoint: dysmsapi.aliyuncs.com
|
|
23
|
+
# 自有发送服务:按需将 template.provider 改为 custom。
|
|
24
|
+
# - name: custom
|
|
25
|
+
# type: api
|
|
26
|
+
# url: https://notify.example.com/send
|
|
27
|
+
# method: POST # POST | PUT
|
|
28
|
+
# headers:
|
|
29
|
+
# Authorization: '${HAI_REACH_PROVIDERS_0_HEADERS_AUTHORIZATION}'
|
|
30
|
+
# timeout: 10000
|
|
31
|
+
# idempotent: ${HAI_REACH_PROVIDERS_0_IDEMPOTENT:false} # 仅接收端具备原子持久去重时启用;默认在途不明结果转 unknown
|
|
32
|
+
templates:
|
|
33
|
+
- name: otp_email
|
|
34
|
+
provider: email
|
|
35
|
+
subject: Verification code
|
|
36
|
+
body: 'Your verification code is {code}.'
|
|
37
|
+
- name: otp_sms
|
|
38
|
+
provider: sms
|
|
39
|
+
body: 'Your verification code is {code}.'
|
|
40
|
+
- name: password_reset
|
|
41
|
+
provider: email
|
|
42
|
+
subject: Password reset
|
|
43
|
+
body: 'Reset your password: {resetUrl}. Expires at {expiresAt}.'
|
|
44
|
+
- name: password_reset_sms
|
|
45
|
+
provider: sms
|
|
46
|
+
body: 'Reset your password: {resetUrl}. Expires at {expiresAt}.'
|
|
47
|
+
|
|
48
|
+
# 免打扰可选;验证码应避免 delay,以免延迟到失效后投递。
|
|
49
|
+
# dnd:
|
|
50
|
+
# enabled: false
|
|
51
|
+
# strategy: discard # discard | delay
|
|
52
|
+
# start: '22:00'
|
|
53
|
+
# end: '08:00'
|
|
54
|
+
|
|
55
|
+
# 延迟队列策略随每条消息保存;最多领取 5 次,指数退避 1 分钟至 1 小时。
|
|
56
|
+
# 超限 dead 只经可信服务端人工恢复;unknown 不允许自动重发。
|
|
57
|
+
queue:
|
|
58
|
+
maxAttempts: ${HAI_REACH_QUEUE_MAXATTEMPTS:5}
|
|
59
|
+
retryDelayMs: ${HAI_REACH_QUEUE_RETRYDELAYMS:60000}
|
|
60
|
+
maxRetryDelayMs: ${HAI_REACH_QUEUE_MAXRETRYDELAYMS:3600000}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{{{moduleConfig 'scheduler' features.scheduler}}}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# =============================================================================
|
|
2
|
+
# {{fullstack.servPackageName}} - Serv / HTTP 入口配置
|
|
3
|
+
# =============================================================================
|
|
4
|
+
#
|
|
5
|
+
# 业务接口路径直接写在对应 *-contract.ts;公共 HTTP 传输配置以本文件为准。
|
|
6
|
+
# =============================================================================
|
|
7
|
+
|
|
8
|
+
server:
|
|
9
|
+
host: 127.0.0.1
|
|
10
|
+
port: 3000
|
|
11
|
+
|
|
12
|
+
http:
|
|
13
|
+
# auto 仅在受管私有 Compose 代理中采用容器网段;普通宿主忽略转发头。
|
|
14
|
+
# 自建代理填写明确 IP/CIDR;[] 显式忽略转发头。
|
|
15
|
+
trustedProxies: auto
|
|
16
|
+
access: false
|
|
17
|
+
# access:
|
|
18
|
+
# file: ./config/security/request-access.yml
|
|
19
|
+
apiPrefix: /api/v1
|
|
20
|
+
openapi:
|
|
21
|
+
path: /openapi.json
|
|
22
|
+
docs:
|
|
23
|
+
path: /docs
|
|
24
|
+
# requireAuth: true
|
|
25
|
+
health:
|
|
26
|
+
path: /health
|
|
27
|
+
readyPath: /ready
|
|
28
|
+
rpc: false
|
|
29
|
+
# 内部 RPC 可选;启用时用对象替换 false,gateway-only 必须配置网关密钥。
|
|
30
|
+
# rpc:
|
|
31
|
+
# prefix: /api/rpc
|
|
32
|
+
# access: gateway-only # loopback | private-network | gateway-only
|
|
33
|
+
# gatewayHeader: X-Gateway-Key
|
|
34
|
+
# gatewaySecret: ${HAI_SERV_HTTP_RPC_GATEWAYSECRET}
|
|
35
|
+
|
|
36
|
+
cors:
|
|
37
|
+
origin: ${HAI_SERV_CORS_ORIGIN:*}
|
|
38
|
+
nativeOrigins: ${HAI_SERV_CORS_NATIVEORIGINS:http://localhost,https://localhost,http://tauri.localhost,https://tauri.localhost,tauri://localhost,capacitor://localhost}
|
|
39
|
+
credentials: ${HAI_SERV_CORS_CREDENTIALS:true}
|
|
40
|
+
allowedHeaders:
|
|
41
|
+
- Authorization
|
|
42
|
+
- Content-Type
|
|
43
|
+
- X-Request-Id
|
|
44
|
+
- X-Requested-With
|
|
45
|
+
- X-Request-Client
|
|
46
|
+
- X-Hai-Terminal
|
|
47
|
+
- X-HAI-Token-Transport
|
|
48
|
+
- X-Client-Id
|
|
49
|
+
- X-Encrypted
|
|
50
|
+
- X-Hai-Transport
|
|
51
|
+
exposedHeaders:
|
|
52
|
+
- X-Request-Id
|
|
53
|
+
- X-Encrypted
|
|
54
|
+
- X-Hai-Transport
|
|
55
|
+
|
|
56
|
+
transport:
|
|
57
|
+
# 双向业务明文 UTF-8 字节;默认 {{#if features.feedback}}auto(按反馈预算推导){{else}}1 MiB{{/if}},范围 1 KiB~64 MiB,密文容量自动推导。
|
|
58
|
+
{{#if features.feedback}}
|
|
59
|
+
maxPlaintextBytes: ${HAI_SERV_TRANSPORT_MAXPLAINTEXTBYTES:auto}
|
|
60
|
+
{{else}}
|
|
61
|
+
maxPlaintextBytes: ${HAI_SERV_TRANSPORT_MAXPLAINTEXTBYTES:1048576}
|
|
62
|
+
{{/if}}
|
|
63
|
+
keyExchangePath: /_hai/key-exchange
|
|
64
|
+
# excludePaths: [/health, /ready]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{{{moduleConfig 'storage' true}}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{{{moduleConfig 'vecdb' features.vecdb}}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"engines": { "node": "^22.23.4 || ^24.22.1 || ^26.12.1" },
|
|
3
|
+
"name": "{{fullstack.servPackageName}}",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"version": "0.1.0",
|
|
6
|
+
"private": true,
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"import": "./dist/index.js"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"main": "./dist/index.js",
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"files": [
|
|
16
|
+
"config",
|
|
17
|
+
"data",
|
|
18
|
+
"dist",
|
|
19
|
+
"README.md"
|
|
20
|
+
],
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build": "tsup",
|
|
23
|
+
"check": "pnpm typecheck",
|
|
24
|
+
"typecheck": "tsc --noEmit",
|
|
25
|
+
"lint": "eslint .",
|
|
26
|
+
"lint:fix": "eslint . --fix",
|
|
27
|
+
"test": "vitest run",
|
|
28
|
+
"test:watch": "vitest",
|
|
29
|
+
"clean": "rimraf dist .turbo",
|
|
30
|
+
"dev": "pnpm build && node --env-file=../../.env --env-file=../../.env.dev --env-file-if-exists=../../.env.local dist/index.js",
|
|
31
|
+
"start": "node --env-file=../../.env --env-file=../../.env.prod --env-file-if-exists=../../.env.local dist/index.js",
|
|
32
|
+
"dev:test": "pnpm build && node --env-file=../../.env --env-file=../../.env.test --env-file-if-exists=../../.env.local dist/index.js",
|
|
33
|
+
"dev:prod": "pnpm build && pnpm start",
|
|
34
|
+
"start:test": "node --env-file=../../.env --env-file=../../.env.test --env-file-if-exists=../../.env.local dist/index.js",
|
|
35
|
+
"start:prod": "pnpm start"
|
|
36
|
+
},
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"pino-pretty": "{{fullstack.packageSpecifiers.pinoPretty}}",
|
|
39
|
+
{{#if features.ai}}
|
|
40
|
+
"@h-ai/ai": "{{haiPackageSpecifier}}",
|
|
41
|
+
{{/if}}
|
|
42
|
+
"@h-ai/audit": "{{haiPackageSpecifier}}",
|
|
43
|
+
"@h-ai/cache": "{{haiPackageSpecifier}}",
|
|
44
|
+
"@h-ai/core": "{{haiPackageSpecifier}}",
|
|
45
|
+
"@h-ai/crypto": "{{haiPackageSpecifier}}",
|
|
46
|
+
{{#if features.feedback}}
|
|
47
|
+
"@h-ai/api-contract": "{{haiPackageSpecifier}}",
|
|
48
|
+
"@h-ai/feedback": "{{haiPackageSpecifier}}",
|
|
49
|
+
{{/if}}
|
|
50
|
+
"@h-ai/iam": "{{haiPackageSpecifier}}",
|
|
51
|
+
"@h-ai/reach": "{{haiPackageSpecifier}}",
|
|
52
|
+
"@h-ai/reldb": "{{haiPackageSpecifier}}",
|
|
53
|
+
"@h-ai/scheduler": "{{haiPackageSpecifier}}",
|
|
54
|
+
"@h-ai/serv": "{{haiPackageSpecifier}}",
|
|
55
|
+
"@h-ai/storage": "{{haiPackageSpecifier}}",
|
|
56
|
+
{{#if features.vecdb}}
|
|
57
|
+
"@h-ai/vecdb": "{{haiPackageSpecifier}}",
|
|
58
|
+
{{/if}}
|
|
59
|
+
"zod": "{{fullstack.packageSpecifiers.zod}}",
|
|
60
|
+
"{{fullstack.contractPackageName}}": "workspace:*"
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import type { CoreConfig } from '@h-ai/core'
|
|
2
|
+
import type { {{#if features.feedback}}CreateServContext, {{/if}}ServHttpApp } from '@h-ai/serv'
|
|
3
|
+
import process from 'node:process'
|
|
4
|
+
{{#if features.feedback}}import { resolveFeedbackBudget } from '@h-ai/api-contract/feedback'
|
|
5
|
+
{{/if}}import { core } from '@h-ai/core'
|
|
6
|
+
import { createReldbTransportKeyStore, crypto, parseTransportServerKeyRing } from '@h-ai/crypto'
|
|
7
|
+
{{#if features.feedback}}
|
|
8
|
+
import { FeedbackConfigSchema } from '@h-ai/feedback'
|
|
9
|
+
{{/if}}
|
|
10
|
+
import { iam } from '@h-ai/iam'
|
|
11
|
+
import { reldb } from '@h-ai/reldb'
|
|
12
|
+
import { serv, ServConfigSchema } from '@h-ai/serv'
|
|
13
|
+
import { storage } from '@h-ai/storage'
|
|
14
|
+
import * as contracts from '{{fullstack.contractPackageName}}'
|
|
15
|
+
import { createServerProcedures } from './server/procedures/index.js'
|
|
16
|
+
|
|
17
|
+
const STARTED_AT = new Date().toISOString()
|
|
18
|
+
|
|
19
|
+
/** 创建后端 HTTP App;与 @h-ai/serv 的 createApp 返回语义保持一致。 */
|
|
20
|
+
export function createServerApp(): ServHttpApp {
|
|
21
|
+
const coreConfig = core.config.getOrThrow<CoreConfig>('core')
|
|
22
|
+
const servConfig = ServConfigSchema.parse(core.config.getOrThrow('serv'))
|
|
23
|
+
if (servConfig.transport === false)
|
|
24
|
+
throw new Error('Fullstack management requires transport encryption; configure scoped access.rules[].encryption: disabled for plaintext external endpoints')
|
|
25
|
+
const ring = parseTransportServerKeyRing(process.env.HAI_CRYPTO_TRANSPORT_SERVERKEYRING)
|
|
26
|
+
if (!ring.success)
|
|
27
|
+
throw new Error(`HAI_CRYPTO_TRANSPORT_SERVERKEYRING: ${ring.error.message} (${ring.error.code})`)
|
|
28
|
+
{{#if features.feedback}} // 可选反馈故障重试期间仍使用配置预算,避免缓存默认容量。
|
|
29
|
+
const feedbackSettings = FeedbackConfigSchema.safeParse(core.config.get('feedback'))
|
|
30
|
+
const feedbackBudget = resolveFeedbackBudget(feedbackSettings.success ? feedbackSettings.data : {})
|
|
31
|
+
{{/if}} const security = serv.createRuntimeSecurityPolicy({
|
|
32
|
+
environment: coreConfig.env,
|
|
33
|
+
corsOrigin: servConfig.cors.origin,
|
|
34
|
+
nativeOrigins: servConfig.cors.nativeOrigins,
|
|
35
|
+
})
|
|
36
|
+
const publicPaths = [
|
|
37
|
+
`${servConfig.http.apiPrefix}/assets`,
|
|
38
|
+
...(servConfig.http.health ? [servConfig.http.health.path, servConfig.http.health.readyPath].filter((path): path is `/${string}` => Boolean(path)) : []),
|
|
39
|
+
...(servConfig.http.openapi && security.exposeApiDocs ? [servConfig.http.openapi.path] : []),
|
|
40
|
+
...(servConfig.http.docs && security.exposeApiDocs ? [servConfig.http.docs.path] : []),
|
|
41
|
+
]
|
|
42
|
+
{{#if features.feedback}}
|
|
43
|
+
// 普通业务日志与反馈使用同一可信应用范围;身份仍由 IAM 验证。
|
|
44
|
+
const authenticatedContext = serv.buildAuthContextFactory(token => iam.auth.verifyToken(token))
|
|
45
|
+
const createContext: CreateServContext = async input => ({
|
|
46
|
+
...await authenticatedContext(input),
|
|
47
|
+
scopeId: '{{projectName}}',
|
|
48
|
+
})
|
|
49
|
+
{{/if}}
|
|
50
|
+
|
|
51
|
+
return serv.createApp({
|
|
52
|
+
contract: contracts.{{fullstack.contractExportName}},
|
|
53
|
+
procedures: createServerProcedures({
|
|
54
|
+
app: {
|
|
55
|
+
name: coreConfig.name,
|
|
56
|
+
version: coreConfig.version,
|
|
57
|
+
env: coreConfig.env,
|
|
58
|
+
defaultLocale: coreConfig.defaultLocale,
|
|
59
|
+
supportedLocales: contracts.SUPPORTED_LOCALES,
|
|
60
|
+
startedAt: STARTED_AT,
|
|
61
|
+
frontends: [...contracts.ENABLED_FRONTENDS],
|
|
62
|
+
},
|
|
63
|
+
}),
|
|
64
|
+
iam,
|
|
65
|
+
{{#if features.feedback}}
|
|
66
|
+
createContext,
|
|
67
|
+
{{/if}}
|
|
68
|
+
refreshCookie: {
|
|
69
|
+
secure: security.secureRefreshCookie,
|
|
70
|
+
nativeTokenTransport: { isRequest: request => request.headers.get('x-hai-token-transport') === 'body'
|
|
71
|
+
&& ['{{projectName}}-app', '{{projectName}}-desktop'].includes(request.headers.get('x-request-client') ?? '')
|
|
72
|
+
&& security.isNativeOrigin(request.headers.get('origin') ?? '') },
|
|
73
|
+
},
|
|
74
|
+
// 管理业务默认强制双向加密;各节点复用受管服务端密钥环与共享 reldb 客户端 registry。
|
|
75
|
+
transport: {
|
|
76
|
+
...servConfig.transport,
|
|
77
|
+
{{#if features.feedback}}
|
|
78
|
+
routePlaintextBytes: {
|
|
79
|
+
[`${servConfig.http.apiPrefix}/feedback/reports`]: feedbackBudget.maxTransportBytes,
|
|
80
|
+
[`${servConfig.http.apiPrefix}/feedback/reports/*/evidence`]: feedbackBudget.maxTransportBytes,
|
|
81
|
+
[`${servConfig.http.apiPrefix}/feedback/reports/*/attachments/*`]: feedbackBudget.maxTransportBytes,
|
|
82
|
+
[`${servConfig.http.apiPrefix}/feedback/reports/*/exports/*`]: feedbackBudget.maxTransportBytes,
|
|
83
|
+
...servConfig.transport.routePlaintextBytes,
|
|
84
|
+
},
|
|
85
|
+
{{/if}}
|
|
86
|
+
excludePaths: [...new Set([...publicPaths, ...servConfig.transport.excludePaths])],
|
|
87
|
+
crypto,
|
|
88
|
+
serverKeyRing: ring.data,
|
|
89
|
+
keyStore: createReldbTransportKeyStore({ reldb, ttlSeconds: 3600 }),
|
|
90
|
+
},
|
|
91
|
+
http: {
|
|
92
|
+
...servConfig.http,
|
|
93
|
+
openapi: security.exposeApiDocs ? servConfig.http.openapi : false,
|
|
94
|
+
docs: security.exposeApiDocs ? servConfig.http.docs : false,
|
|
95
|
+
},
|
|
96
|
+
middlewares: [{
|
|
97
|
+
middleware: serv.cors({
|
|
98
|
+
origin: security.allowOrigin,
|
|
99
|
+
credentials: servConfig.cors.credentials,
|
|
100
|
+
allowedHeaders: [...new Set([...servConfig.cors.allowedHeaders, 'X-Client-Id', 'X-Encrypted', 'X-Hai-Transport', 'X-Request-Client', 'X-HAI-Token-Transport', 'X-Hai-Reauthentication', 'X-Hai-Terminal'])],
|
|
101
|
+
exposedHeaders: [...new Set([...servConfig.cors.exposedHeaders, 'X-Encrypted', 'X-Hai-Transport'])],
|
|
102
|
+
}),
|
|
103
|
+
}, {
|
|
104
|
+
path: `${servConfig.http.apiPrefix}/assets/*`,
|
|
105
|
+
middleware: serv.storageAssets({
|
|
106
|
+
storage,
|
|
107
|
+
pathPrefix: `${servConfig.http.apiPrefix}/assets/`,
|
|
108
|
+
keyPattern: /^avatars\/[\w-]+\/[\w-]+\.(png|jpg|webp)$/,
|
|
109
|
+
allowedContentTypes: ['image/png', 'image/jpeg', 'image/webp'],
|
|
110
|
+
cacheControl: 'public, max-age=31536000, immutable',
|
|
111
|
+
crossOriginResourcePolicy: 'cross-origin',
|
|
112
|
+
}),
|
|
113
|
+
}],
|
|
114
|
+
})
|
|
115
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { CoreConfig } from '@h-ai/core'
|
|
2
|
+
import type { ServConfigInput } from '@h-ai/serv'
|
|
3
|
+
import process from 'node:process'
|
|
4
|
+
import { core } from '@h-ai/core'
|
|
5
|
+
import { serv, ServConfigSchema } from '@h-ai/serv'
|
|
6
|
+
import { createServerApp } from './app-server.js'
|
|
7
|
+
import { closeModules, initializeModules } from './server/runtime.js'
|
|
8
|
+
|
|
9
|
+
async function startServer(): Promise<void> {
|
|
10
|
+
// 加载配置后按依赖顺序初始化;日志设置沿用 _core.yml。
|
|
11
|
+
core.init({ configDir: './config' })
|
|
12
|
+
await initializeModules()
|
|
13
|
+
try {
|
|
14
|
+
// HOST/PORT 环境变量优先于 config/_serv.yml。
|
|
15
|
+
const { server } = ServConfigSchema.parse(core.config.getOrThrow<ServConfigInput>('serv'))
|
|
16
|
+
const app = createServerApp()
|
|
17
|
+
await new Promise<void>((resolve, reject) => {
|
|
18
|
+
const listener = serv.listen(app, {
|
|
19
|
+
host: server.host,
|
|
20
|
+
port: server.port,
|
|
21
|
+
onListening: (info) => {
|
|
22
|
+
core.logger.info('Application listening', { name: core.config.getOrThrow<CoreConfig>('core').name, host: server.host, port: info.port })
|
|
23
|
+
resolve()
|
|
24
|
+
},
|
|
25
|
+
onClose: closeModules,
|
|
26
|
+
})
|
|
27
|
+
listener.server.once('error', reject)
|
|
28
|
+
listener.server.once('listening', () => listener.server.off('error', reject))
|
|
29
|
+
})
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
await closeModules()
|
|
33
|
+
throw error
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
void startServer().catch((error: unknown) => {
|
|
38
|
+
core.logger.fatal('Application startup failed', { error })
|
|
39
|
+
process.exitCode = 1
|
|
40
|
+
})
|