@h-ai/cli 1.0.0-beta.2 → 1.0.0-beta.20
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 +74 -154
- package/README.ja.md +74 -154
- package/README.md +74 -154
- package/dist/{chunk-AKV2CQZV.js → chunk-2ZL5ORQ2.js} +316 -97
- package/dist/chunk-2ZL5ORQ2.js.map +1 -0
- package/dist/{chunk-EF7WOFWZ.js → chunk-L53P43B2.js} +1312 -163
- package/dist/chunk-L53P43B2.js.map +1 -0
- package/dist/cli-config-templates-LWMD2I6V.js +5 -0
- package/dist/{cli-config-templates-HHFHNXBN.js.map → cli-config-templates-LWMD2I6V.js.map} +1 -1
- package/dist/{cli-skill-templates-PN4FLGBC.js → cli-skill-templates-D6S2OQ6X.js} +3 -3
- package/dist/{cli-skill-templates-PN4FLGBC.js.map → cli-skill-templates-D6S2OQ6X.js.map} +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +30118 -3253
- package/dist/index.js.map +1 -1
- package/dist/metafile-esm.json +1 -1
- package/package.json +22 -18
- package/templates/README.md +45 -30
- package/templates/apps/admin/src/routes/+layout.svelte.hbs +16 -10
- package/templates/apps/admin/src/routes/+page.svelte.hbs +4 -4
- package/templates/apps/admin/src/routes/admin/+layout.svelte.hbs +17 -7
- package/templates/apps/admin/src/routes/admin/+page.svelte.hbs +26 -6
- package/templates/apps/admin/src/routes/admin/settings/+page.svelte.hbs +2 -2
- package/templates/apps/api/.dockerignore.hbs +10 -0
- package/templates/apps/api/apps/{{projectName}}-contract/README.md.hbs +8 -0
- package/templates/apps/api/apps/{{projectName}}-contract/src/app-schemas.ts.hbs +6 -1
- package/templates/apps/api/apps/{{projectName}}-contract/src/{{projectName}}-contract.ts.hbs +13 -5
- package/templates/apps/api/apps/{{projectName}}-contract/tests/contract.test.ts.hbs +13 -5
- package/templates/apps/api/apps/{{projectName}}-contract/tsup.config.ts.hbs +3 -1
- package/templates/apps/api/apps/{{projectName}}-serv/README.md.hbs +6 -2
- package/templates/apps/api/apps/{{projectName}}-serv/config/_serv.yml.hbs +24 -2
- package/templates/apps/api/apps/{{projectName}}-serv/package.json.hbs +4 -4
- package/templates/apps/api/apps/{{projectName}}-serv/src/app.ts.hbs +74 -24
- package/templates/apps/api/apps/{{projectName}}-serv/src/build-metadata.ts.hbs +0 -1
- package/templates/apps/api/apps/{{projectName}}-serv/src/lib/server/init.ts.hbs +153 -37
- package/templates/apps/api/apps/{{projectName}}-serv/src/server/procedures/index.ts.hbs +39 -13
- package/templates/apps/api/apps/{{projectName}}-serv/tests/service.test.ts.hbs +106 -33
- package/templates/apps/api/apps/{{projectName}}-serv/tsup.config.ts.hbs +3 -1
- package/templates/apps/api/apps/{{projectName}}-serv/vitest.config.ts.hbs +3 -1
- package/templates/apps/api/e2e/api-serv.spec.ts.hbs +20 -3
- package/templates/apps/api/e2e/capture-evidence.ts.hbs +4 -1
- package/templates/apps/api/eslint.config.js.hbs +3 -1
- package/templates/apps/api/images/serv.Dockerfile.hbs +15 -10
- package/templates/apps/api/playwright.config.ts.hbs +23 -19
- package/templates/apps/api/pnpm-workspace.yaml.hbs +27 -16
- package/templates/apps/fullstack/.dockerignore.hbs +10 -0
- package/templates/apps/fullstack/.gitignore.hbs +7 -2
- package/templates/apps/fullstack/apps/{{projectName}}-app/README.md.hbs +14 -12
- package/templates/apps/fullstack/apps/{{projectName}}-app/capacitor.config.ts.hbs +5 -3
- package/templates/apps/fullstack/apps/{{projectName}}-app/eslint.config.js.hbs +10 -2
- package/templates/apps/fullstack/apps/{{projectName}}-app/package.json.hbs +1 -1
- package/templates/apps/fullstack/apps/{{projectName}}-app/src/App.svelte.hbs +10 -2
- package/templates/apps/fullstack/apps/{{projectName}}-app/src/build-metadata.ts.hbs +0 -1
- package/templates/apps/fullstack/apps/{{projectName}}-app/src/lib/capacitor.ts.hbs +0 -1
- package/templates/apps/fullstack/apps/{{projectName}}-app/svelte.config.js.hbs +4 -1
- package/templates/apps/fullstack/apps/{{projectName}}-app/vite.config.ts.hbs +16 -4
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/README.md.hbs +10 -8
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/package.json.hbs +3 -3
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/src/App.svelte.hbs +8 -2
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/src/build-metadata.ts.hbs +0 -1
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/tauri.conf.json.hbs +0 -2
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/svelte.config.js.hbs +4 -1
- package/templates/apps/fullstack/apps/{{projectName}}-desktop/vite.config.ts.hbs +16 -4
- package/templates/apps/fullstack/apps/{{projectName}}-miniapp/README.md.hbs +2 -0
- package/templates/apps/fullstack/apps/{{projectName}}-web/README.md.hbs +9 -9
- package/templates/apps/fullstack/apps/{{projectName}}-web/eslint.config.js.hbs +10 -2
- package/templates/apps/fullstack/apps/{{projectName}}-web/index.html.hbs +1 -0
- package/templates/apps/fullstack/apps/{{projectName}}-web/src/build-metadata.ts.hbs +0 -1
- package/templates/apps/fullstack/apps/{{projectName}}-web/src/main.ts.hbs +0 -2
- package/templates/apps/fullstack/apps/{{projectName}}-web/svelte.config.js.hbs +4 -1
- package/templates/apps/fullstack/apps/{{projectName}}-web/vite.config.ts.hbs +19 -5
- package/templates/apps/fullstack/docs/security-configuration.md.hbs +4 -1
- package/templates/apps/fullstack/e2e/fullstack.spec.ts.hbs +140 -28
- package/templates/apps/fullstack/e2e/session-close.spec.ts.hbs +19 -5
- package/templates/apps/fullstack/eslint.config.js.hbs +3 -1
- package/templates/apps/fullstack/images/serv.Dockerfile.hbs +19 -11
- package/templates/apps/fullstack/images/web.Dockerfile.hbs +17 -8
- package/templates/apps/fullstack/package.json.hbs +2 -1
- package/templates/apps/fullstack/packages/{{projectName}}-contract/README.md.hbs +7 -5
- package/templates/apps/fullstack/packages/{{projectName}}-contract/src/admin-contract.ts.hbs +114 -23
- package/templates/apps/fullstack/packages/{{projectName}}-contract/src/admin-schemas.ts.hbs +57 -11
- package/templates/apps/fullstack/packages/{{projectName}}-contract/tsup.config.ts.hbs +3 -1
- package/templates/apps/fullstack/packages/{{projectName}}-serv/README.md.hbs +17 -7
- package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_reach.yml.hbs +13 -7
- package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_serv.yml.hbs +17 -7
- package/templates/apps/fullstack/packages/{{projectName}}-serv/package.json.hbs +10 -4
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/app-server.ts.hbs +65 -30
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/build-metadata.ts.hbs +0 -1
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/index.ts.hbs +1 -2
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/admin-service.ts.hbs +69 -21
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/business-settings.ts.hbs +21 -5
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/procedures/admin-procedures.ts.hbs +63 -11
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/procedures/app-procedures.ts.hbs +6 -1
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/procedures/index.ts.hbs +3 -1
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/runtime.ts.hbs +147 -33
- package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/security-roles.ts.hbs +43 -12
- package/templates/apps/fullstack/packages/{{projectName}}-serv/tests/app-server.test.ts.hbs +146 -30
- package/templates/apps/fullstack/packages/{{projectName}}-serv/tests/runtime.test.ts.hbs +33 -10
- package/templates/apps/fullstack/packages/{{projectName}}-serv/tests/security.test.ts.hbs +142 -26
- package/templates/apps/fullstack/packages/{{projectName}}-serv/tsup.config.ts.hbs +6 -2
- package/templates/apps/fullstack/packages/{{projectName}}-serv/vitest.config.ts.hbs +3 -1
- package/templates/apps/fullstack/packages/{{projectName}}-shared/README.md.hbs +48 -66
- package/templates/apps/fullstack/packages/{{projectName}}-shared/messages/en-US.json.hbs +1 -0
- package/templates/apps/fullstack/packages/{{projectName}}-shared/messages/ja-JP.json.hbs +1 -0
- package/templates/apps/fullstack/packages/{{projectName}}-shared/messages/zh-CN.json.hbs +1 -0
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/api/api-client.ts.hbs +9 -2
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/api/feedback-client.ts.hbs +0 -3
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/api/sensitive-actions.svelte.ts.hbs +15 -3
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/AppShell.svelte.hbs +33 -7
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/FeedbackEntry.svelte.hbs +16 -2
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/audit.svelte.hbs +104 -13
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/feedback.svelte.hbs +37 -7
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/roles.svelte.hbs +9 -3
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/settings.svelte.hbs +25 -3
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/users.svelte.hbs +133 -19
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/security/role-label.ts.hbs +14 -2
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/stores/guide-store.ts.hbs +66 -34
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/stores/management-store.svelte.ts.hbs +132 -30
- package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/stores/theme-store.svelte.ts.hbs +8 -2
- package/templates/apps/fullstack/packages/{{projectName}}-shared/tests/api-client.test.ts.hbs +23 -5
- package/templates/apps/fullstack/packages/{{projectName}}-shared/tests/management-store.test.ts.hbs +143 -21
- package/templates/apps/fullstack/packages/{{projectName}}-shared/vitest.config.ts.hbs +7 -2
- package/templates/apps/fullstack/playwright.config.ts.hbs +25 -3
- package/templates/apps/fullstack/pnpm-workspace.yaml.hbs +30 -14
- package/templates/apps/h5/src/routes/+layout.svelte.hbs +74 -30
- package/templates/apps/h5/src/routes/+page.svelte.hbs +2 -2
- package/templates/apps/h5/src/routes/discover/+page.svelte.hbs +2 -2
- package/templates/apps/h5/src/routes/profile/+page.svelte.hbs +2 -2
- package/templates/apps/mobile-app/capacitor.config.ts.hbs +5 -2
- package/templates/apps/mobile-app/messages/en-US.json +2 -2
- package/templates/apps/mobile-app/messages/ja-JP.json +2 -2
- package/templates/apps/mobile-app/messages/zh-CN.json +2 -2
- package/templates/apps/mobile-app/src/App.svelte.hbs +17 -5
- package/templates/apps/mobile-app/src/lib/api.ts.hbs +20 -6
- package/templates/apps/mobile-app/src/lib/app-config.ts.hbs +8 -1
- package/templates/apps/mobile-app/src/lib/pages/home.svelte.hbs +6 -2
- package/templates/apps/mobile-app/src/lib/pages/profile.svelte.hbs +13 -1
- package/templates/apps/mobile-app/src/lib/pages/tasks.svelte.hbs +5 -1
- package/templates/apps/website/scripts/static-website.mjs.hbs +19 -6
- package/templates/apps/website/src/routes/+layout.svelte.hbs +56 -29
- package/templates/apps/website/src/routes/+page.svelte.hbs +4 -4
- package/templates/apps/website/src/routes/about/+page.svelte.hbs +2 -2
- package/templates/apps/website/src/routes/contact/+page.svelte.hbs +2 -2
- package/templates/apps/website/src/routes/services/+page.svelte.hbs +20 -5
- package/templates/base/.dockerignore.hbs +10 -0
- package/templates/base/.gitignore.hbs +7 -2
- package/templates/base/e2e/app.spec.ts.hbs +130 -36
- package/templates/base/eslint-rules.js +44 -25
- package/templates/base/eslint.config.js.hbs +3 -1
- package/templates/base/images/web.Dockerfile.hbs +25 -14
- package/templates/base/package.json.hbs +13 -7
- package/templates/base/patches/daisyui-5.7.50.patch +8 -0
- package/templates/base/patches/deepmerge-4.3.1.patch +51 -0
- package/templates/base/playwright.config.ts +3 -1
- package/templates/base/pnpm-workspace.yaml.hbs +33 -16
- package/templates/base/src/app.d.ts.hbs +1 -1
- package/templates/base/src/app.html.hbs +1 -0
- package/templates/base/src/hooks.server.ts.hbs +44 -33
- package/templates/base/src/lib/build-metadata.ts.hbs +0 -1
- package/templates/base/src/lib/server/init.ts.hbs +146 -35
- package/templates/base/src/lib/server/kit-config.ts.hbs +7 -4
- package/templates/base/src/routes/+layout.server.ts.hbs +2 -2
- package/templates/base/svelte.config.js.hbs +10 -100
- package/templates/base/tests/scaffold.test.ts.hbs +12 -3
- package/templates/base/tsconfig.json.hbs +7 -4
- package/templates/base/vite.config.ts.hbs +108 -14
- package/templates/base/vitest.config.ts.hbs +6 -2
- package/templates/features/ai/routes/api/chat/+server.ts +3 -1
- package/templates/features/iam/routes-admin/(auth)/auth/forgot-password/+page.svelte.hbs +3 -3
- package/templates/features/iam/routes-admin/(auth)/auth/login/+page.svelte.hbs +3 -3
- package/templates/features/iam/routes-admin/(auth)/auth/register/+page.svelte.hbs +3 -3
- package/templates/features/iam/routes-admin/(auth)/auth/reset-password/+page.svelte.hbs +3 -3
- package/templates/features/iam/routes-h5/auth/forgot-password/+page.svelte.hbs +3 -3
- package/templates/features/iam/routes-h5/auth/login/+page.svelte.hbs +3 -3
- package/templates/features/iam/routes-h5/auth/register/+page.svelte.hbs +3 -3
- package/templates/features/iam/routes-h5/auth/reset-password/+page.svelte.hbs +3 -3
- package/templates/features/iam/routes-website/auth/forgot-password/+page.svelte.hbs +3 -3
- package/templates/features/iam/routes-website/auth/login/+page.svelte.hbs +3 -3
- package/templates/features/iam/routes-website/auth/register/+page.svelte.hbs +3 -3
- package/templates/features/iam/routes-website/auth/reset-password/+page.svelte.hbs +3 -3
- package/templates/features/payment/routes/api/v1/payment/create/+server.ts +14 -3
- package/templates/shared/browser-check/startup.js +115 -0
- package/templates/shared/browser-check/update.css +37 -0
- package/templates/shared/browser-check/update.html +19 -0
- package/templates/shared/browser-startup.spec.ts +23 -0
- package/templates/shared/build-metadata.ts +18 -4
- package/templates/shared/e2e-env.ts +22 -6
- package/templates/shared/native-name.d.mts +5 -0
- package/templates/shared/native-name.mjs +94 -0
- package/templates/shared/public-base-path.mjs +17 -4
- package/templates/shared/run-e2e.mjs +46 -12
- package/templates/shared/run-node.mjs +56 -0
- package/templates/shared/run-tauri.mjs +74 -0
- package/templates/shared/vite-runtime-config.ts +9 -3
- package/templates/skills/AGENTS.md +10 -73
- package/templates/skills/bridges/admin/AGENTS.md +9 -65
- package/templates/skills/bridges/api/AGENTS.md +8 -59
- package/templates/skills/bridges/fullstack/AGENTS.md.hbs +9 -54
- package/templates/skills/bridges/h5/AGENTS.md +8 -63
- package/templates/skills/bridges/mobile-app/AGENTS.md +8 -62
- package/templates/skills/bridges/website/AGENTS.md +8 -63
- package/templates/skills/hai-ai/SKILL.md +6 -2
- package/templates/skills/hai-ai/acp.md +153 -0
- package/templates/skills/hai-ai/guide.md +27 -12
- package/templates/skills/hai-ai/reference.md +24 -15
- package/templates/skills/hai-ai/resources.md +69 -0
- package/templates/skills/hai-api-client/SKILL.md +1 -1
- package/templates/skills/hai-api-client/reference.md +3 -1
- package/templates/skills/hai-app-create/collaboration.md +7 -0
- package/templates/skills/hai-app-create/development.md +19 -10
- package/templates/skills/hai-app-create/project.md +33 -0
- package/templates/skills/hai-app-create/reference.md +10 -10
- package/templates/skills/hai-app-tests/reference.md +1 -1
- package/templates/skills/hai-app-tests/validation.md +1 -0
- package/templates/skills/hai-build/SKILL.md +19 -9
- package/templates/skills/hai-build/paths.md +4 -2
- package/templates/skills/hai-build/reference.md +26 -8
- package/templates/skills/hai-cache/reference.md +6 -2
- package/templates/skills/hai-capacitor/SKILL.md +1 -0
- package/templates/skills/hai-capacitor/reference.md +24 -0
- package/templates/skills/hai-ci/SKILL.md +3 -0
- package/templates/skills/hai-core/SKILL.md +9 -4
- package/templates/skills/hai-core/reference.md +40 -10
- package/templates/skills/hai-core/request-access.md +2 -2
- package/templates/skills/hai-crypto/SKILL.md +2 -2
- package/templates/skills/hai-crypto/reference.md +7 -3
- package/templates/skills/hai-deploy/SKILL.md +41 -5
- package/templates/skills/hai-deploy/middleware.md +26 -14
- package/templates/skills/hai-framework-sync/SKILL.md +13 -9
- package/templates/skills/hai-framework-sync/sync.md +2 -0
- package/templates/skills/hai-iam/reference.md +2 -2
- package/templates/skills/hai-kit/SKILL.md +1 -1
- package/templates/skills/hai-kit/reference.md +19 -5
- package/templates/skills/hai-payment/reference.md +41 -17
- package/templates/skills/hai-reach/SKILL.md +3 -1
- package/templates/skills/hai-reach/reference.md +29 -9
- package/templates/skills/hai-reldb/SKILL.md +1 -1
- package/templates/skills/hai-reldb/reference.md +9 -2
- package/templates/skills/hai-scheduler/reference.md +1 -1
- package/templates/skills/hai-serv/SKILL.md +2 -1
- package/templates/skills/hai-serv/reference.md +26 -15
- package/templates/skills/hai-storage/reference.md +23 -6
- package/templates/skills/hai-ui/SKILL.md +8 -5
- package/templates/skills/hai-ui/charts.md +51 -0
- package/templates/skills/hai-ui/guide.md +1 -1
- package/templates/skills/hai-ui/reference.md +29 -38
- package/templates/skills/hai-vecdb/reference.md +8 -3
- package/dist/chunk-AKV2CQZV.js.map +0 -1
- package/dist/chunk-EF7WOFWZ.js.map +0 -1
- package/dist/chunk-MUHRBJSW.js +0 -29272
- package/dist/chunk-MUHRBJSW.js.map +0 -1
- package/dist/cli-config-templates-HHFHNXBN.js +0 -5
- package/dist/src-TEBHR5I5.js +0 -5
- package/dist/src-TEBHR5I5.js.map +0 -1
- package/templates/apps/fullstack/playwright.session-close.config.ts.hbs +0 -18
|
@@ -7,8 +7,7 @@ import { createServerApp } from './app-server.js'
|
|
|
7
7
|
import { closeModules, initializeModules } from './server/runtime.js'
|
|
8
8
|
|
|
9
9
|
async function startServer(): Promise<void> {
|
|
10
|
-
//
|
|
11
|
-
core.init({ configDir: './config' })
|
|
10
|
+
// 运行时先注册模块 Schema 并加载配置,再按依赖顺序初始化;日志设置沿用 _core.yml。
|
|
12
11
|
await initializeModules()
|
|
13
12
|
try {
|
|
14
13
|
// HOST/PORT 环境变量优先于 config/_serv.yml。
|
package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/admin-service.ts.hbs
CHANGED
|
@@ -11,8 +11,18 @@ import { storage } from '@h-ai/storage'
|
|
|
11
11
|
import { SUPPORTED_LOCALES } from '{{fullstack.contractPackageName}}'
|
|
12
12
|
import { serverM } from './messages.js'
|
|
13
13
|
|
|
14
|
-
const ROLE_CODES = new Set<AdminRoleCode>([
|
|
15
|
-
|
|
14
|
+
const ROLE_CODES = new Set<AdminRoleCode>([
|
|
15
|
+
'admin',
|
|
16
|
+
'audit_admin',
|
|
17
|
+
'config_admin',
|
|
18
|
+
'business_operator',
|
|
19
|
+
'user',
|
|
20
|
+
])
|
|
21
|
+
const SUPPORTED_THEMES = [
|
|
22
|
+
{{#each fullstack.theme.themes}}
|
|
23
|
+
'{{this}}',
|
|
24
|
+
{{/each}}
|
|
25
|
+
] as readonly string[]
|
|
16
26
|
let adminMutationActive = false
|
|
17
27
|
|
|
18
28
|
/** 管理写操作串行化,锁内重新鉴权,防止两个管理员同时互相删除或降级。 */
|
|
@@ -42,7 +52,10 @@ export async function withAdminMutation<T>(context: ServContext, operation: stri
|
|
|
42
52
|
void cache.lock.extend(key, 300, owner).then((renewed) => {
|
|
43
53
|
if (!renewed.success || !renewed.data) {
|
|
44
54
|
leaseLost = true
|
|
45
|
-
core.logger.error('Admin mutation lease renewal failed', {
|
|
55
|
+
core.logger.error('Admin mutation lease renewal failed', {
|
|
56
|
+
owner,
|
|
57
|
+
error: renewed.success ? 'lease_not_owned' : renewed.error.code,
|
|
58
|
+
})
|
|
46
59
|
}
|
|
47
60
|
}).catch(() => {
|
|
48
61
|
leaseLost = true
|
|
@@ -55,7 +68,9 @@ export async function withAdminMutation<T>(context: ServContext, operation: stri
|
|
|
55
68
|
const actor = await getAdminUser(context.session?.userId ?? '')
|
|
56
69
|
if (!actor.success)
|
|
57
70
|
return actor
|
|
58
|
-
const permitted = await iam.authz.checkPermission(actor.data.id, operation.startsWith('iam.roles.')
|
|
71
|
+
const permitted = await iam.authz.checkPermission(actor.data.id, operation.startsWith('iam.roles.')
|
|
72
|
+
? 'app.roles.manage'
|
|
73
|
+
: 'app.users.manage')
|
|
59
74
|
if (!permitted.success)
|
|
60
75
|
return permitted
|
|
61
76
|
if (actor.data.accountStatus !== 'active' || (actor.data.accountType === 'temporary' && (!actor.data.accountExpiresAt || new Date(actor.data.accountExpiresAt).getTime() <= Date.now())) || !permitted.data)
|
|
@@ -73,8 +88,12 @@ export async function withAdminMutation<T>(context: ServContext, operation: stri
|
|
|
73
88
|
try {
|
|
74
89
|
if (acquired) {
|
|
75
90
|
const released = await cache.lock.release(key, owner)
|
|
76
|
-
if (!released.success || !released.data)
|
|
77
|
-
core.logger.error('Failed to release admin mutation lock', {
|
|
91
|
+
if (!released.success || !released.data) {
|
|
92
|
+
core.logger.error('Failed to release admin mutation lock', {
|
|
93
|
+
owner,
|
|
94
|
+
error: released.success ? 'lease_not_owned' : released.error.code,
|
|
95
|
+
})
|
|
96
|
+
}
|
|
78
97
|
}
|
|
79
98
|
}
|
|
80
99
|
finally {
|
|
@@ -123,7 +142,12 @@ export async function createAdminUser(input: AdminCreateUser, context: ServConte
|
|
|
123
142
|
return role
|
|
124
143
|
if (!role.data)
|
|
125
144
|
return err(HaiCommonError.NOT_FOUND, serverM('notFound', { locale: context.locale }))
|
|
126
|
-
const registered = await iam.user.createUser({
|
|
145
|
+
const registered = await iam.user.createUser({
|
|
146
|
+
...input,
|
|
147
|
+
accountExpiresAt: input.accountExpiresAt
|
|
148
|
+
? new Date(input.accountExpiresAt)
|
|
149
|
+
: undefined,
|
|
150
|
+
})
|
|
127
151
|
if (!registered.success)
|
|
128
152
|
return registered
|
|
129
153
|
const userId = registered.data.user.id
|
|
@@ -139,9 +163,17 @@ export async function createAdminUser(input: AdminCreateUser, context: ServConte
|
|
|
139
163
|
/** 安全通知发送后发生未知结果时保留账号,不自动重试或删除已发出的邀请。 */
|
|
140
164
|
export async function inviteAdminUser(input: AdminInviteUser, context: ServContext): Promise<HaiResult<AdminUser>> {
|
|
141
165
|
const role = await iam.authz.getRoleByCode(input.role)
|
|
142
|
-
if (!role.success || !role.data)
|
|
143
|
-
return role.success
|
|
144
|
-
|
|
166
|
+
if (!role.success || !role.data) {
|
|
167
|
+
return role.success
|
|
168
|
+
? err(HaiCommonError.NOT_FOUND, serverM('notFound', { locale: context.locale }))
|
|
169
|
+
: role
|
|
170
|
+
}
|
|
171
|
+
const created = await iam.user.inviteUser({
|
|
172
|
+
...input,
|
|
173
|
+
accountExpiresAt: input.accountExpiresAt
|
|
174
|
+
? new Date(input.accountExpiresAt)
|
|
175
|
+
: undefined,
|
|
176
|
+
})
|
|
145
177
|
if (!created.success)
|
|
146
178
|
return created
|
|
147
179
|
const assigned = await iam.authz.syncRoles(created.data.user.id, [role.data.id])
|
|
@@ -165,7 +197,9 @@ export async function updateAdminUser(input: AdminUpdateUser, context: ServConte
|
|
|
165
197
|
return existing
|
|
166
198
|
if (!existing.data)
|
|
167
199
|
return err(HaiCommonError.NOT_FOUND, serverM('notFound', { locale: context.locale }))
|
|
168
|
-
const role = input.role
|
|
200
|
+
const role = input.role
|
|
201
|
+
? await iam.authz.getRoleByCode(input.role)
|
|
202
|
+
: undefined
|
|
169
203
|
if (role && !role.success)
|
|
170
204
|
return role
|
|
171
205
|
if (role?.success && !role.data)
|
|
@@ -179,7 +213,14 @@ export async function updateAdminUser(input: AdminUpdateUser, context: ServConte
|
|
|
179
213
|
if (!assigned.success)
|
|
180
214
|
return assigned
|
|
181
215
|
}
|
|
182
|
-
const updated = await iam.user.updateUser(id, {
|
|
216
|
+
const updated = await iam.user.updateUser(id, {
|
|
217
|
+
...profile,
|
|
218
|
+
accountExpiresAt: profile.accountExpiresAt === undefined
|
|
219
|
+
? undefined
|
|
220
|
+
: profile.accountExpiresAt
|
|
221
|
+
? new Date(profile.accountExpiresAt)
|
|
222
|
+
: null,
|
|
223
|
+
})
|
|
183
224
|
if (!updated.success) {
|
|
184
225
|
// 角色 ID 可精确还原,避免 undefined 资料字段被 updateUser 忽略后无法补偿。
|
|
185
226
|
if (role?.success && role.data) {
|
|
@@ -189,7 +230,10 @@ export async function updateAdminUser(input: AdminUpdateUser, context: ServConte
|
|
|
189
230
|
}
|
|
190
231
|
return updated
|
|
191
232
|
}
|
|
192
|
-
await recordAudit(context, 'update', 'users', id, {
|
|
233
|
+
await recordAudit(context, 'update', 'users', id, {
|
|
234
|
+
fields: Object.keys(profile),
|
|
235
|
+
role: input.role,
|
|
236
|
+
})
|
|
193
237
|
return getAdminUser(id)
|
|
194
238
|
}
|
|
195
239
|
|
|
@@ -201,13 +245,15 @@ export async function getAdminRoles(): Promise<HaiResult<{ items: AdminRole[] }>
|
|
|
201
245
|
const counts = await iam.authz.getRoleUserCounts(items.map(role => role.id))
|
|
202
246
|
if (!counts.success)
|
|
203
247
|
return counts
|
|
204
|
-
return ok({
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
248
|
+
return ok({
|
|
249
|
+
items: items.map(role => ({
|
|
250
|
+
id: role.id,
|
|
251
|
+
code: role.code as AdminRoleCode,
|
|
252
|
+
name: role.name,
|
|
253
|
+
description: role.description,
|
|
254
|
+
userCount: counts.data.get(role.id) ?? 0,
|
|
255
|
+
})),
|
|
256
|
+
})
|
|
211
257
|
}
|
|
212
258
|
|
|
213
259
|
export async function updateProfile(input: ProfileUpdate, context: ServContext): Promise<HaiResult<AdminUser>> {
|
|
@@ -225,7 +271,9 @@ export async function updateProfile(input: ProfileUpdate, context: ServContext):
|
|
|
225
271
|
const updated = await iam.user.updateCurrentUser(token, { displayName: input.displayName, email: input.email, metadata })
|
|
226
272
|
if (!updated.success)
|
|
227
273
|
return updated
|
|
228
|
-
await recordAudit(context, 'update', 'profile', current.data.id, {
|
|
274
|
+
await recordAudit(context, 'update', 'profile', current.data.id, {
|
|
275
|
+
fields: Object.keys(input),
|
|
276
|
+
})
|
|
229
277
|
return getAdminUser(current.data.id)
|
|
230
278
|
}
|
|
231
279
|
|
package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/business-settings.ts.hbs
CHANGED
|
@@ -5,15 +5,22 @@ import { err, HaiCommonError, ok } from '@h-ai/core'
|
|
|
5
5
|
import { reldb } from '@h-ai/reldb'
|
|
6
6
|
import { z } from 'zod'
|
|
7
7
|
|
|
8
|
-
export const BusinessSettingsSchema = z.object({
|
|
8
|
+
export const BusinessSettingsSchema = z.object({
|
|
9
|
+
notice: z.string().trim().max(200),
|
|
10
|
+
}).strict()
|
|
9
11
|
|
|
10
12
|
export function initBusinessSettings(): Promise<HaiResult<void>> {
|
|
11
|
-
return reldb.ddl.createTable('app_business_settings', {
|
|
13
|
+
return reldb.ddl.createTable('app_business_settings', {
|
|
14
|
+
id: { type: 'TEXT', primaryKey: true },
|
|
15
|
+
notice: { type: 'TEXT', notNull: true },
|
|
16
|
+
})
|
|
12
17
|
}
|
|
13
18
|
|
|
14
19
|
export async function readBusinessSettings(): Promise<HaiResult<{ notice: string }>> {
|
|
15
20
|
const row = await reldb.sql.get<{ notice: string }>('SELECT notice FROM app_business_settings WHERE id = ?', ['public'])
|
|
16
|
-
return row.success
|
|
21
|
+
return row.success
|
|
22
|
+
? ok({ notice: row.data?.notice ?? '' })
|
|
23
|
+
: row
|
|
17
24
|
}
|
|
18
25
|
|
|
19
26
|
export async function saveBusinessSettings(input: { notice: string }): Promise<HaiResult<{ notice: string }>> {
|
|
@@ -33,7 +40,14 @@ export async function saveBusinessSettings(input: { notice: string }): Promise<H
|
|
|
33
40
|
await tx.rollback()
|
|
34
41
|
return saved
|
|
35
42
|
}
|
|
36
|
-
const logged = await audit.capture({
|
|
43
|
+
const logged = await audit.capture({
|
|
44
|
+
action: 'business.configuration.update',
|
|
45
|
+
resource: 'business-settings',
|
|
46
|
+
category: 'business',
|
|
47
|
+
details: {
|
|
48
|
+
fields: ['notice'],
|
|
49
|
+
},
|
|
50
|
+
}, { tx })
|
|
37
51
|
if (!logged.success) {
|
|
38
52
|
await tx.rollback()
|
|
39
53
|
return logged
|
|
@@ -41,7 +55,9 @@ export async function saveBusinessSettings(input: { notice: string }): Promise<H
|
|
|
41
55
|
const committed = await tx.commit()
|
|
42
56
|
if (!committed.success)
|
|
43
57
|
await tx.rollback()
|
|
44
|
-
return committed.success
|
|
58
|
+
return committed.success
|
|
59
|
+
? ok(parsed.data)
|
|
60
|
+
: committed
|
|
45
61
|
}
|
|
46
62
|
catch (error) {
|
|
47
63
|
await tx.rollback()
|
|
@@ -28,13 +28,27 @@ export function createAuthProcedures() {
|
|
|
28
28
|
export function createAdminProcedures() {
|
|
29
29
|
return serv.implement(adminContract).context<ServContext>().route('permissions.list').permission('app.roles.manage').handle(async ({ input }) => {
|
|
30
30
|
const result = await iam.authz.getAllPermissions(input)
|
|
31
|
-
return result.success
|
|
31
|
+
return result.success
|
|
32
|
+
? ok({
|
|
33
|
+
total: result.data.total,
|
|
34
|
+
items: result.data.items.map(item => ({
|
|
35
|
+
id: item.id,
|
|
36
|
+
code: item.code,
|
|
37
|
+
name: item.name,
|
|
38
|
+
sensitiveOperations: item.sensitiveOperations ?? [],
|
|
39
|
+
})),
|
|
40
|
+
})
|
|
41
|
+
: result
|
|
32
42
|
}).route('permissions.update').permission('app.roles.manage').handle(({ input, context }) => withAdminMutation(context, 'iam.permissions.sensitiveOperations', async () => {
|
|
33
43
|
const result = await iam.authz.setPermissionSensitiveOperations(input.id, input.sensitiveOperations)
|
|
34
|
-
return result.success
|
|
44
|
+
return result.success
|
|
45
|
+
? ok({ saved: true as const })
|
|
46
|
+
: result
|
|
35
47
|
})).route('configuration.get').permission('app.config.manage').handle(() => readBusinessSettings()).route('configuration.update').permission('app.config.manage').handle(async ({ input, context }) => {
|
|
36
48
|
const checked = await iam.auth.checkSensitiveOperation(context.accessToken ?? '', 'business.settings.update', context.request.headers.get('x-hai-reauthentication') ?? undefined)
|
|
37
|
-
return checked.success
|
|
49
|
+
return checked.success
|
|
50
|
+
? saveBusinessSettings(input)
|
|
51
|
+
: checked
|
|
38
52
|
}).route('users.list').permission('app.users.manage').handle(async ({ input }) => {
|
|
39
53
|
const users = await iam.user.listUsers(input)
|
|
40
54
|
if (!users.success)
|
|
@@ -42,7 +56,10 @@ export function createAdminProcedures() {
|
|
|
42
56
|
const roles = await iam.authz.getUserRolesForMany(users.data.items.map(user => user.id))
|
|
43
57
|
if (!roles.success)
|
|
44
58
|
return roles
|
|
45
|
-
return ok({
|
|
59
|
+
return ok({
|
|
60
|
+
...users.data,
|
|
61
|
+
items: users.data.items.map(user => toAdminUser(user, roles.data.get(user.id) ?? [])),
|
|
62
|
+
})
|
|
46
63
|
}).route('users.resetPassword').permission('app.users.manage').handle(({ input, context }) => withAdminMutation(context, 'iam.users.resetPassword', async () => {
|
|
47
64
|
const result = await iam.user.adminResetPassword(input.id, input.newPassword)
|
|
48
65
|
if (!result.success)
|
|
@@ -74,33 +91,68 @@ export function createAdminProcedures() {
|
|
|
74
91
|
const result = roles.data.items.find(item => item.id === input.id)
|
|
75
92
|
if (!result)
|
|
76
93
|
return err(HaiCommonError.NOT_FOUND, serverM('notFound', { locale: context.locale }))
|
|
77
|
-
await recordAudit(context, 'update', 'roles', input.id, {
|
|
94
|
+
await recordAudit(context, 'update', 'roles', input.id, {
|
|
95
|
+
fields: ['description'],
|
|
96
|
+
})
|
|
78
97
|
return ok(result)
|
|
79
98
|
})).route('audit.backup').permission('audit:backup').handle(async ({ input, context }) => {
|
|
80
99
|
const checked = await iam.auth.checkSensitiveOperation(context.accessToken ?? '', 'audit.backup', context.request.headers.get('x-hai-reauthentication') ?? undefined)
|
|
81
100
|
if (!checked.success)
|
|
82
101
|
return checked
|
|
83
102
|
const result = await audit.backup.run({ ...input, signal: context.request.signal })
|
|
84
|
-
return result.success
|
|
103
|
+
return result.success
|
|
104
|
+
? ok({
|
|
105
|
+
...result.data,
|
|
106
|
+
createdAt: result.data.createdAt.toISOString(),
|
|
107
|
+
cutoff: result.data.cutoff.toISOString(),
|
|
108
|
+
})
|
|
109
|
+
: result
|
|
85
110
|
}).route('audit.list').permission('audit:read').handle(async ({ input, context }) => {
|
|
86
111
|
if (input.startDate && input.endDate && input.startDate > input.endDate)
|
|
87
112
|
return err(HaiCommonError.VALIDATION_ERROR, serverM('invalidDateRange', { locale: context.locale }))
|
|
88
|
-
const result = await audit.list({
|
|
113
|
+
const result = await audit.list({
|
|
114
|
+
...input,
|
|
115
|
+
startDate: input.startDate
|
|
116
|
+
? new Date(input.startDate)
|
|
117
|
+
: undefined,
|
|
118
|
+
endDate: input.endDate
|
|
119
|
+
? new Date(input.endDate)
|
|
120
|
+
: undefined,
|
|
121
|
+
})
|
|
89
122
|
if (!result.success)
|
|
90
123
|
return result
|
|
91
|
-
const stats = await audit.getStats(7, {
|
|
124
|
+
const stats = await audit.getStats(7, {
|
|
125
|
+
category: input.category,
|
|
126
|
+
outcome: input.outcome,
|
|
127
|
+
severity: input.severity,
|
|
128
|
+
resource: input.resource,
|
|
129
|
+
scopeId: input.scopeId,
|
|
130
|
+
userId: input.userId,
|
|
131
|
+
})
|
|
92
132
|
if (!stats.success)
|
|
93
133
|
return stats
|
|
94
|
-
return ok({
|
|
134
|
+
return ok({
|
|
135
|
+
...result.data,
|
|
136
|
+
stats: stats.data,
|
|
137
|
+
backupEnabled: AuditInitConfigSchema.parse(core.config.get('audit') ?? {}).backup.enabled,
|
|
138
|
+
items: result.data.items.map(item => ({
|
|
139
|
+
...item,
|
|
140
|
+
createdAt: new Date(item.createdAt).toISOString(),
|
|
141
|
+
})),
|
|
142
|
+
})
|
|
95
143
|
}).build()
|
|
96
144
|
}
|
|
97
145
|
|
|
98
146
|
export function createProfileProcedures() {
|
|
99
147
|
return serv.implement(profileContract).context<ServContext>().route('update').auth().handle(async ({ input, context }) => {
|
|
100
148
|
const checked = await iam.auth.checkSensitiveOperation(context.accessToken ?? '', 'iam.profile.update', context.request.headers.get('x-hai-reauthentication') ?? undefined)
|
|
101
|
-
return checked.success
|
|
149
|
+
return checked.success
|
|
150
|
+
? updateProfile(input, context)
|
|
151
|
+
: checked
|
|
102
152
|
}).route('avatar').auth().handle(async ({ input, context }) => {
|
|
103
153
|
const checked = await iam.auth.checkSensitiveOperation(context.accessToken ?? '', 'iam.profile.avatar', context.request.headers.get('x-hai-reauthentication') ?? undefined)
|
|
104
|
-
return checked.success
|
|
154
|
+
return checked.success
|
|
155
|
+
? uploadAvatar(context.session.userId, input, context)
|
|
156
|
+
: checked
|
|
105
157
|
}).build()
|
|
106
158
|
}
|
|
@@ -22,7 +22,12 @@ export function createAppProcedures(options: CreateAppProceduresOptions) {
|
|
|
22
22
|
.context<ServContext>()
|
|
23
23
|
.route('info', async () => {
|
|
24
24
|
const settings = await readBusinessSettings()
|
|
25
|
-
return settings.success
|
|
25
|
+
return settings.success
|
|
26
|
+
? ok({
|
|
27
|
+
...createAppInfo(options),
|
|
28
|
+
notice: settings.data.notice,
|
|
29
|
+
})
|
|
30
|
+
: settings
|
|
26
31
|
})
|
|
27
32
|
.route('echo', ({ input, context }) => {
|
|
28
33
|
const data: AppEchoOutputData = {
|
package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/procedures/index.ts.hbs
CHANGED
|
@@ -17,7 +17,9 @@ export function createServerProcedures(options: CreateServerProceduresOptions) {
|
|
|
17
17
|
return {
|
|
18
18
|
// @hai-generator:procedures:entries
|
|
19
19
|
app: createAppProcedures(options.app),
|
|
20
|
-
iam: {
|
|
20
|
+
iam: {
|
|
21
|
+
auth: createAuthProcedures(),
|
|
22
|
+
},
|
|
21
23
|
admin: createAdminProcedures(),
|
|
22
24
|
profile: createProfileProcedures(),
|
|
23
25
|
{{#if features.feedback}}
|
|
@@ -10,12 +10,12 @@ import { cache, CacheConfigSchema } from '@h-ai/cache'
|
|
|
10
10
|
import { core, HaiConfigError } from '@h-ai/core'
|
|
11
11
|
import { crypto } from '@h-ai/crypto'
|
|
12
12
|
{{#if features.feedback}}
|
|
13
|
-
import { feedback } from '@h-ai/feedback'
|
|
13
|
+
import { feedback, FeedbackConfigSchema } from '@h-ai/feedback'
|
|
14
14
|
{{/if}}
|
|
15
15
|
import { iam, IamConfigSchema } from '@h-ai/iam'
|
|
16
16
|
import { reach, ReachConfigSchema } from '@h-ai/reach'
|
|
17
17
|
import { reldb, ReldbConfigSchema } from '@h-ai/reldb'
|
|
18
|
-
import { scheduler } from '@h-ai/scheduler'
|
|
18
|
+
import { scheduler, SchedulerConfigSchema } from '@h-ai/scheduler'
|
|
19
19
|
import { ServConfigSchema } from '@h-ai/serv'
|
|
20
20
|
import { storage, StorageConfigSchema } from '@h-ai/storage'
|
|
21
21
|
{{#if features.vecdb}}
|
|
@@ -42,7 +42,10 @@ function startFeedback(): Promise<void> {
|
|
|
42
42
|
feedbackClosing = false
|
|
43
43
|
feedbackStarting = (async () => {
|
|
44
44
|
try {
|
|
45
|
-
const result = await feedback.init({
|
|
45
|
+
const result = await feedback.init({
|
|
46
|
+
...core.config.getOrThrow<import('@h-ai/feedback').FeedbackConfigInput>('feedback'),
|
|
47
|
+
...buildMetadata,
|
|
48
|
+
})
|
|
46
49
|
if (!result.success) {
|
|
47
50
|
if (feedbackFailure !== result.error.code)
|
|
48
51
|
core.logger.warn('Optional feedback unavailable; business runtime remains ready', { module: 'feedback', code: result.error.code })
|
|
@@ -76,38 +79,127 @@ async function closeFeedback() {
|
|
|
76
79
|
await feedbackStarting
|
|
77
80
|
return feedback.close()
|
|
78
81
|
}
|
|
79
|
-
|
|
80
82
|
{{/if}}
|
|
81
83
|
const runtime = core.module.createRuntime([
|
|
82
|
-
{
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
84
|
+
{
|
|
85
|
+
name: 'configuration',
|
|
86
|
+
start: () => {
|
|
87
|
+
core.init({
|
|
88
|
+
configDir: './config',
|
|
89
|
+
configSchemas: {
|
|
90
|
+
{{#if features.ai}}
|
|
91
|
+
ai: AIConfigSchema,
|
|
92
|
+
{{/if}}
|
|
93
|
+
audit: AuditInitConfigSchema,
|
|
94
|
+
cache: CacheConfigSchema,
|
|
95
|
+
db: ReldbConfigSchema,
|
|
96
|
+
{{#if features.feedback}}
|
|
97
|
+
feedback: FeedbackConfigSchema,
|
|
98
|
+
{{/if}}
|
|
99
|
+
iam: IamConfigSchema,
|
|
100
|
+
reach: ReachConfigSchema,
|
|
101
|
+
scheduler: SchedulerConfigSchema,
|
|
102
|
+
serv: ServConfigSchema,
|
|
103
|
+
storage: StorageConfigSchema,
|
|
90
104
|
{{#if features.vecdb}}
|
|
91
|
-
|
|
105
|
+
vecdb: VecdbConfigSchema,
|
|
92
106
|
{{/if}}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
{
|
|
107
|
+
},
|
|
108
|
+
})
|
|
109
|
+
},
|
|
110
|
+
close: () => { },
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
name: 'serv-config',
|
|
114
|
+
start: () => core.config.validate('serv', ServConfigSchema),
|
|
115
|
+
close: () => { },
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
name: 'reldb',
|
|
119
|
+
start: async () => {
|
|
120
|
+
const config = ReldbConfigSchema.parse(core.config.getOrThrow('db'))
|
|
121
|
+
if (config.type === 'sqlite' && config.database !== ':memory:')
|
|
122
|
+
await mkdir(path.dirname(config.database), { recursive: true })
|
|
123
|
+
return reldb.init(config)
|
|
124
|
+
},
|
|
125
|
+
close: () => reldb.close(),
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
name: 'cache',
|
|
129
|
+
start: () => cache.init(CacheConfigSchema.parse(core.config.getOrThrow('cache'))),
|
|
130
|
+
close: () => cache.close(),
|
|
131
|
+
},
|
|
132
|
+
{{#if features.vecdb}}
|
|
133
|
+
{
|
|
134
|
+
name: 'vecdb',
|
|
135
|
+
start: () => vecdb.init(VecdbConfigSchema.parse(core.config.getOrThrow('vecdb'))),
|
|
136
|
+
close: () => vecdb.close(),
|
|
137
|
+
},
|
|
138
|
+
{{/if}}
|
|
139
|
+
{
|
|
140
|
+
name: 'crypto',
|
|
141
|
+
start: () => crypto.init(),
|
|
142
|
+
close: () => crypto.close(),
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
name: 'storage',
|
|
146
|
+
start: () => storage.init(StorageConfigSchema.parse(core.config.getOrThrow('storage'))),
|
|
147
|
+
close: () => storage.close(),
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
name: 'reach',
|
|
151
|
+
enabled: () => Boolean(core.config.get('reach')),
|
|
152
|
+
start: () => reach.init(ReachConfigSchema.parse(core.config.getOrThrow('reach'))),
|
|
153
|
+
close: () => reach.close(),
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
name: 'audit',
|
|
157
|
+
start: () => audit.init({
|
|
158
|
+
...AuditInitConfigSchema.parse(core.config.get('audit') ?? {}),
|
|
159
|
+
storage,
|
|
160
|
+
reach,
|
|
161
|
+
}),
|
|
162
|
+
close: () => audit.close(),
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
name: 'iam',
|
|
166
|
+
start: () => iam.init({
|
|
167
|
+
...IamConfigSchema.parse(core.config.getOrThrow('iam')),
|
|
168
|
+
reach,
|
|
169
|
+
}),
|
|
170
|
+
close: () => iam.close(),
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
name: 'audit-jobs',
|
|
174
|
+
enabled: () => Boolean(audit.schedulerPlan),
|
|
175
|
+
start: startAuditJobs,
|
|
176
|
+
close: () => scheduler.close(),
|
|
177
|
+
},
|
|
99
178
|
{{#if features.feedback}}
|
|
100
|
-
{
|
|
179
|
+
{
|
|
180
|
+
name: 'feedback',
|
|
181
|
+
enabled: () => Boolean(core.config.get('feedback')),
|
|
182
|
+
start: startFeedback,
|
|
183
|
+
close: closeFeedback,
|
|
184
|
+
},
|
|
101
185
|
{{/if}}
|
|
102
186
|
{{#if features.ai}}
|
|
103
|
-
{
|
|
187
|
+
{
|
|
188
|
+
name: 'ai',
|
|
189
|
+
start: () => ai.init(AIConfigSchema.parse(core.config.getOrThrow('ai'))),
|
|
190
|
+
close: () => ai.close(),
|
|
191
|
+
},
|
|
104
192
|
{{/if}}
|
|
105
|
-
{
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
193
|
+
{
|
|
194
|
+
name: 'bootstrap',
|
|
195
|
+
start: async () => {
|
|
196
|
+
requireInitialized(await initBusinessSettings(), 'business-settings')
|
|
197
|
+
await ensureRolesAndAdmin()
|
|
198
|
+
const app = core.config.getOrThrow<CoreConfig>('core')
|
|
199
|
+
core.logger.info('Application initialized', { name: app.name, version: app.version, environment: app.env })
|
|
200
|
+
},
|
|
201
|
+
close: () => { },
|
|
202
|
+
},
|
|
111
203
|
])
|
|
112
204
|
|
|
113
205
|
/** 仅声明模块和业务钩子,失败回滚、并发及关闭重试由 core 负责。 */
|
|
@@ -125,15 +217,27 @@ export async function closeModules(): Promise<void> {
|
|
|
125
217
|
|
|
126
218
|
/** 此函数仅用于进程启动边界,业务请求继续返回 HaiResult。 */
|
|
127
219
|
function requireInitialized<T>(result: HaiResult<T>, moduleName: string): T {
|
|
128
|
-
if (!result.success)
|
|
129
|
-
throw Object.assign(new Error(moduleName), {
|
|
220
|
+
if (!result.success) {
|
|
221
|
+
throw Object.assign(new Error(moduleName), {
|
|
222
|
+
code: result.error.code,
|
|
223
|
+
ext: {
|
|
224
|
+
fields: moduleName === 'iam-bootstrap'
|
|
225
|
+
? ['HAI_ADMIN_DEFAULT_PASSWORD']
|
|
226
|
+
: [],
|
|
227
|
+
},
|
|
228
|
+
})
|
|
229
|
+
}
|
|
130
230
|
return result.data
|
|
131
231
|
}
|
|
132
232
|
|
|
133
233
|
async function ensureRolesAndAdmin(): Promise<void> {
|
|
134
234
|
requireInitialized(await seedAppDuties(), 'app-duties')
|
|
135
235
|
await ensureInitialSystemAdmin()
|
|
136
|
-
for (const [username, password] of [
|
|
236
|
+
for (const [username, password] of [
|
|
237
|
+
['audit_admin', process.env.HAI_AUDIT_ADMIN_PASSWORD],
|
|
238
|
+
['config_admin', process.env.HAI_CONFIG_ADMIN_PASSWORD],
|
|
239
|
+
['business_operator', process.env.HAI_BUSINESS_OPERATOR_PASSWORD],
|
|
240
|
+
] as const)
|
|
137
241
|
requireInitialized(await bootstrapDutyAccount(username, password), 'app-duty-bootstrap')
|
|
138
242
|
}
|
|
139
243
|
|
|
@@ -142,9 +246,19 @@ async function ensureInitialSystemAdmin(): Promise<void> {
|
|
|
142
246
|
if (users.total > 0)
|
|
143
247
|
return
|
|
144
248
|
const password = process.env.HAI_ADMIN_DEFAULT_PASSWORD?.trim()
|
|
145
|
-
if (!password)
|
|
146
|
-
throw Object.assign(new Error('Administrator bootstrap configuration required'), {
|
|
147
|
-
|
|
249
|
+
if (!password) {
|
|
250
|
+
throw Object.assign(new Error('Administrator bootstrap configuration required'), {
|
|
251
|
+
code: HaiConfigError.CONFIG_ENV_VAR_MISSING.code,
|
|
252
|
+
ext: {
|
|
253
|
+
fields: ['HAI_ADMIN_DEFAULT_PASSWORD'],
|
|
254
|
+
},
|
|
255
|
+
})
|
|
256
|
+
}
|
|
257
|
+
const registered = requireInitialized(await iam.user.createUser({
|
|
258
|
+
username: 'admin',
|
|
259
|
+
password,
|
|
260
|
+
displayName: serverM('admin'),
|
|
261
|
+
}), 'iam-bootstrap')
|
|
148
262
|
const adminRole = requireInitialized(await iam.authz.getRoleByCode('admin'), 'iam-role')
|
|
149
263
|
if (!adminRole)
|
|
150
264
|
throw new Error('Administrator role is missing')
|