@h-ai/cli 0.1.0-alpha5

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 (109) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +167 -0
  3. package/dist/chunk-2H7UOFLK.js +10 -0
  4. package/dist/chunk-2H7UOFLK.js.map +1 -0
  5. package/dist/chunk-LANIDJC7.js +659 -0
  6. package/dist/chunk-LANIDJC7.js.map +1 -0
  7. package/dist/chunk-YV7WPUI6.js +206 -0
  8. package/dist/chunk-YV7WPUI6.js.map +1 -0
  9. package/dist/config-templates-S5RI2MC6.js +5 -0
  10. package/dist/config-templates-S5RI2MC6.js.map +1 -0
  11. package/dist/index.d.ts +2 -0
  12. package/dist/index.js +1645 -0
  13. package/dist/index.js.map +1 -0
  14. package/dist/skill-templates-N5DU5FNX.js +5 -0
  15. package/dist/skill-templates-N5DU5FNX.js.map +1 -0
  16. package/dist/src-XJ6YXNAF.js +15030 -0
  17. package/dist/src-XJ6YXNAF.js.map +1 -0
  18. package/package.json +59 -0
  19. package/templates/README.md +126 -0
  20. package/templates/apps/admin/messages/en-US.json +33 -0
  21. package/templates/apps/admin/messages/zh-CN.json +33 -0
  22. package/templates/apps/admin/src/routes/+layout.svelte.hbs +30 -0
  23. package/templates/apps/admin/src/routes/+page.svelte.hbs +35 -0
  24. package/templates/apps/admin/src/routes/admin/+layout.svelte.hbs +44 -0
  25. package/templates/apps/admin/src/routes/admin/+page.svelte.hbs +45 -0
  26. package/templates/apps/admin/src/routes/admin/settings/+page.svelte.hbs +37 -0
  27. package/templates/apps/admin/src/routes/api/health/+server.ts +12 -0
  28. package/templates/apps/android-app/.env.hbs +3 -0
  29. package/templates/apps/android-app/capacitor.config.ts.hbs +12 -0
  30. package/templates/apps/android-app/messages/en-US.json +24 -0
  31. package/templates/apps/android-app/messages/zh-CN.json +24 -0
  32. package/templates/apps/android-app/src/lib/api.ts.hbs +30 -0
  33. package/templates/apps/android-app/src/lib/capacitor.ts +21 -0
  34. package/templates/apps/android-app/src/routes/+layout.svelte.hbs +42 -0
  35. package/templates/apps/android-app/src/routes/+layout.ts +7 -0
  36. package/templates/apps/android-app/src/routes/+page.svelte.hbs +27 -0
  37. package/templates/apps/api/src/routes/+layout.svelte +14 -0
  38. package/templates/apps/api/src/routes/+page.svelte +11 -0
  39. package/templates/apps/api/src/routes/api/v1/health/+server.ts +14 -0
  40. package/templates/apps/api/src/routes/api/v1/items/+server.ts +37 -0
  41. package/templates/apps/api/src/routes/api/v1/items/[id]/+server.ts +30 -0
  42. package/templates/apps/h5/messages/en-US.json +37 -0
  43. package/templates/apps/h5/messages/zh-CN.json +37 -0
  44. package/templates/apps/h5/src/routes/+layout.svelte.hbs +57 -0
  45. package/templates/apps/h5/src/routes/+page.svelte.hbs +27 -0
  46. package/templates/apps/h5/src/routes/discover/+page.svelte.hbs +27 -0
  47. package/templates/apps/h5/src/routes/profile/+page.svelte.hbs +27 -0
  48. package/templates/apps/website/messages/en-US.json +32 -0
  49. package/templates/apps/website/messages/zh-CN.json +32 -0
  50. package/templates/apps/website/src/routes/+layout.svelte.hbs +66 -0
  51. package/templates/apps/website/src/routes/+page.svelte.hbs +33 -0
  52. package/templates/apps/website/src/routes/about/+page.svelte.hbs +27 -0
  53. package/templates/apps/website/src/routes/api/health/+server.ts +12 -0
  54. package/templates/apps/website/src/routes/contact/+page.svelte.hbs +59 -0
  55. package/templates/apps/website/src/routes/services/+page.svelte.hbs +63 -0
  56. package/templates/base/e2e/helpers.ts +49 -0
  57. package/templates/base/package.json.hbs +67 -0
  58. package/templates/base/playwright.config.ts +32 -0
  59. package/templates/base/src/app.css +2 -0
  60. package/templates/base/src/app.d.ts.hbs +18 -0
  61. package/templates/base/src/app.html.hbs +16 -0
  62. package/templates/base/src/hooks.server.ts.hbs +123 -0
  63. package/templates/base/src/lib/server/init.ts.hbs +190 -0
  64. package/templates/base/svelte.config.js.hbs +30 -0
  65. package/templates/base/tsconfig.json +8 -0
  66. package/templates/base/vite.config.ts.hbs +27 -0
  67. package/templates/features/ai/routes/api/chat/+server.ts +26 -0
  68. package/templates/features/datapipe/routes/api/datapipe/pipeline/+server.ts +41 -0
  69. package/templates/features/iam/routes-admin/(auth)/+layout.svelte.hbs +21 -0
  70. package/templates/features/iam/routes-admin/(auth)/auth/login/+page.svelte.hbs +98 -0
  71. package/templates/features/iam/routes-admin/(auth)/auth/register/+page.svelte.hbs +124 -0
  72. package/templates/features/iam/routes-h5/auth/login/+page.svelte.hbs +98 -0
  73. package/templates/features/iam/routes-h5/auth/register/+page.svelte.hbs +114 -0
  74. package/templates/features/iam/routes-shared/api/auth/login/+server.ts +32 -0
  75. package/templates/features/iam/routes-shared/api/auth/logout/+server.ts +15 -0
  76. package/templates/features/iam/routes-shared/api/auth/me/+server.ts +12 -0
  77. package/templates/features/iam/routes-shared/api/auth/register/+server.ts +26 -0
  78. package/templates/features/payment/routes/api/v1/payment/create/+server.ts +30 -0
  79. package/templates/features/payment/routes/api/v1/payment/notify/[provider]/+server.ts +42 -0
  80. package/templates/features/payment/routes/api/v1/payment/query/[orderNo]/+server.ts +25 -0
  81. package/templates/features/storage/routes/api/upload/+server.ts +35 -0
  82. package/templates/features/vecdb/routes/api/vecdb/search/+server.ts +36 -0
  83. package/templates/i18n/project.inlang/settings.json +11 -0
  84. package/templates/skills/AGENTS.md +26 -0
  85. package/templates/skills/CLAUDE.md +12 -0
  86. package/templates/skills/copilot-instructions.md +61 -0
  87. package/templates/skills/hai-ai/SKILL.md +659 -0
  88. package/templates/skills/hai-api-client/SKILL.md +346 -0
  89. package/templates/skills/hai-app-create/SKILL.md +371 -0
  90. package/templates/skills/hai-app-review/SKILL.md +156 -0
  91. package/templates/skills/hai-app-tests/SKILL.md +468 -0
  92. package/templates/skills/hai-audit/SKILL.md +141 -0
  93. package/templates/skills/hai-build/SKILL.md +319 -0
  94. package/templates/skills/hai-cache/SKILL.md +184 -0
  95. package/templates/skills/hai-capacitor/SKILL.md +389 -0
  96. package/templates/skills/hai-core/SKILL.md +544 -0
  97. package/templates/skills/hai-crypto/SKILL.md +183 -0
  98. package/templates/skills/hai-datapipe/SKILL.md +214 -0
  99. package/templates/skills/hai-deploy/SKILL.md +210 -0
  100. package/templates/skills/hai-iam/SKILL.md +355 -0
  101. package/templates/skills/hai-kit/SKILL.md +319 -0
  102. package/templates/skills/hai-payment/SKILL.md +289 -0
  103. package/templates/skills/hai-reach/SKILL.md +325 -0
  104. package/templates/skills/hai-reldb/SKILL.md +459 -0
  105. package/templates/skills/hai-scheduler/SKILL.md +242 -0
  106. package/templates/skills/hai-storage/SKILL.md +230 -0
  107. package/templates/skills/hai-ui/SKILL.md +275 -0
  108. package/templates/skills/hai-vecdb/SKILL.md +273 -0
  109. package/templates/skills/opencode.json +11 -0
@@ -0,0 +1,355 @@
1
+ ---
2
+ name: hai-iam
3
+ description: 使用 @h-ai/iam 进行身份认证(密码/OTP/LDAP/API Key)、统一 Bearer Token 管理(TokenPair/refresh/revoke)与 RBAC 授权(角色/权限/通配符);当需求涉及登录、注册、权限检查、角色管理、Token 认证或 API 契约时使用。
4
+ ---
5
+
6
+ # hai-iam
7
+
8
+ > `@h-ai/iam` 是统一的身份与访问管理模块,支持多种认证策略(密码/OTP/LDAP/API Key)、Bearer Token 管理和 RBAC 授权。依赖 `@h-ai/reldb`(持久化)、`@h-ai/cache`(Token + 权限缓存)、`@h-ai/crypto`(密码哈希)、`@h-ai/audit`(审计日志)。
9
+
10
+ ---
11
+
12
+ ## 依赖
13
+
14
+ | 模块 | 用途 | 是否必需 | 初始化要求 |
15
+ | --- | --- | --- | --- |
16
+ | `@h-ai/reldb` | 数据库(用户/角色/权限持久化) | **必需** | 需在 `iam.init()` 前初始化 |
17
+ | `@h-ai/cache` | 缓存(会话/OTP/重置令牌/权限缓存) | **必需** | 需在 `iam.init()` 前初始化 |
18
+ | `@h-ai/crypto` | 密码哈希 | 内部使用 | 自动初始化 |
19
+ | `@h-ai/audit` | 审计日志(RBAC 关键操作记录) | 内部使用 | 已初始化时自动写入 |
20
+
21
+ ## 适用场景
22
+
23
+ - 用户注册与登录(密码/OTP/LDAP/API Key)
24
+ - Bearer Token 认证与管理(TokenPair: accessToken + refreshToken)
25
+ - Token 刷新(rotation 策略)与吊销
26
+ - RBAC 角色/权限的创建、分配与检查
27
+ - API Key 管理(创建、吊销、验证)
28
+ - 密码重置流程
29
+ - 与 kit 集成的认证守卫
30
+ - API 契约定义(`@h-ai/iam/api`)
31
+
32
+ ---
33
+
34
+ ## 使用步骤
35
+
36
+ ### 1. 配置
37
+
38
+ ```yaml
39
+ # config/_iam.yml
40
+ password:
41
+ minLength: ${HAI_IAM_PASSWORD_MIN_LENGTH:8}
42
+ requireUppercase: true
43
+ requireNumber: true
44
+ session:
45
+ maxAge: ${HAI_IAM_SESSION_MAX_AGE:86400}
46
+ refreshTokenMaxAge: ${HAI_IAM_REFRESH_TOKEN_MAX_AGE:604800} # 7 天
47
+ sliding: true
48
+ singleDevice: false
49
+ login:
50
+ password: true
51
+ otp: false
52
+ register:
53
+ enabled: true
54
+ security:
55
+ maxLoginAttempts: ${HAI_IAM_MAX_LOGIN_ATTEMPTS:5}
56
+ lockoutDuration: 900
57
+ rbac:
58
+ defaultRole: user
59
+ superAdminRole: super_admin
60
+ ```
61
+
62
+ ### 2. 初始化
63
+
64
+ ```typescript
65
+ import { cache } from '@h-ai/cache'
66
+ import { reldb } from '@h-ai/reldb'
67
+ import { iam } from '@h-ai/iam'
68
+
69
+ // 必须先初始化 reldb 和 cache
70
+ await reldb.init(core.config.get('db'))
71
+ await cache.init(core.config.get('cache'))
72
+
73
+ // IAM 自动使用已初始化的 reldb 和 cache 单例
74
+ await iam.init({
75
+ ...core.config.get('iam'),
76
+ seedDefaultData: true,
77
+ })
78
+ ```
79
+
80
+ 初始化时自动创建数据库表(5 张):`hai_iam_users`、`hai_iam_roles`、`hai_iam_permissions`、`hai_iam_role_permissions`、`hai_iam_user_roles`。
81
+
82
+ Token 和 OTP 存储在 cache 中(不落库)。refreshToken 使用独立的 cache key 存储。
83
+
84
+ ---
85
+
86
+ ## 核心 API
87
+
88
+ ### 认证 — `iam.auth`
89
+
90
+ | 方法 | 签名 | 说明 |
91
+ | ------------------ | ----------------------------------------------------------- | ---------------------------------- |
92
+ | `login` | `({ identifier, password }) => Promise<HaiResult<AuthResult>>` | 密码登录(用户名/邮箱/手机号) |
93
+ | `loginWithOtp` | `({ identifier, code }) => Promise<HaiResult<AuthResult>>` | OTP 验证码登录 |
94
+ | `loginWithLdap` | `({ username, password }) => Promise<HaiResult<AuthResult>>` | LDAP 登录 |
95
+ | `loginWithApiKey` | `({ key }) => Promise<HaiResult<AuthResult>>` | API Key 登录 |
96
+ | `sendOtp` | `(identifier) => Promise<HaiResult<{ expiresAt }>>` | 发送 OTP 验证码 |
97
+ | `verifyToken` | `(token) => Promise<HaiResult<Session>>` | 验证令牌(推荐入口,委托 session) |
98
+ | `logout` | `(token) => Promise<HaiResult<void>>` | 登出 |
99
+ | `registerAndLogin` | `(options: RegisterOptions) => Promise<HaiResult<AuthResult>>` | 注册并登录(一站式) |
100
+
101
+ **AuthResult**(已改造为 TokenPair):
102
+
103
+ ```typescript
104
+ interface AuthResult {
105
+ user: User
106
+ tokens: TokenPair
107
+ roles: string[]
108
+ permissions: string[]
109
+ agreements?: AgreementDisplay
110
+ }
111
+
112
+ interface TokenPair {
113
+ accessToken: string
114
+ refreshToken: string
115
+ expiresIn: number
116
+ tokenType: 'Bearer'
117
+ }
118
+ ```
119
+
120
+ ### 会话管理 — `iam.session`
121
+
122
+ | 方法 | 签名 | 说明 |
123
+ | ------------------- | -------------------------------------------------------------------- | --------------------------------------- |
124
+ | `create` | `({ userId, username, roles, source? }) => Promise<HaiResult<Session>>` | 创建会话(返回含 TokenPair 的 session) |
125
+ | `get` | `(token) => Promise<HaiResult<Session \| null>>` | 获取会话(滑动续期自动延长) |
126
+ | `verifyToken` | `(token) => Promise<HaiResult<Session>>` | 验证令牌 |
127
+ | `update` | `(token, data) => Promise<HaiResult<void>>` | 更新会话字段(浅合并 data) |
128
+ | `delete` | `(token) => Promise<HaiResult<void>>` | 删除会话 |
129
+ | `deleteByUserId` | `(userId) => Promise<HaiResult<number>>` | 删除用户所有会话(返回删除数量) |
130
+ | `refresh` | `(refreshToken) => Promise<HaiResult<TokenPair>>` | 刷新 Token(rotation:旧 Token 失效) |
131
+ | `revokeRefresh` | `(refreshToken) => Promise<HaiResult<void>>` | 吊销 refreshToken |
132
+ | `patchUserSessions` | `(userId, updates: SessionFieldUpdates) => Promise<HaiResult<void>>` | 批量更新用户所有会话的 roles/permissions |
133
+
134
+ **Token 刷新(rotation 策略)**:
135
+
136
+ - `refresh()` 会删除旧的 accessToken + refreshToken
137
+ - 创建全新的 session,返回新的 TokenPair
138
+ - 旧 refreshToken 立即失效,防止重放攻击
139
+
140
+ ### 用户管理 — `iam.user`
141
+
142
+ | 方法 | 签名 | 说明 |
143
+ | -------------------------- | ----------------------------------------------------------------------- | ------------------------------------ |
144
+ | `register` | `(input) => Promise<HaiResult<RegisterResult>>` | 用户注册 |
145
+ | `getUser` | `(id, options?) => Promise<HaiResult<User \| null>>` | 获取用户(可选 include: ['roles']) |
146
+ | `getCurrentUser` | `(token) => Promise<HaiResult<User>>` | 通过令牌获取当前用户 |
147
+ | `updateCurrentUser` | `(token, data: UpdateCurrentUserInput) => Promise<HaiResult<User>>` | 当前用户修改个人资料(白名单字段) |
148
+ | `listUsers` | `(options?: ListUsersOptions) => Promise<HaiResult<PaginatedResult<User>>>` | 用户列表(分页 + 搜索 + 过滤) |
149
+ | `updateUser` | `(id, data) => Promise<HaiResult<User>>` | 更新用户信息 |
150
+ | `deleteUser` | `(id) => Promise<HaiResult<void>>` | 删除用户(自动清除会话) |
151
+ | `changePassword` | `(id, old, new) => Promise<HaiResult<void>>` | 修改密码(自动清除会话) |
152
+ | `changeCurrentUserPassword`| `(token, old, new) => Promise<HaiResult<void>>` | 当前用户修改密码(通过令牌识别) |
153
+ | `requestPasswordReset` | `(email) => Promise<HaiResult<void>>` | 请求密码重置(防枚举) |
154
+ | `confirmPasswordReset` | `(token, newPassword) => Promise<HaiResult<void>>` | 确认密码重置(自动清除会话) |
155
+ | `adminResetPassword` | `(userId, newPassword) => Promise<HaiResult<void>>` | 管理员直接重置密码 |
156
+ | `validatePassword` | `(password) => HaiResult<void>` | 验证密码强度(**同步方法**) |
157
+
158
+ ### RBAC 授权 — `iam.authz`
159
+
160
+ | 方法 | 签名 | 说明 |
161
+ | -------------------------- | -------------------------------------------------------------------------------- | -------------------------------------------- |
162
+ | `checkPermission` | `(userId, permission) => Promise<HaiResult<boolean>>` | 检查权限(超管自动通过,支持通配符) |
163
+ | `getUserPermissions` | `(userId) => Promise<HaiResult<Permission[]>>` | 获取用户权限列表(通过角色聚合去重) |
164
+ | `getUserRoles` | `(userId) => Promise<HaiResult<Role[]>>` | 获取用户角色列表 |
165
+ | `getUserRolesForMany` | `(userIds) => Promise<HaiResult<Map<string, Role[]>>>` | 批量获取多用户角色(避免 N+1) |
166
+ | `assignRole` | `(userId, roleId, tx?) => Promise<HaiResult<void>>` | 分配角色给用户 |
167
+ | `removeRole` | `(userId, roleId, tx?) => Promise<HaiResult<void>>` | 移除用户角色 |
168
+ | `syncRoles` | `(userId, roleIds, tx?) => Promise<HaiResult<void>>` | 同步用户角色(替换为目标列表) |
169
+ | `createRole` | `({ code, name, description? }, tx?) => Promise<HaiResult<Role>>` | 创建角色 |
170
+ | `getRole` | `(roleId) => Promise<HaiResult<Role \| null>>` | 获取角色 |
171
+ | `getRoleByCode` | `(code) => Promise<HaiResult<Role \| null>>` | 根据代码获取角色 |
172
+ | `getAllRoles` | `(options?) => Promise<HaiResult<PaginatedResult<Role>>>` | 获取所有角色(分页) |
173
+ | `updateRole` | `(roleId, data, tx?) => Promise<HaiResult<Role>>` | 更新角色 |
174
+ | `deleteRole` | `(roleId, tx?) => Promise<HaiResult<void>>` | 删除角色(级联清理关联 + 会话同步) |
175
+ | `createPermission` | `({ code, name, type?, resource?, action? }, tx?) => Promise<HaiResult<Permission>>`| 创建权限 |
176
+ | `getPermission` | `(permissionId) => Promise<HaiResult<Permission \| null>>` | 获取权限 |
177
+ | `getPermissionByCode` | `(code) => Promise<HaiResult<Permission \| null>>` | 根据代码获取权限 |
178
+ | `getAllPermissions` | `(options?: PermissionQueryOptions) => Promise<HaiResult<PaginatedResult<Permission>>>` | 获取所有权限(分页 + 类型/关键字筛选) |
179
+ | `deletePermission` | `(permissionId, tx?) => Promise<HaiResult<void>>` | 删除权限(级联清理关联 + 会话同步) |
180
+ | `assignPermissionToRole` | `(roleId, permId, tx?) => Promise<HaiResult<void>>` | 分配权限给角色 |
181
+ | `removePermissionFromRole` | `(roleId, permId, tx?) => Promise<HaiResult<void>>` | 移除角色权限 |
182
+ | `getRolePermissions` | `(roleId) => Promise<HaiResult<Permission[]>>` | 获取角色的权限列表 |
183
+ | `getRolePermissionsForMany`| `(roleIds) => Promise<HaiResult<Map<string, Permission[]>>>` | 批量获取多角色权限(避免 N+1) |
184
+
185
+ **通配符规则**:`admin:*` 匹配 `admin:read`、`admin:write` 等。超管角色自动拥有所有权限。
186
+
187
+ ### API Key 管理 — `iam.apiKey`
188
+
189
+ | 方法 | 签名 | 说明 |
190
+ | -------------- | ----------------------------------------------------------------------- | -------------------------------------- |
191
+ | `createApiKey` | `(userId, options: CreateApiKeyOptions) => Promise<HaiResult<CreateApiKeyResult>>` | 创建 API Key(明文密钥仅返回一次) |
192
+ | `listApiKeys` | `(userId) => Promise<HaiResult<ApiKey[]>>` | 列出用户的所有 API Key |
193
+ | `getApiKey` | `(keyId) => Promise<HaiResult<ApiKey \| null>>` | 获取 API Key 详情 |
194
+ | `revokeApiKey` | `(keyId) => Promise<HaiResult<void>>` | 吊销/删除 API Key |
195
+ | `verifyApiKey` | `(rawKey) => Promise<HaiResult<ApiKey>>` | 验证 API Key 并返回实体(含用户 ID) |
196
+
197
+ > 需在 `login.apikey: true` 时才会初始化此子模块,否则访问返回 `NOT_INITIALIZED` 错误。
198
+
199
+ ---
200
+
201
+ ## API 契约 — `@h-ai/iam/api`
202
+
203
+ ```typescript
204
+ import { iamEndpoints } from '@h-ai/iam/api'
205
+
206
+ // 9 个端点契约,含 Zod schema
207
+ iamEndpoints.login // POST /api/v1/auth/login
208
+ iamEndpoints.loginWithOtp // POST /api/v1/auth/login-otp
209
+ iamEndpoints.sendOtp // POST /api/v1/auth/send-otp
210
+ iamEndpoints.logout // POST /api/v1/auth/logout
211
+ iamEndpoints.register // POST /api/v1/auth/register
212
+ iamEndpoints.currentUser // GET /api/v1/auth/me
213
+ iamEndpoints.refreshToken // POST /api/v1/auth/refresh
214
+ iamEndpoints.changePassword // POST /api/v1/auth/change-password
215
+ iamEndpoints.resetPassword // POST /api/v1/auth/reset-password
216
+ ```
217
+
218
+ 服务端使用 `kit.fromContract()`,客户端使用 `api.call()`:
219
+
220
+ ```typescript
221
+ // 服务端
222
+ export const POST = kit.fromContract(iamEndpoints.login, async (input) => {
223
+ const result = await iam.auth.login(input)
224
+ return result.success ? kit.response.ok(result.data) : kit.response.unauthorized()
225
+ })
226
+
227
+ // 客户端
228
+ const result = await api.call(iamEndpoints.login, { username, password })
229
+ ```
230
+
231
+ ---
232
+
233
+ ## 错误码 — `HaiIamError`
234
+
235
+ | 错误码 | code | 说明 |
236
+ |--------|------|------|
237
+ | `HaiIamError.AUTH_FAILED` | `hai:iam:001` | 认证失败 |
238
+ | `HaiIamError.INVALID_CREDENTIALS` | `hai:iam:002` | 凭证无效 |
239
+ | `HaiIamError.USER_NOT_FOUND` | `hai:iam:003` | 用户不存在 |
240
+ | `HaiIamError.USER_DISABLED` | `hai:iam:004` | 用户已禁用 |
241
+ | `HaiIamError.USER_LOCKED` | `hai:iam:005` | 用户已锁定 |
242
+ | `HaiIamError.USER_ALREADY_EXISTS` | `hai:iam:006` | 用户已存在 |
243
+ | `HaiIamError.PASSWORD_EXPIRED` | `hai:iam:007` | 密码已过期 |
244
+ | `HaiIamError.PASSWORD_POLICY_VIOLATION` | `hai:iam:008` | 密码不符合策略 |
245
+ | `HaiIamError.OTP_INVALID` | `hai:iam:009` | 验证码无效 |
246
+ | `HaiIamError.OTP_EXPIRED` | `hai:iam:010` | 验证码已过期 |
247
+ | `HaiIamError.OTP_RESEND_TOO_FAST` | `hai:iam:011` | 发送过于频繁 |
248
+ | `HaiIamError.LOGIN_DISABLED` | `hai:iam:012` | 登录方式已禁用 |
249
+ | `HaiIamError.REGISTER_DISABLED` | `hai:iam:013` | 注册已禁用 |
250
+ | `HaiIamError.STRATEGY_NOT_SUPPORTED` | `hai:iam:014` | 认证策略不支持 |
251
+ | `HaiIamError.APIKEY_INVALID` | `hai:iam:015` | API Key 无效 |
252
+ | `HaiIamError.APIKEY_EXPIRED` | `hai:iam:016` | API Key 已过期 |
253
+ | `HaiIamError.APIKEY_DISABLED` | `hai:iam:017` | API Key 已禁用 |
254
+ | `HaiIamError.APIKEY_NOT_FOUND` | `hai:iam:018` | API Key 不存在 |
255
+ | `HaiIamError.RESET_TOKEN_INVALID` | `hai:iam:019` | 重置令牌无效 |
256
+ | `HaiIamError.RESET_TOKEN_EXPIRED` | `hai:iam:020` | 重置令牌已过期 |
257
+ | `HaiIamError.RESET_TOKEN_MAX_ATTEMPTS` | `hai:iam:021` | 重置验证次数超限 |
258
+ | `HaiIamError.SESSION_NOT_FOUND` | `hai:iam:101` | 会话不存在 |
259
+ | `HaiIamError.SESSION_EXPIRED` | `hai:iam:102` | 会话已过期 |
260
+ | `HaiIamError.SESSION_INVALID` | `hai:iam:103` | 会话无效 |
261
+ | `HaiIamError.SESSION_CREATE_FAILED` | `hai:iam:104` | 会话创建失败 |
262
+ | `HaiIamError.TOKEN_EXPIRED` | `hai:iam:105` | 令牌已过期 |
263
+ | `HaiIamError.TOKEN_INVALID` | `hai:iam:106` | 令牌无效 |
264
+ | `HaiIamError.TOKEN_REFRESH_FAILED` | `hai:iam:107` | 令牌刷新失败 |
265
+ | `HaiIamError.PERMISSION_DENIED` | `hai:iam:201` | 权限不足 |
266
+ | `HaiIamError.ROLE_NOT_FOUND` | `hai:iam:202` | 角色不存在 |
267
+ | `HaiIamError.PERMISSION_NOT_FOUND` | `hai:iam:203` | 权限不存在 |
268
+ | `HaiIamError.ROLE_ALREADY_EXISTS` | `hai:iam:204` | 角色已存在 |
269
+ | `HaiIamError.PERMISSION_ALREADY_EXISTS` | `hai:iam:205` | 权限已存在 |
270
+ | `HaiIamError.LDAP_CONNECTION_FAILED` | `hai:iam:301` | LDAP 连接失败 |
271
+ | `HaiIamError.LDAP_BIND_FAILED` | `hai:iam:302` | LDAP 绑定失败 |
272
+ | `HaiIamError.LDAP_SEARCH_FAILED` | `hai:iam:303` | LDAP 搜索失败 |
273
+ | `HaiIamError.REPOSITORY_ERROR` | `hai:iam:401` | 存储层操作错误 |
274
+ | `HaiIamError.NOT_FOUND` | `hai:iam:402` | 资源不存在 |
275
+ | `HaiIamError.CONFLICT` | `hai:iam:403` | 资源冲突 |
276
+ | `HaiIamError.FORBIDDEN` | `hai:iam:501` | 禁止访问 |
277
+ | `HaiIamError.INVALID_ARGUMENT` | `hai:iam:502` | 参数无效 |
278
+ | `HaiIamError.CONFIG_ERROR` | `hai:iam:901` | 配置错误 |
279
+ | `HaiIamError.NOT_INITIALIZED` | `hai:iam:910` | 未初始化 |
280
+ | `HaiIamError.INTERNAL_ERROR` | `hai:iam:999` | 内部错误 |
281
+
282
+ ---
283
+
284
+ ## 常见模式
285
+
286
+ ### 与 kit 集成(hooks.server.ts)
287
+
288
+ ```typescript
289
+ import { iam } from '$lib/server/init'
290
+ import { kit } from '@h-ai/kit'
291
+
292
+ const haiHandle = kit.createHandle({
293
+ validateSession: async (token) => {
294
+ const result = await iam.auth.verifyToken(token)
295
+ if (!result.success)
296
+ return null
297
+ const roles = await iam.authz.getUserRoles(result.data.userId)
298
+ return {
299
+ userId: result.data.userId,
300
+ roles: roles.success ? roles.data.map(r => r.code) : [],
301
+ permissions: [],
302
+ }
303
+ },
304
+ guards: [
305
+ {
306
+ guard: kit.guard.auth({ apiMode: true }),
307
+ paths: ['/api/*'],
308
+ exclude: ['/api/auth/*'],
309
+ },
310
+ ],
311
+ })
312
+
313
+ export const handle = kit.sequence(haiHandle)
314
+ ```
315
+
316
+ ### 客户端认证流程
317
+
318
+ ```typescript
319
+ import { iamEndpoints } from '@h-ai/iam/api'
320
+
321
+ // 登录 → 保存 Token
322
+ const login = await api.call(iamEndpoints.login, { username, password })
323
+ if (login.success) {
324
+ await api.auth.setTokens(login.data.tokens)
325
+ }
326
+
327
+ // 获取当前用户
328
+ const me = await api.call(iamEndpoints.currentUser, {})
329
+
330
+ // Token 过期时,api-client 自动调用 refreshUrl 刷新
331
+ // 刷新失败时,清除 Token 并跳转登录页
332
+
333
+ // 登出
334
+ await api.call(iamEndpoints.logout, {})
335
+ await api.auth.clear()
336
+ ```
337
+
338
+ ### 自动会话失效
339
+
340
+ 以下操作自动清除受影响用户的所有活跃会话:
341
+
342
+ - `deleteUser` / `updateUser({ enabled: false })` / `changePassword` / `confirmPasswordReset`
343
+ - `assignRole` / `removeRole` / `deleteRole` 会同步更新活跃会话中的角色列表
344
+
345
+ ---
346
+
347
+ ## 相关 Skills
348
+
349
+ - `hai-build`:模块初始化顺序(db → cache → iam)
350
+ - `hai-kit`:SvelteKit 集成(Bearer Token + 契约处理)
351
+ - `hai-api-client`:客户端契约调用
352
+ - `hai-reldb`:底层数据存储
353
+ - `hai-cache`:Token 与权限缓存
354
+ - `hai-crypto`:密码哈希(内部依赖)
355
+ - `hai-ui`:IAM 场景组件(LoginForm/RegisterForm 等)
@@ -0,0 +1,319 @@
1
+ ---
2
+ name: hai-kit
3
+ description: 使用 @h-ai/kit 构建 SvelteKit 请求管道与 API 安全边界(handle/guard/middleware/handler/validate/response/fromContract)。当需求涉及 hooks.server.ts、权限守卫、限流、统一响应、Bearer Token 认证、契约处理或 CORS 配置时使用。
4
+ ---
5
+
6
+ # hai-kit
7
+
8
+ `@h-ai/kit` 是 hai-framework 的 SvelteKit 集成层。它把请求处理中的"通用横切能力"收敛到统一命名空间 `kit`:
9
+
10
+ - 请求入口:`createHandle` / `sequence`
11
+ - 安全边界:`guard` / `middleware`
12
+ - API 工具:`handler` / `response` / `validate`
13
+ - Bearer Token 认证:从 `Authorization` header 解析,SSR 端通过 `hai_token` Cookie 透传
14
+ - 契约处理:`fromContract(endpoint, fn)` — 基于 EndpointDef 的类型安全 handler
15
+ - 双构建模式:`createAdapter()` 根据环境变量选择 adapter-node 或 adapter-static
16
+
17
+ ## 模块概述
18
+
19
+ 当任务涉及以下关键词时,优先使用本 Skill:
20
+
21
+ - `hooks.server.ts`
22
+ - `kit.createHandle` / `kit.sequence`
23
+ - `kit.guard.*`(`require` / `check`)
24
+ - `kit.handler` / `kit.response` / `kit.validate`
25
+ - `kit.fromContract(endpoint, fn)` — 契约到 SvelteKit handler
26
+ - Bearer Token / `Authorization` header
27
+ - CORS 配置(含 Capacitor Origin 预设)
28
+ - 传输加密(key exchange / `X-Client-Id`)
29
+
30
+ > **重要变更**:Cookie Session 和 CSRF 中间件已移除。统一使用 Bearer Token 认证。
31
+
32
+ ## 依赖
33
+
34
+ | 模块 | 用途 | 是否必需 | 初始化要求 |
35
+ | --- | --- | --- | --- |
36
+ | `@h-ai/iam` | 身份认证与授权(A2A 认证) | 可选 | 使用 A2A 功能时需在 kit 使用前初始化 |
37
+ | `@h-ai/crypto` | 加密(传输加密 / Cookie 加密) | 可选 | 启用加密时需在 kit 使用前初始化 |
38
+
39
+ ## 使用步骤
40
+
41
+ ### 1) 在 `hooks.server.ts` 搭建请求管道
42
+
43
+ ```typescript
44
+ import { kit } from '@h-ai/kit'
45
+
46
+ export const handle = kit.createHandle({
47
+ auth: {
48
+ verifyToken: async token => token
49
+ ? { userId: 'u_1', roles: ['admin'], permissions: ['user:read'] }
50
+ : null,
51
+ loginUrl: '/auth/login',
52
+ protectedPaths: ['/admin/*', '/api/*'],
53
+ publicPaths: ['/api/auth/*', '/api/public/*'],
54
+ },
55
+ rateLimit: { windowMs: 60_000, maxRequests: 100 },
56
+ logging: true,
57
+ cors: {
58
+ origins: ['https://example.com'],
59
+ capacitor: true, // 自动添加 capacitor://localhost 等原生 App Origin
60
+ },
61
+ })
62
+ ```
63
+
64
+ Bearer Token 解析流程:
65
+
66
+ 1. 检查 `Authorization: Bearer <token>` header
67
+ 2. 若无 header,回退到 `event.cookies.get('hai_access_token')`(SSR 透传场景)
68
+ 3. Token 经 `auth.verifyToken` 校验后挂载到 `event.locals.session`
69
+
70
+ ### 2) API 端点使用 `kit.handler`
71
+
72
+ ```typescript
73
+ import { kit } from '@h-ai/kit'
74
+ import { z } from 'zod'
75
+
76
+ const CreateSchema = z.object({ name: z.string().min(1) })
77
+
78
+ export const POST = kit.handler(async ({ request, locals }) => {
79
+ kit.guard.require(locals.session, 'user:create')
80
+ const data = await kit.validate.body(request, CreateSchema)
81
+ return kit.response.created(data)
82
+ })
83
+ ```
84
+
85
+ ### 3) 基于契约的类型安全 handler(推荐)
86
+
87
+ ```typescript
88
+ import { iam } from '@h-ai/iam'
89
+ import { iamEndpoints } from '@h-ai/iam/api'
90
+ import { kit } from '@h-ai/kit'
91
+
92
+ // 入参自动 Zod 校验,返回值类型安全
93
+ export const POST = kit.fromContract(iamEndpoints.login, async (input, event) => {
94
+ const result = await iam.auth.login(input)
95
+ if (!result.success) {
96
+ return kit.response.unauthorized(result.error.message)
97
+ }
98
+ return kit.response.ok(result.data)
99
+ })
100
+ ```
101
+
102
+ ### 4) CORS 配置
103
+
104
+ ```typescript
105
+ export const handle = kit.createHandle({
106
+ cors: {
107
+ origins: ['https://example.com', 'https://*.example.com'], // 支持通配符
108
+ capacitor: true, // 自动添加 capacitor://localhost, ionic://localhost 等
109
+ methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
110
+ credentials: true,
111
+ },
112
+ })
113
+ ```
114
+
115
+ ### 5) 需要传输加密时启用 `crypto` 配置
116
+
117
+ ```typescript
118
+ import { crypto } from '@h-ai/crypto'
119
+ import { kit } from '@h-ai/kit'
120
+
121
+ export const handle = kit.createHandle({
122
+ crypto: {
123
+ crypto,
124
+ transport: true,
125
+ cookieEncryptionKey: process.env.HAI_KIT_COOKIE_KEY,
126
+ },
127
+ })
128
+ ```
129
+
130
+ ## 核心 API
131
+
132
+ | API | 用途 | 关键点 |
133
+ | --------------------------- | ---------------------- | --------------------------------------------- |
134
+ | `kit.createHandle(config?)` | 创建 SvelteKit Handle | Bearer Token 解析、守卫、中间件、CORS |
135
+ | `kit.sequence(...handles)` | 组合多个 Handle | 洋葱模型顺序执行 |
136
+ | `kit.handler(fn)` | 包装 API 处理函数 | 非控制流异常统一转 500 |
137
+ | `kit.fromContract(ep, fn)` | 契约到 handler | 自动 Zod 入参校验,类型安全 |
138
+ | `kit.guard.*` | 权限守卫 | `require`(throw 式) / `check`(布尔) |
139
+ | `kit.response.*` | 标准化响应 | 统一 `{ success, data?, error?, requestId? }` |
140
+ | `kit.validate.*` | 请求体/查询/参数校验 | 失败 throw Response(SvelteKit 控制流) |
141
+ | `defineEndpoint(def)` | 定义 EndpointDef 契约 | 与 api-client 配合使用 |
142
+
143
+ ### EndpointDef 契约
144
+
145
+ ```typescript
146
+ import { defineEndpoint } from '@h-ai/kit'
147
+ import { z } from 'zod'
148
+
149
+ const myEndpoint = defineEndpoint({
150
+ method: 'POST',
151
+ path: '/api/v1/items',
152
+ input: z.object({ name: z.string() }),
153
+ output: z.object({ id: z.string(), name: z.string() }),
154
+ requireAuth: true,
155
+ })
156
+ ```
157
+
158
+ ## 错误码(常见)
159
+
160
+ ### API 响应错误码(`kit.response`)
161
+
162
+ | code | status | 场景 |
163
+ | ------------------ | ------ | ------------------ |
164
+ | `BAD_REQUEST` | 400 | 参数或请求格式错误 |
165
+ | `UNAUTHORIZED` | 401 | 未认证 |
166
+ | `FORBIDDEN` | 403 | 无权限 |
167
+ | `NOT_FOUND` | 404 | 资源不存在 |
168
+ | `CONFLICT` | 409 | 资源冲突 |
169
+ | `VALIDATION_ERROR` | 422 | 数据校验失败 |
170
+ | `INTERNAL_ERROR` | 500 | 未处理异常 |
171
+
172
+ ### 传输加密常见错误(i18n key)
173
+
174
+ | key | 含义 |
175
+ | -------------------------------- | ---------------------------------- |
176
+ | `kit_transportClientIdRequired` | 缺少 `X-Client-Id`(强制加密模式) |
177
+ | `kit_transportClientKeyNotFound` | 客户端密钥不存在或失效 |
178
+ | `kit_transportInvalidPayload` | 加密载荷格式非法 |
179
+ | `kit_transportDecryptFailed` | 请求体解密失败 |
180
+ | `kit_transportKeyExchangeFailed` | 密钥交换失败 |
181
+
182
+ ## API 契约范式(端到端类型安全)
183
+
184
+ > `kit.fromContract` 和 `api.call` 共同消费模块 `api/` 下的 `EndpointDef`,实现服务端↔客户端的全链路类型安全 + 运行时 Zod 校验。
185
+
186
+ ### 契约架构
187
+
188
+ ```
189
+ ┌─────────────────────────────────────────────────────────────┐
190
+ │ @h-ai/xx/api │
191
+ │ xx-api-schemas.ts ← Zod Schema(唯一真相源) │
192
+ │ xx-api-contract.ts ← xxEndpoints(method + path + schema)│
193
+ └─────────────┬──────────────────────────┬────────────────────┘
194
+ │ │
195
+ ┌────────▼────────┐ ┌─────────▼─────────┐
196
+ │ 客户端(浏览器)│ │ 服务端(SvelteKit)│
197
+ │ api.call(ep, i) │ │ kit.fromContract │
198
+ │ @h-ai/api-client│ │ @h-ai/kit │
199
+ └────────┬────────┘ └─────────┬─────────┘
200
+ │ HTTP(类型安全) │
201
+ └──────────────────────────┘
202
+ ```
203
+
204
+ ### 服务端流程(本模块责任)
205
+
206
+ 1. 从 `@h-ai/xx/api` 导入 `xxEndpoints`
207
+ 2. `kit.fromContract(xxEndpoints.xxx, handler)` → 自动 Zod 校验入参 → handler 获得类型安全的 `input`
208
+ 3. 需权限时在 handler 内调用 `kit.guard.require`
209
+
210
+ ### 多模块契约示例
211
+
212
+ ```typescript
213
+ // ─── 存储:预签名上传 ───
214
+ import { storageEndpoints } from '@h-ai/storage/api'
215
+ import { storage } from '$lib/server/init'
216
+
217
+ export const POST = kit.fromContract(storageEndpoints.presignUpload, async (input, event) => {
218
+ kit.guard.require(event.locals.session, 'storage:write')
219
+ const result = await storage.presign.putUrl(input.key, input)
220
+ if (!result.success) {
221
+ return kit.response.internalError(result.error.message)
222
+ }
223
+ return kit.response.ok({ url: result.data, key: input.key })
224
+ })
225
+
226
+ // ─── IAM:登录 ───
227
+ import { iamEndpoints } from '@h-ai/iam/api'
228
+ import { iam } from '$lib/server/init'
229
+
230
+ export const POST = kit.fromContract(iamEndpoints.login, async (input) => {
231
+ const result = await iam.auth.login(input)
232
+ if (!result.success) {
233
+ return kit.response.unauthorized(result.error.message)
234
+ }
235
+ return kit.response.ok(result.data)
236
+ })
237
+
238
+ // ─── 支付:创建订单 ───
239
+ import { paymentEndpoints } from '@h-ai/payment/api'
240
+ import { payment } from '$lib/server/init'
241
+
242
+ export const POST = kit.fromContract(paymentEndpoints.createOrder, async (input, event) => {
243
+ kit.guard.require(event.locals.session, 'payment:create')
244
+ const result = await payment.order.create(input)
245
+ if (!result.success) {
246
+ return kit.response.internalError(result.error.message)
247
+ }
248
+ return kit.response.created(result.data)
249
+ })
250
+ ```
251
+
252
+ ### 已有契约模块一览
253
+
254
+ | 模块 | 导入路径 | 端点对象 | 典型端点 |
255
+ | ---------- | -------------------- | -------------------- | ------------------------------- |
256
+ | `storage` | `@h-ai/storage/api` | `storageEndpoints` | presignUpload, listFiles, … |
257
+ | `iam` | `@h-ai/iam/api` | `iamEndpoints` | login, logout, currentUser, … |
258
+ | `ai` | `@h-ai/ai/api` | `aiEndpoints` | chat, chatStream, sendMessage |
259
+ | `payment` | `@h-ai/payment/api` | `paymentEndpoints` | createOrder, queryOrder, … |
260
+
261
+ ### 新模块接入契约
262
+
263
+ 在模块 `src/api/` 下创建 Schema + Contract,并在 `package.json` 中声明 `"./api"` 子路径导出。
264
+
265
+ ## 常见模式
266
+
267
+ ### Bearer Token 认证流程
268
+
269
+ ```
270
+ 客户端 → Authorization: Bearer <accessToken> → kit.createHandle 解析
271
+ ↓
272
+ auth.verifyToken(token) → locals.session
273
+ ↓
274
+ protectedPaths 守卫检查 → 通过则继续
275
+ ```
276
+
277
+ SSR 场景下,浏览器通过 `hai_access_token` httpOnly Cookie 透传 Token,Handle 自动回退读取。
278
+
279
+ ### 契约模式(推荐新项目使用)
280
+
281
+ 1. 在模块的 `api/` 目录定义 `EndpointDef`(Zod schema)
282
+ 2. 服务端用 `kit.fromContract(endpoint, handler)` 处理
283
+ 3. 客户端用 `api.call(endpoint, input)` 调用
284
+ 4. 入参/出参全链路类型安全 + 运行时校验
285
+
286
+ ### CORS + Capacitor 配置
287
+
288
+ Capacitor 原生 App 的 WebView 发送的请求并非 CORS 同源,需要配置:
289
+
290
+ ```typescript
291
+ cors: {
292
+ origins: ['https://your-api.com'],
293
+ capacitor: true, // 自动添加 capacitor://* 和 ionic://* 等预设
294
+ }
295
+ ```
296
+
297
+ ### 双构建模式
298
+
299
+ ```typescript
300
+ // svelte.config.js
301
+ import { createAdapter } from '@h-ai/kit/adapter'
302
+
303
+ const config = {
304
+ kit: {
305
+ adapter: createAdapter(),
306
+ // VITE_ADAPTER=static → adapter-static (SPA)
307
+ // VITE_ADAPTER=node → adapter-node (SSR)
308
+ // 默认 → adapter-node
309
+ },
310
+ }
311
+ ```
312
+
313
+ ## 相关 Skills
314
+
315
+ - `hai-core`:全局配置、日志、HaiResult 基础能力
316
+ - `hai-iam`:Token 认证、角色权限模型
317
+ - `hai-api-client`:客户端契约调用
318
+ - `hai-crypto`:传输加密与密钥管理能力
319
+ - `hai-capacitor`:原生 App CORS 场景