@h-ai/cli 0.1.0-alpha.56 → 0.1.0-alpha.58

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.
Files changed (208) hide show
  1. package/README.en.md +239 -0
  2. package/README.ja.md +239 -0
  3. package/README.md +51 -1
  4. package/dist/chunk-2H7UOFLK.js +0 -0
  5. package/dist/chunk-F4IBPVZ5.js +355 -0
  6. package/dist/chunk-F4IBPVZ5.js.map +1 -0
  7. package/dist/{chunk-O3ANVICQ.js → chunk-WUSFJFW5.js} +13 -7
  8. package/dist/chunk-WUSFJFW5.js.map +1 -0
  9. package/dist/cli-config-templates-6XA7V6CC.js +5 -0
  10. package/dist/{cli-config-templates-OWBEGBZQ.js.map → cli-config-templates-6XA7V6CC.js.map} +1 -1
  11. package/dist/{cli-skill-templates-ZIWYMS4Y.js → cli-skill-templates-45CK4RQ2.js} +3 -3
  12. package/dist/{cli-skill-templates-ZIWYMS4Y.js.map → cli-skill-templates-45CK4RQ2.js.map} +1 -1
  13. package/dist/index.js +134 -125
  14. package/dist/index.js.map +1 -1
  15. package/dist/{src-TBIET7YU.js → src-DUGLX4YB.js} +7270 -2119
  16. package/dist/src-DUGLX4YB.js.map +1 -0
  17. package/package.json +17 -17
  18. package/templates/README.md +4 -4
  19. package/templates/apps/admin/src/routes/+layout.svelte.hbs +7 -0
  20. package/templates/apps/admin/src/routes/+page.svelte.hbs +5 -4
  21. package/templates/apps/admin/src/routes/admin/+layout.svelte.hbs +2 -2
  22. package/templates/apps/api/.gitignore.hbs +13 -0
  23. package/templates/apps/api/.npmrc.hbs +1 -0
  24. package/templates/apps/api/Dockerfile.hbs +6 -5
  25. package/templates/apps/api/apps/{{projectName}}-contract/package.json.hbs +1 -1
  26. package/templates/apps/api/apps/{{projectName}}-contract/src/app-schemas.ts.hbs +2 -0
  27. package/templates/apps/api/apps/{{projectName}}-contract/tsup.config.ts.hbs +1 -1
  28. package/templates/apps/api/apps/{{projectName}}-service/config/_core.yml.hbs +8 -0
  29. package/templates/apps/api/apps/{{projectName}}-service/package.json.hbs +1 -1
  30. package/templates/apps/api/apps/{{projectName}}-service/src/app.ts.hbs +7 -8
  31. package/templates/apps/api/apps/{{projectName}}-service/src/index.ts.hbs +4 -3
  32. package/templates/apps/api/apps/{{projectName}}-service/src/lib/server/init.ts.hbs +0 -1
  33. package/templates/apps/api/apps/{{projectName}}-service/src/server/procedures/app-procedures.ts.hbs +4 -0
  34. package/templates/apps/api/apps/{{projectName}}-service/tests/service.test.ts.hbs +3 -1
  35. package/templates/apps/api/e2e/api-service.spec.ts.hbs +4 -2
  36. package/templates/apps/api/{eslint.config.js → eslint.config.js.hbs} +6 -8
  37. package/templates/apps/api/package.json.hbs +3 -3
  38. package/templates/apps/api/pnpm-workspace.yaml.hbs +26 -0
  39. package/templates/apps/api/tsconfig.base.json +0 -1
  40. package/templates/apps/fullstack/.dockerignore.hbs +3 -0
  41. package/templates/apps/fullstack/.gitignore.hbs +16 -0
  42. package/templates/apps/fullstack/.npmrc.hbs +1 -0
  43. package/templates/apps/fullstack/Dockerfile.hbs +6 -5
  44. package/templates/apps/fullstack/apps/{{projectName}}-app/README.md.hbs +2 -2
  45. package/templates/apps/fullstack/apps/{{projectName}}-app/capacitor.config.ts.hbs +1 -1
  46. package/templates/apps/fullstack/apps/{{projectName}}-app/eslint.config.js.hbs +5 -8
  47. package/templates/apps/fullstack/apps/{{projectName}}-app/index.html.hbs +7 -2
  48. package/templates/apps/fullstack/apps/{{projectName}}-app/messages/en-US.json.hbs +1 -34
  49. package/templates/apps/fullstack/apps/{{projectName}}-app/messages/zh-CN.json.hbs +1 -34
  50. package/templates/apps/fullstack/apps/{{projectName}}-app/package.json.hbs +2 -2
  51. package/templates/apps/fullstack/apps/{{projectName}}-app/src/App.svelte.hbs +30 -170
  52. package/templates/apps/fullstack/apps/{{projectName}}-app/src/app.d.ts.hbs +1 -1
  53. package/templates/apps/fullstack/apps/{{projectName}}-app/src/lib/capacitor.ts.hbs +6 -5
  54. package/templates/apps/fullstack/apps/{{projectName}}-app/src/main.ts.hbs +1 -1
  55. package/templates/apps/fullstack/apps/{{projectName}}-app/vite.config.ts.hbs +48 -22
  56. package/templates/apps/fullstack/apps/{{projectName}}-desktop/README.md.hbs +3 -1
  57. package/templates/apps/fullstack/apps/{{projectName}}-desktop/eslint.config.js.hbs +5 -8
  58. package/templates/apps/fullstack/apps/{{projectName}}-desktop/index.html.hbs +6 -1
  59. package/templates/apps/fullstack/apps/{{projectName}}-desktop/messages/en-US.json.hbs +1 -28
  60. package/templates/apps/fullstack/apps/{{projectName}}-desktop/messages/zh-CN.json.hbs +1 -28
  61. package/templates/apps/fullstack/apps/{{projectName}}-desktop/package.json.hbs +5 -4
  62. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src/App.svelte.hbs +10 -141
  63. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/Cargo.toml.hbs +5 -5
  64. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src-tauri/tauri.conf.json.hbs +3 -3
  65. package/templates/apps/fullstack/apps/{{projectName}}-desktop/vite.config.ts.hbs +21 -2
  66. package/templates/apps/fullstack/apps/{{projectName}}-web/README.md.hbs +2 -2
  67. package/templates/apps/fullstack/apps/{{projectName}}-web/eslint.config.js.hbs +5 -8
  68. package/templates/apps/fullstack/apps/{{projectName}}-web/index.html.hbs +6 -1
  69. package/templates/apps/fullstack/apps/{{projectName}}-web/messages/en-US.json.hbs +1 -28
  70. package/templates/apps/fullstack/apps/{{projectName}}-web/messages/zh-CN.json.hbs +1 -28
  71. package/templates/apps/fullstack/apps/{{projectName}}-web/package.json.hbs +2 -2
  72. package/templates/apps/fullstack/apps/{{projectName}}-web/src/App.svelte.hbs +5 -143
  73. package/templates/apps/fullstack/apps/{{projectName}}-web/src/app.d.ts.hbs +1 -1
  74. package/templates/apps/fullstack/apps/{{projectName}}-web/src/main.ts.hbs +1 -1
  75. package/templates/apps/fullstack/apps/{{projectName}}-web/vite.config.ts.hbs +48 -22
  76. package/templates/apps/fullstack/e2e/fullstack.spec.ts.hbs +207 -17
  77. package/templates/apps/fullstack/{eslint.config.js → eslint.config.js.hbs} +6 -8
  78. package/templates/apps/fullstack/package.json.hbs +3 -3
  79. package/templates/apps/fullstack/packages/{{projectName}}-contract/README.md.hbs +2 -0
  80. package/templates/apps/fullstack/packages/{{projectName}}-contract/package.json.hbs +1 -1
  81. package/templates/apps/fullstack/packages/{{projectName}}-contract/src/admin-contract.ts.hbs +66 -0
  82. package/templates/apps/fullstack/packages/{{projectName}}-contract/src/admin-schemas.ts.hbs +96 -0
  83. package/templates/apps/fullstack/packages/{{projectName}}-contract/src/app-schemas.ts.hbs +3 -0
  84. package/templates/apps/fullstack/packages/{{projectName}}-contract/src/index.ts.hbs +3 -1
  85. package/templates/apps/fullstack/packages/{{projectName}}-contract/src/{{projectName}}-contract.ts.hbs +5 -0
  86. package/templates/apps/fullstack/packages/{{projectName}}-contract/tests/contract.test.ts.hbs +3 -0
  87. package/templates/apps/fullstack/packages/{{projectName}}-contract/tsup.config.ts.hbs +1 -1
  88. package/templates/apps/fullstack/packages/{{projectName}}-serv/README.md.hbs +13 -3
  89. package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_audit.yml.hbs +3 -0
  90. package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_cache.yml.hbs +2 -0
  91. package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_core.yml.hbs +10 -3
  92. package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_db.yml.hbs +3 -0
  93. package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_iam.yml.hbs +21 -0
  94. package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_serv.yml.hbs +10 -3
  95. package/templates/apps/fullstack/packages/{{projectName}}-serv/config/_storage.yml.hbs +3 -0
  96. package/templates/apps/fullstack/packages/{{projectName}}-serv/package.json.hbs +10 -3
  97. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/app-server.ts.hbs +63 -28
  98. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/index.ts.hbs +26 -11
  99. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/admin-service.ts.hbs +276 -0
  100. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/messages.ts.hbs +29 -0
  101. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/procedures/admin-procedures.ts.hbs +94 -0
  102. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/procedures/app-procedures.ts.hbs +6 -0
  103. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/procedures/index.ts.hbs +5 -1
  104. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/runtime.ts.hbs +98 -0
  105. package/templates/apps/fullstack/packages/{{projectName}}-serv/tests/app-server.test.ts.hbs +446 -33
  106. package/templates/apps/fullstack/packages/{{projectName}}-serv/tests/runtime.test.ts.hbs +48 -0
  107. package/templates/apps/fullstack/packages/{{projectName}}-serv/tsup.config.ts.hbs +1 -1
  108. package/templates/apps/fullstack/packages/{{projectName}}-shared/README.md.hbs +19 -26
  109. package/templates/apps/fullstack/packages/{{projectName}}-shared/messages/en-US.json.hbs +78 -15
  110. package/templates/apps/fullstack/packages/{{projectName}}-shared/messages/zh-CN.json.hbs +78 -15
  111. package/templates/apps/fullstack/packages/{{projectName}}-shared/package.json.hbs +4 -1
  112. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/index.ts.hbs +3 -4
  113. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/api/api-client.ts.hbs +53 -27
  114. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/AdminWorkspace.svelte.hbs +73 -0
  115. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/AppShell.svelte.hbs +55 -45
  116. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/AuditManagement.svelte.hbs +61 -0
  117. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/ProfileSettings.svelte.hbs +57 -0
  118. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/RoleManagement.svelte.hbs +49 -0
  119. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/UserManagement.svelte.hbs +174 -0
  120. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/stores/management-store.svelte.ts.hbs +399 -0
  121. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/stores/theme-store.svelte.ts.hbs +7 -39
  122. package/templates/apps/fullstack/packages/{{projectName}}-shared/svelte.config.js.hbs +1 -0
  123. package/templates/apps/fullstack/packages/{{projectName}}-shared/tests/api-client.test.ts.hbs +151 -0
  124. package/templates/apps/fullstack/packages/{{projectName}}-shared/tests/management-store.test.ts.hbs +101 -0
  125. package/templates/apps/fullstack/packages/{{projectName}}-shared/vitest.config.ts.hbs +7 -0
  126. package/templates/apps/fullstack/playwright.config.ts.hbs +6 -0
  127. package/templates/apps/fullstack/pnpm-workspace.yaml.hbs +24 -0
  128. package/templates/apps/fullstack/tsconfig.base.json +0 -1
  129. package/templates/apps/h5/src/routes/+layout.svelte.hbs +4 -0
  130. package/templates/apps/h5/src/routes/+page.svelte.hbs +3 -2
  131. package/templates/apps/mobile-app/index.html.hbs +2 -2
  132. package/templates/apps/mobile-app/src/App.svelte.hbs +6 -2
  133. package/templates/apps/mobile-app/src/lib/app-config.ts.hbs +13 -0
  134. package/templates/apps/website/src/routes/+layout.svelte.hbs +8 -2
  135. package/templates/apps/website/src/routes/+page.svelte.hbs +4 -3
  136. package/templates/base/.npmrc.hbs +1 -0
  137. package/templates/base/Dockerfile.hbs +9 -8
  138. package/templates/base/e2e/app.spec.ts.hbs +1 -0
  139. package/templates/base/eslint-rules.js +45 -0
  140. package/templates/base/eslint.config.js.hbs +6 -8
  141. package/templates/base/package.json.hbs +39 -42
  142. package/templates/base/pnpm-workspace.yaml.hbs +26 -0
  143. package/templates/base/src/app.d.ts.hbs +4 -0
  144. package/templates/base/src/hooks.server.ts.hbs +0 -8
  145. package/templates/base/src/lib/server/init.ts.hbs +26 -5
  146. package/templates/base/src/routes/+layout.server.ts.hbs +18 -0
  147. package/templates/base/tests/scaffold.test.ts.hbs +21 -1
  148. package/templates/base/vite.config.ts.hbs +21 -0
  149. package/templates/features/ai/routes/api/chat/+server.ts +6 -7
  150. package/templates/features/iam/routes-admin/(auth)/auth/login/+page.svelte.hbs +1 -1
  151. package/templates/features/iam/routes-h5/auth/login/+page.svelte.hbs +1 -1
  152. package/templates/features/iam/routes-shared/api/auth/login/+server.ts +14 -10
  153. package/templates/features/iam/routes-shared/api/auth/logout/+server.ts +6 -4
  154. package/templates/features/storage/routes/api/upload/+server.ts +10 -8
  155. package/templates/skills/AGENTS.md +9 -8
  156. package/templates/skills/CLAUDE.md +0 -10
  157. package/templates/skills/bridges/admin/AGENTS.md +8 -0
  158. package/templates/skills/bridges/admin/copilot-instructions.md +2 -0
  159. package/templates/skills/bridges/api/AGENTS.md +8 -1
  160. package/templates/skills/bridges/api/copilot-instructions.md +2 -0
  161. package/templates/skills/bridges/fullstack/AGENTS.md.hbs +21 -4
  162. package/templates/skills/bridges/fullstack/copilot-instructions.md.hbs +5 -0
  163. package/templates/skills/bridges/h5/AGENTS.md +8 -0
  164. package/templates/skills/bridges/h5/copilot-instructions.md +2 -0
  165. package/templates/skills/bridges/mobile-app/AGENTS.md +8 -0
  166. package/templates/skills/bridges/mobile-app/copilot-instructions.md +2 -0
  167. package/templates/skills/bridges/website/AGENTS.md +8 -0
  168. package/templates/skills/bridges/website/copilot-instructions.md +2 -0
  169. package/templates/skills/copilot-instructions.md +2 -0
  170. package/templates/skills/hai-api-client/SKILL.md +2 -0
  171. package/templates/skills/hai-app-create/SKILL.md +9 -7
  172. package/templates/skills/hai-app-create/development.md +72 -0
  173. package/templates/skills/hai-app-create/reference.md +44 -32
  174. package/templates/skills/hai-app-review/SKILL.md +50 -144
  175. package/templates/skills/hai-app-tests/SKILL.md +3 -1
  176. package/templates/skills/hai-app-tests/reference.md +47 -27
  177. package/templates/skills/hai-build/SKILL.md +5 -4
  178. package/templates/skills/hai-build/reference.md +56 -34
  179. package/templates/skills/hai-ci/SKILL.md +8 -15
  180. package/templates/skills/hai-core/SKILL.md +2 -0
  181. package/templates/skills/hai-core/reference.md +6 -4
  182. package/templates/skills/hai-framework-sync/SKILL.md +8 -2
  183. package/templates/skills/hai-pr-review/SKILL.md +5 -4
  184. package/templates/skills/hai-reldb/SKILL.md +1 -0
  185. package/templates/skills/hai-reldb/reference.md +3 -0
  186. package/templates/skills/hai-scheduler/SKILL.md +3 -0
  187. package/templates/skills/hai-serv/SKILL.md +2 -0
  188. package/templates/skills/hai-serv/reference.md +18 -2
  189. package/templates/skills/hai-storage/SKILL.md +3 -1
  190. package/templates/skills/hai-ui/SKILL.md +6 -0
  191. package/templates/skills/hai-ui/reference.md +7 -4
  192. package/templates/skills/hai-vecdb/reference.md +53 -49
  193. package/dist/chunk-O3ANVICQ.js.map +0 -1
  194. package/dist/chunk-WUYLYCW2.js +0 -237
  195. package/dist/chunk-WUYLYCW2.js.map +0 -1
  196. package/dist/cli-config-templates-OWBEGBZQ.js +0 -5
  197. package/dist/src-TBIET7YU.js.map +0 -1
  198. package/templates/apps/fullstack/apps/{{projectName}}-app/tests/api.test.ts.hbs +0 -28
  199. package/templates/apps/fullstack/apps/{{projectName}}-desktop/tests/api.test.ts.hbs +0 -28
  200. package/templates/apps/fullstack/apps/{{projectName}}-web/tests/api.test.ts.hbs +0 -55
  201. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/LanguageSwitcher.svelte.hbs +0 -27
  202. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/ThemeSwitcher.svelte.hbs +0 -18
  203. package/templates/skills/bridges/admin/CLAUDE.md +0 -11
  204. package/templates/skills/bridges/api/CLAUDE.md +0 -11
  205. package/templates/skills/bridges/fullstack/CLAUDE.md.hbs +0 -5
  206. package/templates/skills/bridges/h5/CLAUDE.md +0 -11
  207. package/templates/skills/bridges/mobile-app/CLAUDE.md +0 -11
  208. package/templates/skills/bridges/website/CLAUDE.md +0 -11
@@ -0,0 +1,94 @@
1
+ import type { ServContext } from '@h-ai/serv'
2
+ import { audit } from '@h-ai/audit'
3
+ import { err, HaiCommonError, ok } from '@h-ai/core'
4
+ import { iam } from '@h-ai/iam'
5
+ import { serv } from '@h-ai/serv'
6
+ import { createIamProcedures } from '@h-ai/serv/features/iam'
7
+ import { adminContract, authContract, profileContract } from '{{fullstack.contractPackageName}}'
8
+ import {
9
+ createAdminUser,
10
+ getAdminRoles,
11
+ isRoleCode,
12
+ recordAudit,
13
+ toAdminUser,
14
+ updateAdminUser,
15
+ updateProfile,
16
+ uploadAvatar,
17
+ withAdminMutation,
18
+ } from '../admin-service.js'
19
+ import { serverM } from '../messages.js'
20
+
21
+ /** 身份认证沿用框架公共契约与 refresh-cookie 传输。 */
22
+ export function createAuthProcedures() {
23
+ const { currentUser, refresh } = createIamProcedures({ iam }).auth
24
+ const { login, logout, changePassword } = authContract
25
+ // 仅覆写需要业务审计的认证操作,其余直接复用框架;未选认证入口不挂载。
26
+ const audited = serv.implement({ login, logout, changePassword }).context<ServContext>().route('login', async ({ input, context }) => {
27
+ const result = await iam.auth.login(input)
28
+ if (!result.success)
29
+ return result
30
+ await recordAudit({ ...context, session: { userId: result.data.user.id, roles: result.data.roles, permissions: result.data.permissions } }, 'login', 'auth')
31
+ return result
32
+ }).route('logout').auth().handle(async ({ context }) => {
33
+ const result = await iam.auth.logout(context.accessToken ?? '')
34
+ if (!result.success)
35
+ return result
36
+ await recordAudit(context, 'logout', 'auth')
37
+ return result
38
+ }).route('changePassword').auth().handle(async ({ input, context }) => {
39
+ const result = await iam.user.changeCurrentUserPassword(context.accessToken ?? '', input.oldPassword, input.newPassword)
40
+ if (!result.success)
41
+ return result
42
+ await recordAudit(context, 'password-change', 'profile', context.session.userId)
43
+ return result
44
+ }).build()
45
+ return { ...audited, currentUser, refresh }
46
+ }
47
+
48
+ export function createAdminProcedures() {
49
+ return serv.implement(adminContract).context<ServContext>().route('users.list').role('admin').handle(async ({ input }) => {
50
+ const users = await iam.user.listUsers(input)
51
+ if (!users.success)
52
+ return users
53
+ const roles = await iam.authz.getUserRolesForMany(users.data.items.map(user => user.id))
54
+ if (!roles.success)
55
+ return roles
56
+ return ok({ ...users.data, items: users.data.items.map(user => toAdminUser(user, roles.data.get(user.id) ?? [])) })
57
+ }).route('users.create').role('admin').handle(({ input, context }) => withAdminMutation(context, () => createAdminUser(input, context))).route('users.update').role('admin').handle(({ input, context }) => withAdminMutation(context, () => updateAdminUser(input, context))).route('users.delete').role('admin').handle(({ input, context }) => withAdminMutation(context, async () => {
58
+ if (context.session.userId === input.id)
59
+ return err(HaiCommonError.FORBIDDEN, serverM('selfProtection', { locale: context.locale }))
60
+ const result = await iam.user.deleteUser(input.id)
61
+ if (!result.success)
62
+ return result
63
+ await recordAudit(context, 'delete', 'users', input.id)
64
+ return ok({ deleted: true as const })
65
+ })).route('users.avatar').role('admin').handle(({ input, context }) => withAdminMutation(context, () => uploadAvatar(input.id, input, context))).route('roles.list').role('admin').handle(() => getAdminRoles()).route('roles.update').role('admin').handle(({ input, context }) => withAdminMutation(context, async () => {
66
+ const role = await iam.authz.getRole(input.id)
67
+ if (!role.success)
68
+ return role
69
+ if (!role.data || !isRoleCode(role.data.code))
70
+ return err(HaiCommonError.NOT_FOUND, serverM('notFound', { locale: context.locale }))
71
+ const updated = await iam.authz.updateRole(input.id, { description: input.description })
72
+ if (!updated.success)
73
+ return updated
74
+ const roles = await getAdminRoles()
75
+ if (!roles.success)
76
+ return roles
77
+ const result = roles.data.items.find(item => item.id === input.id)
78
+ if (!result)
79
+ return err(HaiCommonError.NOT_FOUND, serverM('notFound', { locale: context.locale }))
80
+ await recordAudit(context, 'update', 'roles', input.id, { fields: ['description'] })
81
+ return ok(result)
82
+ })).route('audit.list').role('admin').handle(async ({ input, context }) => {
83
+ if (input.startDate && input.endDate && input.startDate > input.endDate)
84
+ return err(HaiCommonError.VALIDATION_ERROR, serverM('invalidDateRange', { locale: context.locale }))
85
+ const result = await audit.list({ ...input, startDate: input.startDate ? new Date(input.startDate) : undefined, endDate: input.endDate ? new Date(input.endDate) : undefined })
86
+ if (!result.success)
87
+ return result
88
+ return ok({ ...result.data, page: input.page, pageSize: input.pageSize, items: result.data.items.map(item => ({ ...item, createdAt: new Date(item.createdAt).toISOString() })) })
89
+ }).build()
90
+ }
91
+
92
+ export function createProfileProcedures() {
93
+ return serv.implement(profileContract).context<ServContext>().route('update').auth().handle(({ input, context }) => updateProfile(input, context)).route('avatar').auth().handle(({ input, context }) => uploadAvatar(context.session.userId, input, context)).build()
94
+ }
@@ -7,6 +7,9 @@ import { appContract } from '{{fullstack.contractPackageName}}'
7
7
  export interface CreateAppProceduresOptions {
8
8
  readonly name: string
9
9
  readonly version: string
10
+ readonly env: string
11
+ readonly defaultLocale: string
12
+ readonly supportedLocales: readonly string[]
10
13
  readonly startedAt: string
11
14
  readonly frontends: readonly AppFrontendTarget[]
12
15
  }
@@ -32,6 +35,9 @@ function createAppInfo(options: CreateAppProceduresOptions): AppInfoOutputData {
32
35
  return {
33
36
  name: options.name,
34
37
  version: options.version,
38
+ env: options.env,
39
+ defaultLocale: options.defaultLocale,
40
+ supportedLocales: [...options.supportedLocales],
35
41
  startedAt: options.startedAt,
36
42
  frontends: [...options.frontends],
37
43
  }
@@ -1,4 +1,5 @@
1
1
  import type { CreateAppProceduresOptions } from './app-procedures.js'
2
+ import { createAdminProcedures, createAuthProcedures, createProfileProcedures } from './admin-procedures.js'
2
3
  import { createAppProcedures } from './app-procedures.js'
3
4
 
4
5
  export interface CreateServerProceduresOptions {
@@ -9,5 +10,8 @@ export interface CreateServerProceduresOptions {
9
10
  export function createServerProcedures(options: CreateServerProceduresOptions) {
10
11
  return {
11
12
  app: createAppProcedures(options.app),
13
+ iam: { auth: createAuthProcedures() },
14
+ admin: createAdminProcedures(),
15
+ profile: createProfileProcedures(),
12
16
  }
13
- }
17
+ }
@@ -0,0 +1,98 @@
1
+ import type { CoreConfig, HaiResult } from '@h-ai/core'
2
+ import { mkdir } from 'node:fs/promises'
3
+ import path from 'node:path'
4
+ import process from 'node:process'
5
+ import { audit, AuditInitConfigSchema } from '@h-ai/audit'
6
+ import { cache, CacheConfigSchema } from '@h-ai/cache'
7
+ import { core } from '@h-ai/core'
8
+ import { crypto } from '@h-ai/crypto'
9
+ import { iam, IamConfigSchema } from '@h-ai/iam'
10
+ import { reldb, ReldbConfigSchema } from '@h-ai/reldb'
11
+ import { ServConfigSchema } from '@h-ai/serv'
12
+ import { storage, StorageConfigSchema } from '@h-ai/storage'
13
+ import { serverM } from './messages.js'
14
+
15
+ let initialized = false
16
+ let initializing: Promise<void> | undefined
17
+
18
+ /** 初始化失败停止启动并反序释放资源;不把功能缺失降级为成功。 */
19
+ export async function initializeModules(): Promise<void> {
20
+ if (initialized)
21
+ return
22
+ initializing ??= runInitialization().finally(() => {
23
+ initializing = undefined
24
+ })
25
+ return initializing
26
+ }
27
+
28
+ async function runInitialization(): Promise<void> {
29
+ try {
30
+ requireInitialized(core.config.validate('serv', ServConfigSchema), 'serv-config')
31
+ const db = ReldbConfigSchema.parse(core.config.getOrThrow('db'))
32
+ const cacheConfig = CacheConfigSchema.parse(core.config.getOrThrow('cache'))
33
+ const storageConfig = StorageConfigSchema.parse(core.config.getOrThrow('storage'))
34
+ const iamConfig = IamConfigSchema.parse(core.config.getOrThrow('iam'))
35
+ const auditConfig = AuditInitConfigSchema.parse(core.config.get('audit') ?? {})
36
+ if (db.type === 'sqlite' && db.database !== ':memory:')
37
+ await mkdir(path.dirname(db.database), { recursive: true })
38
+ requireInitialized(await reldb.init(db), 'reldb')
39
+ requireInitialized(await cache.init(cacheConfig), 'cache')
40
+ requireInitialized(await crypto.init(), 'crypto')
41
+ requireInitialized(await storage.init(storageConfig), 'storage')
42
+ requireInitialized(await iam.init(iamConfig), 'iam')
43
+ requireInitialized(await audit.init(auditConfig), 'audit')
44
+ await ensureRolesAndAdmin()
45
+ initialized = true
46
+ const app = core.config.getOrThrow<CoreConfig>('core')
47
+ core.logger.info('Application initialized', { name: app.name, version: app.version, environment: app.env })
48
+ }
49
+ catch (error) {
50
+ await closeModules()
51
+ throw error
52
+ }
53
+ }
54
+
55
+ /** 此函数仅用于进程启动边界,业务请求继续返回 HaiResult。 */
56
+ function requireInitialized<T>(result: HaiResult<T>, moduleName: string): T {
57
+ if (!result.success)
58
+ throw new Error(`Failed to initialize ${moduleName}: ${result.error.message} (${result.error.code})`)
59
+ return result.data
60
+ }
61
+
62
+ async function ensureRolesAndAdmin(): Promise<void> {
63
+ for (const code of ['admin', 'user'] as const) {
64
+ const existing = requireInitialized(await iam.authz.getRoleByCode(code), 'iam-role')
65
+ if (!existing)
66
+ requireInitialized(await iam.authz.createRole({ code, name: serverM(code), description: serverM(code === 'admin' ? 'adminDescription' : 'userDescription'), isSystem: true }), 'iam-role')
67
+ }
68
+ const users = requireInitialized(await iam.user.listUsers({ page: 1, pageSize: 1 }), 'iam-users')
69
+ if (users.total > 0)
70
+ return
71
+ const password = process.env.HAI_ADMIN_DEFAULT_PASSWORD?.trim()
72
+ if (!password)
73
+ throw new Error('Set HAI_ADMIN_DEFAULT_PASSWORD before the first start to create the administrator account')
74
+ const registered = requireInitialized(await iam.user.register({ username: 'admin', password, displayName: serverM('admin') }), 'iam-bootstrap')
75
+ const adminRole = requireInitialized(await iam.authz.getRoleByCode('admin'), 'iam-role')
76
+ if (!adminRole)
77
+ throw new Error('Administrator role is missing')
78
+ const assigned = await iam.authz.syncRoles(registered.user.id, [adminRole.id])
79
+ if (!assigned.success) {
80
+ const removed = await iam.user.deleteUser(registered.user.id)
81
+ if (!removed.success)
82
+ core.logger.error('Failed to clean up administrator bootstrap', { error: removed.error.code })
83
+ requireInitialized(assigned, 'iam-bootstrap-role')
84
+ }
85
+ core.logger.info('Initial administrator created', { username: 'admin', passwordSource: 'HAI_ADMIN_DEFAULT_PASSWORD' })
86
+ }
87
+
88
+ /** 关闭顺序与依赖初始化顺序相反,同时覆盖启动失败路径。 */
89
+ export async function closeModules(): Promise<void> {
90
+ for (const [name, module] of [['audit', audit], ['iam', iam], ['storage', storage], ['crypto', crypto], ['cache', cache], ['reldb', reldb]] as const) {
91
+ if (!module.isInitialized)
92
+ continue
93
+ const result = await module.close()
94
+ if (result && !result.success)
95
+ core.logger.error('Failed to close application module', { module: name, error: result.error.code })
96
+ }
97
+ initialized = false
98
+ }