@h-ai/cli 1.0.0-beta.2 → 1.0.0-beta.21
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 +11 -6
- 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 +58 -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
package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/security-roles.ts.hbs
CHANGED
|
@@ -8,14 +8,26 @@ export const APP_DUTIES = {
|
|
|
8
8
|
admin: ['app.users.manage', 'app.roles.manage'],
|
|
9
9
|
audit_admin: ['audit:read', 'audit:backup'],
|
|
10
10
|
config_admin: ['app.config.manage'],
|
|
11
|
-
business_operator: [
|
|
11
|
+
business_operator: [
|
|
12
|
+
'feedback:create',
|
|
13
|
+
'feedback:read',
|
|
14
|
+
'feedback:process',
|
|
15
|
+
'feedback:reopen',
|
|
16
|
+
'feedback:export',
|
|
17
|
+
],
|
|
12
18
|
user: ['feedback:create'],
|
|
13
19
|
} as const
|
|
14
20
|
|
|
15
21
|
export async function seedAppDuties(): Promise<HaiResult<void>> {
|
|
16
22
|
if (iam.config?.rbac.superAdminRole && iam.config.rbac.superAdminRole in APP_DUTIES)
|
|
17
23
|
return err(HaiCommonError.VALIDATION_ERROR)
|
|
18
|
-
for (const code of [
|
|
24
|
+
for (const code of [
|
|
25
|
+
'admin',
|
|
26
|
+
'audit_admin',
|
|
27
|
+
'config_admin',
|
|
28
|
+
'business_operator',
|
|
29
|
+
'user',
|
|
30
|
+
] as const) {
|
|
19
31
|
const permissionIds: string[] = []
|
|
20
32
|
for (const permissionCode of APP_DUTIES[code]) {
|
|
21
33
|
let permission = await iam.authz.getPermissionByCode(permissionCode)
|
|
@@ -25,8 +37,11 @@ export async function seedAppDuties(): Promise<HaiResult<void>> {
|
|
|
25
37
|
permission = await iam.authz.createPermission({ code: permissionCode, name: permissionCode, type: 'api' })
|
|
26
38
|
if (!permission.success) {
|
|
27
39
|
permission = await iam.authz.getPermissionByCode(permissionCode)
|
|
28
|
-
if (!permission.success || !permission.data)
|
|
29
|
-
return permission.success
|
|
40
|
+
if (!permission.success || !permission.data) {
|
|
41
|
+
return permission.success
|
|
42
|
+
? err(HaiCommonError.INTERNAL_ERROR)
|
|
43
|
+
: permission
|
|
44
|
+
}
|
|
30
45
|
}
|
|
31
46
|
}
|
|
32
47
|
if (permission.data)
|
|
@@ -36,16 +51,26 @@ export async function seedAppDuties(): Promise<HaiResult<void>> {
|
|
|
36
51
|
if (!role.success)
|
|
37
52
|
return role
|
|
38
53
|
if (!role.data) {
|
|
39
|
-
role = await iam.authz.createRole({
|
|
54
|
+
role = await iam.authz.createRole({
|
|
55
|
+
code,
|
|
56
|
+
name: serverM(code),
|
|
57
|
+
isSystem: true,
|
|
58
|
+
})
|
|
40
59
|
if (!role.success) {
|
|
41
60
|
role = await iam.authz.getRoleByCode(code)
|
|
42
|
-
if (!role.success || !role.data)
|
|
43
|
-
return role.success
|
|
61
|
+
if (!role.success || !role.data) {
|
|
62
|
+
return role.success
|
|
63
|
+
? err(HaiCommonError.INTERNAL_ERROR)
|
|
64
|
+
: role
|
|
65
|
+
}
|
|
44
66
|
}
|
|
45
67
|
}
|
|
46
68
|
if (!role.data)
|
|
47
69
|
return err(HaiCommonError.INTERNAL_ERROR)
|
|
48
|
-
const updated = await iam.authz.updateRole(role.data.id, {
|
|
70
|
+
const updated = await iam.authz.updateRole(role.data.id, {
|
|
71
|
+
name: serverM(code),
|
|
72
|
+
permissionIds,
|
|
73
|
+
})
|
|
49
74
|
if (!updated.success)
|
|
50
75
|
return updated
|
|
51
76
|
}
|
|
@@ -58,11 +83,17 @@ export async function bootstrapDutyAccount(username: 'audit_admin' | 'config_adm
|
|
|
58
83
|
if (!password)
|
|
59
84
|
return ok(undefined)
|
|
60
85
|
const created = await iam.user.createUser({ username, password })
|
|
61
|
-
if (!created.success)
|
|
62
|
-
return created.error.code === HaiIamError.USER_ALREADY_EXISTS.code
|
|
86
|
+
if (!created.success) {
|
|
87
|
+
return created.error.code === HaiIamError.USER_ALREADY_EXISTS.code
|
|
88
|
+
? ok(undefined)
|
|
89
|
+
: created
|
|
90
|
+
}
|
|
63
91
|
const role = await iam.authz.getRoleByCode(username)
|
|
64
|
-
if (!role.success || !role.data)
|
|
65
|
-
return role.success
|
|
92
|
+
if (!role.success || !role.data) {
|
|
93
|
+
return role.success
|
|
94
|
+
? err(HaiCommonError.INTERNAL_ERROR)
|
|
95
|
+
: role
|
|
96
|
+
}
|
|
66
97
|
const assigned = await iam.authz.syncRoles(created.data.user.id, [role.data.id])
|
|
67
98
|
if (!assigned.success) {
|
|
68
99
|
const removed = await iam.user.deleteUser(created.data.user.id)
|
|
@@ -34,8 +34,17 @@ interface LoginData { user: { id: string }, tokens: { accessToken: string, refre
|
|
|
34
34
|
async function call<T>(route: string, input?: object, token?: string, method = 'POST'): Promise<HaiResult<T>> {
|
|
35
35
|
const response = await transport.encryptedFetch(`http://localhost${PREFIX}${route}`, {
|
|
36
36
|
method,
|
|
37
|
-
headers: {
|
|
38
|
-
|
|
37
|
+
headers: {
|
|
38
|
+
'content-type': 'application/json',
|
|
39
|
+
...(token
|
|
40
|
+
? { authorization: `Bearer ${token}` }
|
|
41
|
+
: {}),
|
|
42
|
+
},
|
|
43
|
+
...(input
|
|
44
|
+
? {
|
|
45
|
+
body: JSON.stringify(input),
|
|
46
|
+
}
|
|
47
|
+
: {}),
|
|
39
48
|
})
|
|
40
49
|
return response.json() as Promise<HaiResult<T>>
|
|
41
50
|
}
|
|
@@ -45,12 +54,14 @@ beforeAll(async () => {
|
|
|
45
54
|
vi.stubEnv('HAI_DB_DATABASE', '":memory:"')
|
|
46
55
|
vi.stubEnv('HAI_STORAGE_ROOT', storageRoot)
|
|
47
56
|
vi.stubEnv('HAI_ADMIN_DEFAULT_PASSWORD', TEST_PASSWORD)
|
|
48
|
-
core.init({ configDir: './config' })
|
|
49
57
|
await initializeModules()
|
|
50
58
|
const key = crypto.asymmetric.generateKeyPair()
|
|
51
59
|
if (!key.success)
|
|
52
60
|
throw new Error(key.error.message)
|
|
53
|
-
vi.stubEnv('HAI_CRYPTO_TRANSPORT_SERVERKEYRING', JSON.stringify({
|
|
61
|
+
vi.stubEnv('HAI_CRYPTO_TRANSPORT_SERVERKEYRING', JSON.stringify({
|
|
62
|
+
activeKeyId: 'test-v1',
|
|
63
|
+
keys: { 'test-v1': key.data },
|
|
64
|
+
}))
|
|
54
65
|
app = createServerApp()
|
|
55
66
|
transport = crypto.transport.createClient({
|
|
56
67
|
keyExchangeUrl: `http://localhost${PREFIX}/_hai/key-exchange`,
|
|
@@ -100,7 +111,12 @@ describe('{{fullstack.servPackageName}} mini administration', () => {
|
|
|
100
111
|
if (!result.success)
|
|
101
112
|
return
|
|
102
113
|
const config = core.config.getOrThrow<CoreConfig>('core')
|
|
103
|
-
expect(result.data).toMatchObject({
|
|
114
|
+
expect(result.data).toMatchObject({
|
|
115
|
+
name: config.name,
|
|
116
|
+
version: config.version,
|
|
117
|
+
env: config.env,
|
|
118
|
+
defaultLocale: config.defaultLocale,
|
|
119
|
+
})
|
|
104
120
|
const invalid = await transport.encryptedFetch(`http://localhost${PREFIX}/app/echo`, {
|
|
105
121
|
method: 'POST',
|
|
106
122
|
headers: { 'content-type': 'application/json' },
|
|
@@ -130,7 +146,14 @@ describe('{{fullstack.servPackageName}} mini administration', () => {
|
|
|
130
146
|
it('mounts public auth routes while keeping default IAM administration routes private', async () => {
|
|
131
147
|
const config = await transport.encryptedFetch(`http://localhost${PREFIX}/auth/config`)
|
|
132
148
|
expect(config.status).toBe(200)
|
|
133
|
-
for (const route of [
|
|
149
|
+
for (const route of [
|
|
150
|
+
'/auth/register',
|
|
151
|
+
'/auth/login/ldap',
|
|
152
|
+
'/auth/login/otp',
|
|
153
|
+
'/auth/otp/send',
|
|
154
|
+
'/auth/password-reset/request',
|
|
155
|
+
'/auth/password-reset/confirm',
|
|
156
|
+
]) {
|
|
134
157
|
const response = await transport.encryptedFetch(`http://localhost${PREFIX}${route}`, {
|
|
135
158
|
method: 'POST',
|
|
136
159
|
headers: { 'content-type': 'application/json' },
|
|
@@ -145,14 +168,21 @@ describe('{{fullstack.servPackageName}} mini administration', () => {
|
|
|
145
168
|
const response = await transport.encryptedFetch(`http://localhost${PREFIX}${route}`, {
|
|
146
169
|
method,
|
|
147
170
|
headers: { 'content-type': 'application/json', 'authorization': `Bearer ${adminToken}` },
|
|
148
|
-
...(method === 'POST'
|
|
171
|
+
...(method === 'POST'
|
|
172
|
+
? { body: '{}' }
|
|
173
|
+
: {}),
|
|
149
174
|
})
|
|
150
175
|
expect(response.status).toBe(404)
|
|
151
176
|
}
|
|
152
177
|
})
|
|
153
178
|
|
|
154
179
|
it('creates a normal user and enforces guards on every administration API', async () => {
|
|
155
|
-
const created = await call<AdminUser>('/admin/users/create', {
|
|
180
|
+
const created = await call<AdminUser>('/admin/users/create', {
|
|
181
|
+
username: 'normal-user',
|
|
182
|
+
password: TEST_PASSWORD,
|
|
183
|
+
role: 'user',
|
|
184
|
+
accountStatus: 'active',
|
|
185
|
+
}, adminToken)
|
|
156
186
|
expect(created.success).toBe(true)
|
|
157
187
|
if (!created.success)
|
|
158
188
|
return
|
|
@@ -163,14 +193,35 @@ describe('{{fullstack.servPackageName}} mini administration', () => {
|
|
|
163
193
|
return
|
|
164
194
|
userToken = loggedIn.data.tokens.accessToken
|
|
165
195
|
for (const [route, input] of [
|
|
166
|
-
[
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
[
|
|
196
|
+
[
|
|
197
|
+
'/admin/users/list',
|
|
198
|
+
{ page: 1, pageSize: 20 },
|
|
199
|
+
],
|
|
200
|
+
[
|
|
201
|
+
'/admin/users/create',
|
|
202
|
+
{ username: 'forbidden', password: TEST_PASSWORD },
|
|
203
|
+
],
|
|
204
|
+
[
|
|
205
|
+
'/admin/users/update',
|
|
206
|
+
{ id: adminId, accountStatus: 'dormant' },
|
|
207
|
+
],
|
|
208
|
+
[
|
|
209
|
+
'/admin/users/delete',
|
|
210
|
+
{ id: adminId },
|
|
211
|
+
],
|
|
212
|
+
[
|
|
213
|
+
'/admin/users/avatar',
|
|
214
|
+
{ id: adminId, contentType: 'image/png', base64: PNG },
|
|
215
|
+
],
|
|
171
216
|
['/admin/roles/list', undefined],
|
|
172
|
-
[
|
|
173
|
-
|
|
217
|
+
[
|
|
218
|
+
'/admin/roles/update',
|
|
219
|
+
{ id: 'missing', description: 'x' },
|
|
220
|
+
],
|
|
221
|
+
[
|
|
222
|
+
'/admin/audit/list',
|
|
223
|
+
{ page: 1, pageSize: 20 },
|
|
224
|
+
],
|
|
174
225
|
] as const) {
|
|
175
226
|
const denied = await call(route, input, userToken)
|
|
176
227
|
expect(denied.success).toBe(false)
|
|
@@ -180,8 +231,16 @@ describe('{{fullstack.servPackageName}} mini administration', () => {
|
|
|
180
231
|
})
|
|
181
232
|
|
|
182
233
|
it('allows refresh-token body transport only for an identified trusted native origin', async () => {
|
|
183
|
-
for (const [clientName, origin] of [
|
|
184
|
-
|
|
234
|
+
for (const [clientName, origin] of [
|
|
235
|
+
['{{projectName}}-app', 'capacitor://localhost'],
|
|
236
|
+
['{{projectName}}-desktop', 'http://tauri.localhost'],
|
|
237
|
+
] as const) {
|
|
238
|
+
const headers = {
|
|
239
|
+
'content-type': 'application/json',
|
|
240
|
+
'x-request-client': clientName,
|
|
241
|
+
'x-hai-token-transport': 'body',
|
|
242
|
+
origin,
|
|
243
|
+
}
|
|
185
244
|
const login = await transport.encryptedFetch(`http://localhost${PREFIX}/auth/login`, {
|
|
186
245
|
method: 'POST',
|
|
187
246
|
headers,
|
|
@@ -203,10 +262,18 @@ describe('{{fullstack.servPackageName}} mini administration', () => {
|
|
|
203
262
|
if (refreshed.success)
|
|
204
263
|
expect(refreshed.data.tokens.refreshToken).not.toBe(result.data.tokens.refreshToken)
|
|
205
264
|
}
|
|
206
|
-
for (const [clientName, origin] of [
|
|
265
|
+
for (const [clientName, origin] of [
|
|
266
|
+
['{{projectName}}-app', 'http://untrusted.example'],
|
|
267
|
+
['unrecognized-client', 'capacitor://localhost'],
|
|
268
|
+
] as const) {
|
|
207
269
|
const response = await transport.encryptedFetch(`http://localhost${PREFIX}/auth/login`, {
|
|
208
270
|
method: 'POST',
|
|
209
|
-
headers: {
|
|
271
|
+
headers: {
|
|
272
|
+
'content-type': 'application/json',
|
|
273
|
+
'x-request-client': clientName,
|
|
274
|
+
'x-hai-token-transport': 'body',
|
|
275
|
+
origin,
|
|
276
|
+
},
|
|
210
277
|
body: JSON.stringify({ identifier: 'admin', password: TEST_PASSWORD }),
|
|
211
278
|
})
|
|
212
279
|
const result = await response.json() as HaiResult<LoginData>
|
|
@@ -223,7 +290,20 @@ describe('{{fullstack.servPackageName}} mini administration', () => {
|
|
|
223
290
|
expect(listed.data.items).toHaveLength(1)
|
|
224
291
|
expect(listed.data.total).toBe(2)
|
|
225
292
|
}
|
|
226
|
-
for (const [route, input] of [
|
|
293
|
+
for (const [route, input] of [
|
|
294
|
+
[
|
|
295
|
+
'/admin/users/delete',
|
|
296
|
+
{ id: adminId },
|
|
297
|
+
],
|
|
298
|
+
[
|
|
299
|
+
'/admin/users/update',
|
|
300
|
+
{ id: adminId, accountStatus: 'dormant' },
|
|
301
|
+
],
|
|
302
|
+
[
|
|
303
|
+
'/admin/users/update',
|
|
304
|
+
{ id: adminId, role: 'user' },
|
|
305
|
+
],
|
|
306
|
+
] as const) {
|
|
227
307
|
const denied = await call(route, input, adminToken)
|
|
228
308
|
expect(denied.success).toBe(false)
|
|
229
309
|
if (!denied.success)
|
|
@@ -231,7 +311,7 @@ describe('{{fullstack.servPackageName}} mini administration', () => {
|
|
|
231
311
|
}
|
|
232
312
|
})
|
|
233
313
|
|
|
234
|
-
it('updates personal preferences
|
|
314
|
+
it('updates personal preferences and clears the email address', async () => {
|
|
235
315
|
expect((await call('/profile/update', { displayName: 'Updated', locale: 'en-US', theme: '{{fullstack.theme.defaultTheme}}' }, userToken)).success).toBe(true)
|
|
236
316
|
const current = await call<{ metadata: { locale: string, theme: string } }>('/auth/me', undefined, userToken, 'GET')
|
|
237
317
|
expect(current.success).toBe(true)
|
|
@@ -242,16 +322,25 @@ describe('{{fullstack.servPackageName}} mini administration', () => {
|
|
|
242
322
|
expect(cleared.success).toBe(true)
|
|
243
323
|
if (cleared.success)
|
|
244
324
|
expect(cleared.data.email).toBe('')
|
|
325
|
+
})
|
|
326
|
+
|
|
327
|
+
it('rejects invalid images, oversized payloads and anonymous avatar uploads', async () => {
|
|
245
328
|
const invalid = await call('/profile/avatar', { contentType: 'image/png', base64: 'PHN2Zz48L3N2Zz4=' }, userToken)
|
|
246
329
|
expect(invalid.success).toBe(false)
|
|
247
330
|
if (!invalid.success)
|
|
248
331
|
expect(invalid.error.httpStatus).toBe(400)
|
|
249
|
-
const oversized = await call('/profile/avatar', {
|
|
332
|
+
const oversized = await call('/profile/avatar', {
|
|
333
|
+
contentType: 'image/png',
|
|
334
|
+
base64: Buffer.alloc(512 * 1024 + 1).toString('base64'),
|
|
335
|
+
}, userToken)
|
|
250
336
|
expect(oversized.success).toBe(false)
|
|
251
337
|
const anonymous = await call('/profile/avatar', { contentType: 'image/png', base64: PNG })
|
|
252
338
|
expect(anonymous.success).toBe(false)
|
|
253
339
|
if (!anonymous.success)
|
|
254
340
|
expect(anonymous.error.httpStatus).toBe(401)
|
|
341
|
+
})
|
|
342
|
+
|
|
343
|
+
it('serves only uploaded avatar resources and restricts profile fields', async () => {
|
|
255
344
|
const injected = await transport.encryptedFetch(`http://localhost${PREFIX}/profile/update`, {
|
|
256
345
|
method: 'POST',
|
|
257
346
|
headers: { 'content-type': 'application/json', 'authorization': `Bearer ${userToken}` },
|
|
@@ -291,7 +380,13 @@ describe('{{fullstack.servPackageName}} mini administration', () => {
|
|
|
291
380
|
const roles = await call<{ items: { id: string, code: string, userCount: number }[] }>('/admin/roles/list', undefined, adminToken)
|
|
292
381
|
expect(roles.success).toBe(true)
|
|
293
382
|
if (roles.success) {
|
|
294
|
-
expect(roles.data.items.map(item => item.code).sort()).toEqual([
|
|
383
|
+
expect(roles.data.items.map(item => item.code).sort()).toEqual([
|
|
384
|
+
'admin',
|
|
385
|
+
'audit_admin',
|
|
386
|
+
'business_operator',
|
|
387
|
+
'config_admin',
|
|
388
|
+
'user',
|
|
389
|
+
])
|
|
295
390
|
const role = roles.data.items.find(item => item.code === 'user')!
|
|
296
391
|
expect(role.userCount).toBe(1)
|
|
297
392
|
expect((await call('/admin/roles/update', { id: role.id, description: 'Updated description' }, adminToken)).success).toBe(true)
|
|
@@ -319,7 +414,12 @@ describe('{{fullstack.servPackageName}} mini administration', () => {
|
|
|
319
414
|
const before = await iam.user.getUser(user.id)
|
|
320
415
|
expect(before.success).toBe(true)
|
|
321
416
|
const sync = vi.spyOn(iam.authz, 'syncRoles').mockResolvedValueOnce(err(HaiCommonError.API_ERROR, 'Injected role persistence failure'))
|
|
322
|
-
const failed = await call('/admin/users/update', {
|
|
417
|
+
const failed = await call('/admin/users/update', {
|
|
418
|
+
id: user.id,
|
|
419
|
+
role: 'admin',
|
|
420
|
+
email: 'changed@example.com',
|
|
421
|
+
displayName: 'Should not change',
|
|
422
|
+
}, adminToken)
|
|
323
423
|
sync.mockRestore()
|
|
324
424
|
expect(failed.success).toBe(false)
|
|
325
425
|
const after = await iam.user.getUser(user.id)
|
|
@@ -333,7 +433,7 @@ describe('{{fullstack.servPackageName}} mini administration', () => {
|
|
|
333
433
|
const log = vi.spyOn(audit, 'log').mockImplementation((input, tx) => input.action === 'update' && input.resource === 'profile'
|
|
334
434
|
? Promise.resolve(err(HaiCommonError.API_ERROR, 'Injected audit persistence failure'))
|
|
335
435
|
: originalLog(input, tx))
|
|
336
|
-
const logger = vi.spyOn(core.logger, 'error').mockImplementation(() => {})
|
|
436
|
+
const logger = vi.spyOn(core.logger, 'error').mockImplementation(() => { })
|
|
337
437
|
try {
|
|
338
438
|
const result = await call<AdminUser>('/profile/update', { displayName: 'Committed profile' }, userToken)
|
|
339
439
|
expect(result.success).toBe(true)
|
|
@@ -369,9 +469,22 @@ describe('{{fullstack.servPackageName}} mini administration', () => {
|
|
|
369
469
|
const snapshot = await iam.session.get(userToken)
|
|
370
470
|
if (!snapshot.success || !snapshot.data)
|
|
371
471
|
throw new Error('Session unavailable')
|
|
372
|
-
const stale = await iam.session.update(userToken, {
|
|
472
|
+
const stale = await iam.session.update(userToken, {
|
|
473
|
+
roles: ['admin'],
|
|
474
|
+
permissions: ['*'],
|
|
475
|
+
}, snapshot.data.version)
|
|
373
476
|
expect(stale.success).toBe(true)
|
|
374
|
-
for (const [route, input] of [
|
|
477
|
+
for (const [route, input] of [
|
|
478
|
+
[
|
|
479
|
+
'/admin/users/list',
|
|
480
|
+
{ page: 1, pageSize: 20 },
|
|
481
|
+
],
|
|
482
|
+
['/admin/roles/list', undefined],
|
|
483
|
+
[
|
|
484
|
+
'/admin/audit/list',
|
|
485
|
+
{ page: 1, pageSize: 20 },
|
|
486
|
+
],
|
|
487
|
+
] as const) {
|
|
375
488
|
const denied = await call(route, input, userToken)
|
|
376
489
|
expect(denied.success).toBe(false)
|
|
377
490
|
if (!denied.success)
|
|
@@ -410,8 +523,8 @@ describe('{{fullstack.servPackageName}} mini administration', () => {
|
|
|
410
523
|
expect(otherLogin.success).toBe(true)
|
|
411
524
|
if (!otherLogin.success)
|
|
412
525
|
return
|
|
413
|
-
let resume: () => void = () => {}
|
|
414
|
-
let entered: () => void = () => {}
|
|
526
|
+
let resume: () => void = () => { }
|
|
527
|
+
let entered: () => void = () => { }
|
|
415
528
|
const paused = new Promise<void>((resolve) => {
|
|
416
529
|
resume = resolve
|
|
417
530
|
})
|
|
@@ -462,7 +575,10 @@ describe('{{fullstack.servPackageName}} mini administration', () => {
|
|
|
462
575
|
call('/admin/users/update', { id: adminId, accountStatus: 'dormant' }, loggedIn.data.tokens.accessToken),
|
|
463
576
|
])
|
|
464
577
|
expect(results.filter(result => result.success)).toHaveLength(1)
|
|
465
|
-
const admins = await iam.user.listUsers({
|
|
578
|
+
const admins = await iam.user.listUsers({
|
|
579
|
+
accountStatus: 'active',
|
|
580
|
+
include: ['roles'],
|
|
581
|
+
})
|
|
466
582
|
expect(admins.success).toBe(true)
|
|
467
583
|
if (admins.success) {
|
|
468
584
|
const surviving = admins.data.items.filter(item => item.roles?.some(role => role.code === 'admin'))
|
|
@@ -15,7 +15,6 @@ import { afterEach, beforeEach, expect, it, vi } from 'vitest'
|
|
|
15
15
|
import { closeModules, initializeModules } from '../src/server/runtime.js'
|
|
16
16
|
|
|
17
17
|
let storageRoot: string
|
|
18
|
-
|
|
19
18
|
beforeEach(async () => {
|
|
20
19
|
storageRoot = await mkdtemp(path.join(os.tmpdir(), '{{projectName}}-bootstrap-'))
|
|
21
20
|
vi.stubEnv('HAI_DB_DATABASE', '":memory:"')
|
|
@@ -23,22 +22,33 @@ beforeEach(async () => {
|
|
|
23
22
|
vi.stubEnv('HAI_IAM_PASSWORD_REQUIREUPPERCASE', 'true')
|
|
24
23
|
core.init({ configDir: './config' })
|
|
25
24
|
})
|
|
26
|
-
|
|
27
25
|
afterEach(async () => {
|
|
28
26
|
await closeModules()
|
|
29
27
|
vi.unstubAllEnvs()
|
|
30
28
|
await rm(storageRoot, { recursive: true, force: true })
|
|
31
29
|
})
|
|
32
|
-
|
|
33
30
|
it.each([
|
|
34
31
|
{ password: 'abc' },
|
|
35
32
|
{ password: 'lowercase123!' },
|
|
36
33
|
])('startup explains the failed password policy and permits a clean retry: $password', async ({ password }) => {
|
|
37
34
|
vi.stubEnv('HAI_ADMIN_DEFAULT_PASSWORD', password)
|
|
38
|
-
await expect(initializeModules()).rejects.toMatchObject({
|
|
39
|
-
|
|
35
|
+
await expect(initializeModules()).rejects.toMatchObject({
|
|
36
|
+
cause: {
|
|
37
|
+
ext: {
|
|
38
|
+
module: 'bootstrap',
|
|
39
|
+
fields: ['HAI_ADMIN_DEFAULT_PASSWORD'],
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
})
|
|
43
|
+
for (const module of [
|
|
44
|
+
audit,
|
|
45
|
+
iam,
|
|
46
|
+
storage,
|
|
47
|
+
crypto,
|
|
48
|
+
cache,
|
|
49
|
+
reldb,
|
|
50
|
+
])
|
|
40
51
|
expect(module.isInitialized).toBe(false)
|
|
41
|
-
|
|
42
52
|
// 失败后再次初始化,确认未保留半成品管理员或未释放的模块。
|
|
43
53
|
vi.stubEnv('HAI_ADMIN_DEFAULT_PASSWORD', 'Bootstrap-retry-Admin123!')
|
|
44
54
|
await initializeModules()
|
|
@@ -49,14 +59,27 @@ it.each([
|
|
|
49
59
|
expect(users.data.items[0]?.username).toBe('admin')
|
|
50
60
|
}
|
|
51
61
|
})
|
|
52
|
-
|
|
53
62
|
it.each(['', ' '])('startup identifies an unset administrator password without exposing its value', async (password) => {
|
|
54
63
|
vi.stubEnv('HAI_ADMIN_DEFAULT_PASSWORD', password)
|
|
55
|
-
await expect(initializeModules()).rejects.toMatchObject({
|
|
56
|
-
|
|
64
|
+
await expect(initializeModules()).rejects.toMatchObject({
|
|
65
|
+
cause: {
|
|
66
|
+
ext: {
|
|
67
|
+
module: 'bootstrap',
|
|
68
|
+
code: HaiConfigError.CONFIG_ENV_VAR_MISSING.code,
|
|
69
|
+
fields: ['HAI_ADMIN_DEFAULT_PASSWORD'],
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
})
|
|
73
|
+
for (const module of [
|
|
74
|
+
audit,
|
|
75
|
+
iam,
|
|
76
|
+
storage,
|
|
77
|
+
crypto,
|
|
78
|
+
cache,
|
|
79
|
+
reldb,
|
|
80
|
+
])
|
|
57
81
|
expect(module.isInitialized).toBe(false)
|
|
58
82
|
})
|
|
59
|
-
|
|
60
83
|
{{#if features.feedback}}
|
|
61
84
|
it('optional feedback failure does not roll back authentication and business modules', async () => {
|
|
62
85
|
vi.stubEnv('HAI_ADMIN_DEFAULT_PASSWORD', 'Bootstrap-retry-Admin123!')
|