@h-ai/cli 1.0.0-beta.17 → 1.0.0-beta.19

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 (195) hide show
  1. package/README.en.md +6 -0
  2. package/README.ja.md +6 -0
  3. package/README.md +6 -0
  4. package/dist/{chunk-EWTIVFHN.js → chunk-2ZL5ORQ2.js} +94 -28
  5. package/dist/chunk-2ZL5ORQ2.js.map +1 -0
  6. package/dist/{chunk-S2ZP7BHG.js → chunk-L53P43B2.js} +138 -32
  7. package/dist/chunk-L53P43B2.js.map +1 -0
  8. package/dist/cli-config-templates-LWMD2I6V.js +5 -0
  9. package/dist/{cli-config-templates-MKVYSK3E.js.map → cli-config-templates-LWMD2I6V.js.map} +1 -1
  10. package/dist/{cli-skill-templates-M64VPXZ4.js → cli-skill-templates-D6S2OQ6X.js} +3 -3
  11. package/dist/{cli-skill-templates-M64VPXZ4.js.map → cli-skill-templates-D6S2OQ6X.js.map} +1 -1
  12. package/dist/index.d.ts +1 -1
  13. package/dist/index.js +2797 -666
  14. package/dist/index.js.map +1 -1
  15. package/dist/metafile-esm.json +1 -1
  16. package/package.json +22 -22
  17. package/templates/apps/admin/src/routes/+layout.svelte.hbs +16 -10
  18. package/templates/apps/admin/src/routes/+page.svelte.hbs +4 -4
  19. package/templates/apps/admin/src/routes/admin/+layout.svelte.hbs +17 -7
  20. package/templates/apps/admin/src/routes/admin/+page.svelte.hbs +26 -6
  21. package/templates/apps/admin/src/routes/admin/settings/+page.svelte.hbs +2 -2
  22. package/templates/apps/api/apps/{{projectName}}-contract/src/app-schemas.ts.hbs +6 -1
  23. package/templates/apps/api/apps/{{projectName}}-contract/src/{{projectName}}-contract.ts.hbs +13 -5
  24. package/templates/apps/api/apps/{{projectName}}-contract/tests/contract.test.ts.hbs +13 -5
  25. package/templates/apps/api/apps/{{projectName}}-contract/tsup.config.ts.hbs +3 -1
  26. package/templates/apps/api/apps/{{projectName}}-serv/src/app.ts.hbs +71 -32
  27. package/templates/apps/api/apps/{{projectName}}-serv/src/build-metadata.ts.hbs +0 -1
  28. package/templates/apps/api/apps/{{projectName}}-serv/src/lib/server/init.ts.hbs +131 -54
  29. package/templates/apps/api/apps/{{projectName}}-serv/src/server/procedures/index.ts.hbs +39 -13
  30. package/templates/apps/api/apps/{{projectName}}-serv/tests/service.test.ts.hbs +78 -31
  31. package/templates/apps/api/apps/{{projectName}}-serv/tsup.config.ts.hbs +3 -1
  32. package/templates/apps/api/apps/{{projectName}}-serv/vitest.config.ts.hbs +3 -1
  33. package/templates/apps/api/e2e/api-serv.spec.ts.hbs +20 -3
  34. package/templates/apps/api/e2e/capture-evidence.ts.hbs +4 -1
  35. package/templates/apps/api/eslint.config.js.hbs +3 -1
  36. package/templates/apps/api/images/serv.Dockerfile.hbs +3 -4
  37. package/templates/apps/api/playwright.config.ts.hbs +23 -19
  38. package/templates/apps/api/pnpm-workspace.yaml.hbs +27 -16
  39. package/templates/apps/fullstack/apps/{{projectName}}-app/capacitor.config.ts.hbs +1 -3
  40. package/templates/apps/fullstack/apps/{{projectName}}-app/eslint.config.js.hbs +10 -2
  41. package/templates/apps/fullstack/apps/{{projectName}}-app/src/App.svelte.hbs +10 -2
  42. package/templates/apps/fullstack/apps/{{projectName}}-app/src/build-metadata.ts.hbs +0 -1
  43. package/templates/apps/fullstack/apps/{{projectName}}-app/src/lib/capacitor.ts.hbs +0 -1
  44. package/templates/apps/fullstack/apps/{{projectName}}-app/svelte.config.js.hbs +4 -1
  45. package/templates/apps/fullstack/apps/{{projectName}}-app/vite.config.ts.hbs +16 -4
  46. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src/App.svelte.hbs +8 -2
  47. package/templates/apps/fullstack/apps/{{projectName}}-desktop/src/build-metadata.ts.hbs +0 -1
  48. package/templates/apps/fullstack/apps/{{projectName}}-desktop/svelte.config.js.hbs +4 -1
  49. package/templates/apps/fullstack/apps/{{projectName}}-desktop/vite.config.ts.hbs +16 -4
  50. package/templates/apps/fullstack/apps/{{projectName}}-web/eslint.config.js.hbs +10 -2
  51. package/templates/apps/fullstack/apps/{{projectName}}-web/src/build-metadata.ts.hbs +0 -1
  52. package/templates/apps/fullstack/apps/{{projectName}}-web/src/main.ts.hbs +0 -2
  53. package/templates/apps/fullstack/apps/{{projectName}}-web/svelte.config.js.hbs +4 -1
  54. package/templates/apps/fullstack/apps/{{projectName}}-web/vite.config.ts.hbs +19 -5
  55. package/templates/apps/fullstack/e2e/fullstack.spec.ts.hbs +111 -26
  56. package/templates/apps/fullstack/e2e/session-close.spec.ts.hbs +19 -5
  57. package/templates/apps/fullstack/eslint.config.js.hbs +3 -1
  58. package/templates/apps/fullstack/images/serv.Dockerfile.hbs +3 -4
  59. package/templates/apps/fullstack/images/web.Dockerfile.hbs +0 -2
  60. package/templates/apps/fullstack/packages/{{projectName}}-contract/src/admin-contract.ts.hbs +114 -23
  61. package/templates/apps/fullstack/packages/{{projectName}}-contract/src/admin-schemas.ts.hbs +57 -11
  62. package/templates/apps/fullstack/packages/{{projectName}}-contract/tsup.config.ts.hbs +3 -1
  63. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/app-server.ts.hbs +65 -30
  64. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/build-metadata.ts.hbs +0 -1
  65. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/admin-service.ts.hbs +69 -21
  66. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/business-settings.ts.hbs +21 -5
  67. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/procedures/admin-procedures.ts.hbs +63 -11
  68. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/procedures/app-procedures.ts.hbs +6 -1
  69. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/procedures/index.ts.hbs +3 -1
  70. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/runtime.ts.hbs +139 -49
  71. package/templates/apps/fullstack/packages/{{projectName}}-serv/src/server/security-roles.ts.hbs +43 -12
  72. package/templates/apps/fullstack/packages/{{projectName}}-serv/tests/app-server.test.ts.hbs +139 -28
  73. package/templates/apps/fullstack/packages/{{projectName}}-serv/tests/runtime.test.ts.hbs +33 -10
  74. package/templates/apps/fullstack/packages/{{projectName}}-serv/tests/security.test.ts.hbs +141 -25
  75. package/templates/apps/fullstack/packages/{{projectName}}-serv/tsup.config.ts.hbs +6 -2
  76. package/templates/apps/fullstack/packages/{{projectName}}-serv/vitest.config.ts.hbs +3 -1
  77. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/api/api-client.ts.hbs +9 -2
  78. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/api/feedback-client.ts.hbs +0 -3
  79. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/api/sensitive-actions.svelte.ts.hbs +15 -3
  80. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/AppShell.svelte.hbs +33 -7
  81. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/components/FeedbackEntry.svelte.hbs +16 -2
  82. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/audit.svelte.hbs +104 -13
  83. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/feedback.svelte.hbs +37 -7
  84. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/roles.svelte.hbs +9 -3
  85. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/settings.svelte.hbs +25 -3
  86. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/pages/users.svelte.hbs +133 -19
  87. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/security/role-label.ts.hbs +14 -2
  88. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/stores/guide-store.ts.hbs +66 -34
  89. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/stores/management-store.svelte.ts.hbs +132 -30
  90. package/templates/apps/fullstack/packages/{{projectName}}-shared/src/lib/stores/theme-store.svelte.ts.hbs +8 -2
  91. package/templates/apps/fullstack/packages/{{projectName}}-shared/tests/api-client.test.ts.hbs +23 -5
  92. package/templates/apps/fullstack/packages/{{projectName}}-shared/tests/management-store.test.ts.hbs +143 -21
  93. package/templates/apps/fullstack/packages/{{projectName}}-shared/vitest.config.ts.hbs +7 -2
  94. package/templates/apps/fullstack/playwright.config.ts.hbs +9 -3
  95. package/templates/apps/fullstack/pnpm-workspace.yaml.hbs +30 -15
  96. package/templates/apps/h5/src/routes/+layout.svelte.hbs +57 -21
  97. package/templates/apps/h5/src/routes/+page.svelte.hbs +2 -2
  98. package/templates/apps/h5/src/routes/discover/+page.svelte.hbs +2 -2
  99. package/templates/apps/h5/src/routes/profile/+page.svelte.hbs +2 -2
  100. package/templates/apps/mobile-app/src/App.svelte.hbs +17 -5
  101. package/templates/apps/mobile-app/src/lib/api.ts.hbs +14 -3
  102. package/templates/apps/mobile-app/src/lib/app-config.ts.hbs +8 -1
  103. package/templates/apps/mobile-app/src/lib/pages/home.svelte.hbs +6 -2
  104. package/templates/apps/mobile-app/src/lib/pages/tasks.svelte.hbs +5 -1
  105. package/templates/apps/website/scripts/static-website.mjs.hbs +16 -3
  106. package/templates/apps/website/src/routes/+layout.svelte.hbs +44 -23
  107. package/templates/apps/website/src/routes/+page.svelte.hbs +4 -4
  108. package/templates/apps/website/src/routes/about/+page.svelte.hbs +2 -2
  109. package/templates/apps/website/src/routes/contact/+page.svelte.hbs +2 -2
  110. package/templates/apps/website/src/routes/services/+page.svelte.hbs +20 -5
  111. package/templates/base/e2e/app.spec.ts.hbs +49 -16
  112. package/templates/base/eslint-rules.js +44 -25
  113. package/templates/base/eslint.config.js.hbs +3 -1
  114. package/templates/base/images/web.Dockerfile.hbs +3 -4
  115. package/templates/base/package.json.hbs +5 -4
  116. package/templates/base/patches/daisyui-5.7.50.patch +8 -0
  117. package/templates/base/patches/deepmerge-4.3.1.patch +51 -0
  118. package/templates/base/playwright.config.ts +3 -1
  119. package/templates/base/pnpm-workspace.yaml.hbs +33 -16
  120. package/templates/base/src/app.d.ts.hbs +1 -1
  121. package/templates/base/src/hooks.server.ts.hbs +36 -27
  122. package/templates/base/src/lib/build-metadata.ts.hbs +0 -1
  123. package/templates/base/src/lib/server/init.ts.hbs +124 -51
  124. package/templates/base/src/lib/server/kit-config.ts.hbs +7 -4
  125. package/templates/base/src/routes/+layout.server.ts.hbs +2 -2
  126. package/templates/base/svelte.config.js.hbs +10 -100
  127. package/templates/base/tests/scaffold.test.ts.hbs +12 -3
  128. package/templates/base/tsconfig.json.hbs +7 -4
  129. package/templates/base/vite.config.ts.hbs +108 -14
  130. package/templates/base/vitest.config.ts.hbs +6 -2
  131. package/templates/features/ai/routes/api/chat/+server.ts +3 -1
  132. package/templates/features/iam/routes-admin/(auth)/auth/forgot-password/+page.svelte.hbs +3 -3
  133. package/templates/features/iam/routes-admin/(auth)/auth/login/+page.svelte.hbs +3 -3
  134. package/templates/features/iam/routes-admin/(auth)/auth/register/+page.svelte.hbs +3 -3
  135. package/templates/features/iam/routes-admin/(auth)/auth/reset-password/+page.svelte.hbs +3 -3
  136. package/templates/features/iam/routes-h5/auth/forgot-password/+page.svelte.hbs +3 -3
  137. package/templates/features/iam/routes-h5/auth/login/+page.svelte.hbs +3 -3
  138. package/templates/features/iam/routes-h5/auth/register/+page.svelte.hbs +3 -3
  139. package/templates/features/iam/routes-h5/auth/reset-password/+page.svelte.hbs +3 -3
  140. package/templates/features/iam/routes-website/auth/forgot-password/+page.svelte.hbs +3 -3
  141. package/templates/features/iam/routes-website/auth/login/+page.svelte.hbs +3 -3
  142. package/templates/features/iam/routes-website/auth/register/+page.svelte.hbs +3 -3
  143. package/templates/features/iam/routes-website/auth/reset-password/+page.svelte.hbs +3 -3
  144. package/templates/features/payment/routes/api/v1/payment/create/+server.ts +11 -2
  145. package/templates/shared/browser-check/startup.js +32 -6
  146. package/templates/shared/browser-startup.spec.ts +3 -1
  147. package/templates/shared/build-metadata.ts +18 -4
  148. package/templates/shared/e2e-env.ts +22 -6
  149. package/templates/shared/native-name.mjs +9 -2
  150. package/templates/shared/public-base-path.mjs +17 -4
  151. package/templates/shared/run-e2e.mjs +40 -9
  152. package/templates/shared/run-node.mjs +15 -4
  153. package/templates/shared/run-tauri.mjs +37 -8
  154. package/templates/shared/vite-runtime-config.ts +9 -3
  155. package/templates/skills/AGENTS.md +10 -73
  156. package/templates/skills/bridges/admin/AGENTS.md +9 -65
  157. package/templates/skills/bridges/api/AGENTS.md +8 -59
  158. package/templates/skills/bridges/fullstack/AGENTS.md.hbs +9 -54
  159. package/templates/skills/bridges/h5/AGENTS.md +8 -63
  160. package/templates/skills/bridges/mobile-app/AGENTS.md +8 -62
  161. package/templates/skills/bridges/website/AGENTS.md +8 -63
  162. package/templates/skills/hai-ai/acp.md +9 -1
  163. package/templates/skills/hai-ai/guide.md +17 -6
  164. package/templates/skills/hai-ai/reference.md +21 -12
  165. package/templates/skills/hai-api-client/reference.md +1 -1
  166. package/templates/skills/hai-app-create/development.md +3 -1
  167. package/templates/skills/hai-app-create/project.md +33 -0
  168. package/templates/skills/hai-app-create/reference.md +8 -8
  169. package/templates/skills/hai-app-tests/reference.md +1 -1
  170. package/templates/skills/hai-build/SKILL.md +3 -2
  171. package/templates/skills/hai-build/paths.md +1 -1
  172. package/templates/skills/hai-build/reference.md +11 -7
  173. package/templates/skills/hai-capacitor/reference.md +2 -0
  174. package/templates/skills/hai-ci/SKILL.md +1 -0
  175. package/templates/skills/hai-core/reference.md +3 -0
  176. package/templates/skills/hai-crypto/reference.md +2 -0
  177. package/templates/skills/hai-deploy/SKILL.md +7 -1
  178. package/templates/skills/hai-deploy/middleware.md +3 -1
  179. package/templates/skills/hai-framework-sync/SKILL.md +13 -9
  180. package/templates/skills/hai-framework-sync/sync.md +2 -0
  181. package/templates/skills/hai-kit/reference.md +16 -4
  182. package/templates/skills/hai-payment/reference.md +2 -0
  183. package/templates/skills/hai-reach/SKILL.md +2 -1
  184. package/templates/skills/hai-reach/reference.md +1 -1
  185. package/templates/skills/hai-reldb/SKILL.md +1 -1
  186. package/templates/skills/hai-reldb/reference.md +4 -0
  187. package/templates/skills/hai-serv/reference.md +12 -2
  188. package/templates/skills/hai-storage/reference.md +12 -3
  189. package/templates/skills/hai-ui/SKILL.md +3 -2
  190. package/templates/skills/hai-ui/guide.md +1 -1
  191. package/templates/skills/hai-ui/reference.md +23 -35
  192. package/templates/skills/hai-vecdb/reference.md +1 -1
  193. package/dist/chunk-EWTIVFHN.js.map +0 -1
  194. package/dist/chunk-S2ZP7BHG.js.map +0 -1
  195. package/dist/cli-config-templates-MKVYSK3E.js +0 -5
@@ -2,13 +2,26 @@ import { IamListUsersInputSchema, IamPermissionSchema } from '@h-ai/api-contract
2
2
  import { z } from 'zod'
3
3
 
4
4
  /** 权限的敏感操作配置复用框架字段;职责授权仍由应用控制。 */
5
- export const AdminPermissionPolicySchema = IamPermissionSchema.pick({ id: true, code: true, name: true, sensitiveOperations: true })
5
+ export const AdminPermissionPolicySchema = IamPermissionSchema.pick({
6
+ id: true,
7
+ code: true,
8
+ name: true,
9
+ sensitiveOperations: true,
10
+ })
6
11
  export const AdminPermissionPolicyInputSchema = AdminPermissionPolicySchema.pick({ id: true, sensitiveOperations: true })
7
12
  export type AdminPermissionPolicy = z.infer<typeof AdminPermissionPolicySchema>
8
13
 
9
- export const BusinessSettingsSchema = z.object({ notice: z.string().trim().max(200) }).strict()
14
+ export const BusinessSettingsSchema = z.object({
15
+ notice: z.string().trim().max(200),
16
+ }).strict()
10
17
 
11
- export const AdminRoleCodeSchema = z.enum(['admin', 'audit_admin', 'config_admin', 'business_operator', 'user'])
18
+ export const AdminRoleCodeSchema = z.enum([
19
+ 'admin',
20
+ 'audit_admin',
21
+ 'config_admin',
22
+ 'business_operator',
23
+ 'user',
24
+ ])
12
25
  export const AdminPageInputSchema = z.object({
13
26
  page: z.number().int().min(1).default(1),
14
27
  pageSize: z.number().int().min(1).max(100).default(20),
@@ -49,10 +62,16 @@ export const AdminCreateUserSchema = z.object({
49
62
  accountStatus: z.enum(['active', 'dormant', 'closed']).default('active'),
50
63
  accountExpiresAt: z.string().datetime().nullable().optional(),
51
64
  }).strict()
52
- export const AdminInviteUserSchema = AdminCreateUserSchema.omit({ password: true }).extend({ phone: z.string().trim().min(1).max(32).optional(), accountStatus: z.literal('active').default('active') })
65
+ export const AdminInviteUserSchema = AdminCreateUserSchema.omit({ password: true }).extend({
66
+ phone: z.string().trim().min(1).max(32).optional(),
67
+ accountStatus: z.literal('active').default('active'),
68
+ })
53
69
  export const AdminUpdateUserSchema = z.object({
54
70
  id: z.string().min(1),
55
- email: z.union([z.email(), z.literal('')]).optional(),
71
+ email: z.union([
72
+ z.email(),
73
+ z.literal(''),
74
+ ]).optional(),
56
75
  displayName: z.string().max(100).optional(),
57
76
  accountType: z.enum(['permanent', 'temporary']).optional(),
58
77
  accountStatus: z.enum(['active', 'dormant', 'closed']).optional(),
@@ -73,7 +92,14 @@ export const AdminAuditInputSchema = AdminPageInputSchema.extend({
73
92
  scopeId: z.string().max(256).optional(),
74
93
  requestId: z.string().max(256).optional(),
75
94
  traceId: z.string().max(256).optional(),
76
- sortBy: z.enum(['createdAt', 'action', 'resource', 'category', 'outcome', 'severity']).optional(),
95
+ sortBy: z.enum([
96
+ 'createdAt',
97
+ 'action',
98
+ 'resource',
99
+ 'category',
100
+ 'outcome',
101
+ 'severity',
102
+ ]).optional(),
77
103
  sortDirection: z.enum(['asc', 'desc']).optional(),
78
104
  userId: z.string().max(100).optional(),
79
105
  action: z.string().max(50).optional(),
@@ -88,7 +114,12 @@ export const AdminAuditLogSchema = z.object({
88
114
  scopeId: z.string().nullish(),
89
115
  requestId: z.string().nullish(),
90
116
  traceId: z.string().nullish(),
91
- actorSnapshot: z.object({ id: z.string(), username: z.string().optional(), displayName: z.string().optional(), roles: z.array(z.string()).optional() }).nullish(),
117
+ actorSnapshot: z.object({
118
+ id: z.string(),
119
+ username: z.string().optional(),
120
+ displayName: z.string().optional(),
121
+ roles: z.array(z.string()).optional(),
122
+ }).nullish(),
92
123
  id: z.string(),
93
124
  userId: z.string().nullable(),
94
125
  username: z.string().nullable(),
@@ -100,10 +131,20 @@ export const AdminAuditLogSchema = z.object({
100
131
  userAgent: z.string().nullable(),
101
132
  createdAt: z.string().datetime(),
102
133
  })
103
- export const AuditBackupReceiptSchema = z.object({ id: z.string(), key: z.string(), recordCount: z.number(), sha256: z.string(), createdAt: z.string().datetime(), cutoff: z.string().datetime() })
134
+ export const AuditBackupReceiptSchema = z.object({
135
+ id: z.string(),
136
+ key: z.string(),
137
+ recordCount: z.number(),
138
+ sha256: z.string(),
139
+ createdAt: z.string().datetime(),
140
+ cutoff: z.string().datetime(),
141
+ })
104
142
  export const AdminAuditSchema = z.object({
105
143
  backupEnabled: z.boolean(),
106
- stats: z.array(z.object({ action: z.string(), count: z.number() })),
144
+ stats: z.array(z.object({
145
+ action: z.string(),
146
+ count: z.number(),
147
+ })),
107
148
  items: z.array(AdminAuditLogSchema),
108
149
  total: z.number(),
109
150
  page: z.number(),
@@ -111,7 +152,10 @@ export const AdminAuditSchema = z.object({
111
152
  })
112
153
  export const ProfileUpdateSchema = z.object({
113
154
  displayName: z.string().max(100).optional(),
114
- email: z.union([z.email(), z.literal('')]).optional(),
155
+ email: z.union([
156
+ z.email(),
157
+ z.literal(''),
158
+ ]).optional(),
115
159
  locale: z.enum([{{#each fullstack.i18n.locales}}'{{this}}'{{#unless @last}}, {{/unless}}{{/each}}]).optional(),
116
160
  theme: z.string().min(1).max(40).regex(/^[\w-]+$/).optional(),
117
161
  }).strict()
@@ -120,7 +164,9 @@ export const AvatarUploadSchema = z.object({
120
164
  contentType: z.enum(['image/png', 'image/jpeg', 'image/webp']),
121
165
  base64: z.string().min(1).max(699052).regex(/^[A-Z\d+/]+={0,2}$/i),
122
166
  }).strict()
123
- export const AvatarOutputSchema = z.object({ avatarUrl: z.string().describe('Generated relative asset path') })
167
+ export const AvatarOutputSchema = z.object({
168
+ avatarUrl: z.string().describe('Generated relative asset path'),
169
+ })
124
170
 
125
171
  export type AdminUser = z.infer<typeof AdminUserSchema>
126
172
  export type AdminRole = z.infer<typeof AdminRoleSchema>
@@ -3,7 +3,9 @@ import { defineConfig } from 'tsup'
3
3
  export default defineConfig({
4
4
  entry: ['src/index.ts'],
5
5
  format: ['esm'],
6
- dts: { compilerOptions: { ignoreDeprecations: '6.0' } },
6
+ dts: {
7
+ compilerOptions: { ignoreDeprecations: '6.0' },
8
+ },
7
9
  sourcemap: true,
8
10
  clean: true,
9
11
  })
@@ -1,8 +1,10 @@
1
1
  import type { CoreConfig } from '@h-ai/core'
2
2
  import type { {{#if features.feedback}}CreateServContext, {{/if}}ServHttpApp } from '@h-ai/serv'
3
3
  import process from 'node:process'
4
- {{#if features.feedback}}import { resolveFeedbackBudget } from '@h-ai/api-contract/feedback'
5
- {{/if}}import { core } from '@h-ai/core'
4
+ {{#if features.feedback}}
5
+ import { resolveFeedbackBudget } from '@h-ai/api-contract/feedback'
6
+ {{/if}}
7
+ import { core } from '@h-ai/core'
6
8
  import { createReldbTransportKeyStore, crypto, parseTransportServerKeyRing } from '@h-ai/crypto'
7
9
  {{#if features.feedback}}
8
10
  import { FeedbackConfigSchema } from '@h-ai/feedback'
@@ -25,19 +27,29 @@ export function createServerApp(): ServHttpApp {
25
27
  const ring = parseTransportServerKeyRing(process.env.HAI_CRYPTO_TRANSPORT_SERVERKEYRING)
26
28
  if (!ring.success)
27
29
  throw new Error(`HAI_CRYPTO_TRANSPORT_SERVERKEYRING: ${ring.error.message} (${ring.error.code})`)
28
- {{#if features.feedback}} // 可选反馈故障重试期间仍使用配置预算,避免缓存默认容量。
30
+ {{#if features.feedback}}
31
+ // 可选反馈故障重试期间仍使用配置预算,避免缓存默认容量。
29
32
  const feedbackSettings = FeedbackConfigSchema.safeParse(core.config.get('feedback'))
30
- const feedbackBudget = resolveFeedbackBudget(feedbackSettings.success ? feedbackSettings.data : {})
31
- {{/if}} const security = serv.createRuntimeSecurityPolicy({
33
+ const feedbackBudget = resolveFeedbackBudget(feedbackSettings.success
34
+ ? feedbackSettings.data
35
+ : {})
36
+ {{/if}}
37
+ const security = serv.createRuntimeSecurityPolicy({
32
38
  environment: coreConfig.env,
33
39
  corsOrigin: servConfig.cors.origin,
34
40
  nativeOrigins: servConfig.cors.nativeOrigins,
35
41
  })
36
42
  const publicPaths = [
37
43
  `${servConfig.http.apiPrefix}/assets`,
38
- ...(servConfig.http.health ? [servConfig.http.health.path, servConfig.http.health.readyPath].filter((path): path is `/${string}` => Boolean(path)) : []),
39
- ...(servConfig.http.openapi && security.exposeApiDocs ? [servConfig.http.openapi.path] : []),
40
- ...(servConfig.http.docs && security.exposeApiDocs ? [servConfig.http.docs.path, `${servConfig.http.docs.path.slice(0, servConfig.http.docs.path.lastIndexOf('/') + 1)}_hai/scalar.js`] : []),
44
+ ...(servConfig.http.health
45
+ ? [servConfig.http.health.path, servConfig.http.health.readyPath].filter((path): path is `/${string}` => Boolean(path))
46
+ : []),
47
+ ...(servConfig.http.openapi && security.exposeApiDocs
48
+ ? [servConfig.http.openapi.path]
49
+ : []),
50
+ ...(servConfig.http.docs && security.exposeApiDocs
51
+ ? [servConfig.http.docs.path, `${servConfig.http.docs.path.slice(0, servConfig.http.docs.path.lastIndexOf('/') + 1)}_hai/scalar.js`]
52
+ : []),
41
53
  ]
42
54
  {{#if features.feedback}}
43
55
  // 普通业务日志与反馈使用同一可信应用范围;身份仍由 IAM 验证。
@@ -67,9 +79,11 @@ export function createServerApp(): ServHttpApp {
67
79
  {{/if}}
68
80
  refreshCookie: {
69
81
  secure: security.secureRefreshCookie,
70
- nativeTokenTransport: { isRequest: request => request.headers.get('x-hai-token-transport') === 'body'
71
- && ['{{projectName}}-app', '{{projectName}}-desktop'].includes(request.headers.get('x-request-client') ?? '')
72
- && security.isNativeOrigin(request.headers.get('origin') ?? '') },
82
+ nativeTokenTransport: {
83
+ isRequest: request => request.headers.get('x-hai-token-transport') === 'body'
84
+ && ['{{projectName}}-app', '{{projectName}}-desktop'].includes(request.headers.get('x-request-client') ?? '')
85
+ && security.isNativeOrigin(request.headers.get('origin') ?? ''),
86
+ },
73
87
  },
74
88
  // 管理业务默认强制双向加密;各节点复用受管服务端密钥环与共享 reldb 客户端 registry。
75
89
  transport: {
@@ -83,7 +97,12 @@ export function createServerApp(): ServHttpApp {
83
97
  ...servConfig.transport.routePlaintextBytes,
84
98
  },
85
99
  {{/if}}
86
- excludePaths: [...new Set([...publicPaths, ...servConfig.transport.excludePaths])],
100
+ excludePaths: [
101
+ ...new Set([
102
+ ...publicPaths,
103
+ ...servConfig.transport.excludePaths,
104
+ ]),
105
+ ],
87
106
  crypto,
88
107
  serverKeyRing: ring.data,
89
108
  keyStore: createReldbTransportKeyStore({ reldb, ttlSeconds: 3600 }),
@@ -93,23 +112,39 @@ export function createServerApp(): ServHttpApp {
93
112
  openapi: security.exposeApiDocs ? servConfig.http.openapi : false,
94
113
  docs: security.exposeApiDocs ? servConfig.http.docs : false,
95
114
  },
96
- middlewares: [{
97
- middleware: serv.cors({
98
- origin: security.allowOrigin,
99
- credentials: servConfig.cors.credentials,
100
- allowedHeaders: [...new Set([...servConfig.cors.allowedHeaders, 'X-Client-Id', 'X-Encrypted', 'X-Hai-Transport', 'X-Request-Client', 'X-HAI-Token-Transport', 'X-Hai-Reauthentication', 'X-Hai-Terminal'])],
101
- exposedHeaders: [...new Set([...servConfig.cors.exposedHeaders, 'X-Encrypted', 'X-Hai-Transport'])],
102
- }),
103
- }, {
104
- path: `${servConfig.http.apiPrefix}/assets/*`,
105
- middleware: serv.storageAssets({
106
- storage,
107
- pathPrefix: `${servConfig.http.apiPrefix}/assets/`,
108
- keyPattern: /^avatars\/[\w-]+\/[\w-]+\.(png|jpg|webp)$/,
109
- allowedContentTypes: ['image/png', 'image/jpeg', 'image/webp'],
110
- cacheControl: 'public, max-age=31536000, immutable',
111
- crossOriginResourcePolicy: 'cross-origin',
112
- }),
113
- }],
115
+ middlewares: [
116
+ {
117
+ middleware: serv.cors({
118
+ origin: security.allowOrigin,
119
+ credentials: servConfig.cors.credentials,
120
+ allowedHeaders: [
121
+ ...new Set([
122
+ ...servConfig.cors.allowedHeaders,
123
+ 'X-Client-Id',
124
+ 'X-Encrypted',
125
+ 'X-Hai-Transport',
126
+ 'X-Request-Client',
127
+ 'X-HAI-Token-Transport',
128
+ 'X-Hai-Reauthentication',
129
+ 'X-Hai-Terminal',
130
+ ]),
131
+ ],
132
+ exposedHeaders: [
133
+ ...new Set([...servConfig.cors.exposedHeaders, 'X-Encrypted', 'X-Hai-Transport']),
134
+ ],
135
+ }),
136
+ },
137
+ {
138
+ path: `${servConfig.http.apiPrefix}/assets/*`,
139
+ middleware: serv.storageAssets({
140
+ storage,
141
+ pathPrefix: `${servConfig.http.apiPrefix}/assets/`,
142
+ keyPattern: /^avatars\/[\w-]+\/[\w-]+\.(png|jpg|webp)$/,
143
+ allowedContentTypes: ['image/png', 'image/jpeg', 'image/webp'],
144
+ cacheControl: 'public, max-age=31536000, immutable',
145
+ crossOriginResourcePolicy: 'cross-origin',
146
+ }),
147
+ },
148
+ ],
114
149
  })
115
150
  }
@@ -2,6 +2,5 @@
2
2
  {{#if features.feedback}}
3
3
  /** 当前应用编译时烘焙的产物身份;运行时配置不能覆盖。 */
4
4
  declare const __HAI_BUILD_METADATA__: { appVersion: string, buildId: string, commit: string }
5
-
6
5
  export const buildMetadata = __HAI_BUILD_METADATA__
7
6
  {{/if}}
@@ -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>(['admin', 'audit_admin', 'config_admin', 'business_operator', 'user'])
15
- const SUPPORTED_THEMES = [{{#each fullstack.theme.themes}}'{{this}}'{{#unless @last}}, {{/unless}}{{/each}}] as readonly string[]
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', { owner, error: renewed.success ? 'lease_not_owned' : renewed.error.code })
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.') ? 'app.roles.manage' : 'app.users.manage')
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', { owner, error: released.success ? 'lease_not_owned' : released.error.code })
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({ ...input, accountExpiresAt: input.accountExpiresAt ? new Date(input.accountExpiresAt) : undefined })
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 ? err(HaiCommonError.NOT_FOUND, serverM('notFound', { locale: context.locale })) : role
144
- const created = await iam.user.inviteUser({ ...input, accountExpiresAt: input.accountExpiresAt ? new Date(input.accountExpiresAt) : undefined })
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 ? await iam.authz.getRoleByCode(input.role) : undefined
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, { ...profile, accountExpiresAt: profile.accountExpiresAt === undefined ? undefined : profile.accountExpiresAt ? new Date(profile.accountExpiresAt) : null })
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, { fields: Object.keys(profile), role: input.role })
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({ items: items.map(role => ({
205
- id: role.id,
206
- code: role.code as AdminRoleCode,
207
- name: role.name,
208
- description: role.description,
209
- userCount: counts.data.get(role.id) ?? 0,
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, { fields: Object.keys(input) })
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
 
@@ -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({ notice: z.string().trim().max(200) }).strict()
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', { id: { type: 'TEXT', primaryKey: true }, notice: { type: 'TEXT', notNull: true } })
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 ? ok({ notice: row.data?.notice ?? '' }) : row
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({ action: 'business.configuration.update', resource: 'business-settings', category: 'business', details: { fields: ['notice'] } }, { tx })
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 ? ok(parsed.data) : committed
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 ? ok({ total: result.data.total, items: result.data.items.map(item => ({ id: item.id, code: item.code, name: item.name, sensitiveOperations: item.sensitiveOperations ?? [] })) }) : result
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 ? ok({ saved: true as const }) : result
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 ? saveBusinessSettings(input) : checked
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({ ...users.data, items: users.data.items.map(user => toAdminUser(user, roles.data.get(user.id) ?? [])) })
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, { fields: ['description'] })
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 ? ok({ ...result.data, createdAt: result.data.createdAt.toISOString(), cutoff: result.data.cutoff.toISOString() }) : result
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({ ...input, startDate: input.startDate ? new Date(input.startDate) : undefined, endDate: input.endDate ? new Date(input.endDate) : undefined })
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, { category: input.category, outcome: input.outcome, severity: input.severity, resource: input.resource, scopeId: input.scopeId, userId: input.userId })
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({ ...result.data, stats: stats.data, backupEnabled: AuditInitConfigSchema.parse(core.config.get('audit') ?? {}).backup.enabled, items: result.data.items.map(item => ({ ...item, createdAt: new Date(item.createdAt).toISOString() })) })
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 ? updateProfile(input, context) : checked
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 ? uploadAvatar(context.session.userId, input, context) : checked
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 ? ok({ ...createAppInfo(options), notice: settings.data.notice }) : settings
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 = {
@@ -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: { auth: createAuthProcedures() },
20
+ iam: {
21
+ auth: createAuthProcedures(),
22
+ },
21
23
  admin: createAdminProcedures(),
22
24
  profile: createProfileProcedures(),
23
25
  {{#if features.feedback}}