@h-ai/cli 0.1.0-alpha.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/README.md +167 -0
- package/dist/chunk-2H7UOFLK.js +10 -0
- package/dist/chunk-2H7UOFLK.js.map +1 -0
- package/dist/chunk-LANIDJC7.js +659 -0
- package/dist/chunk-LANIDJC7.js.map +1 -0
- package/dist/chunk-YV7WPUI6.js +206 -0
- package/dist/chunk-YV7WPUI6.js.map +1 -0
- package/dist/config-templates-S5RI2MC6.js +5 -0
- package/dist/config-templates-S5RI2MC6.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1650 -0
- package/dist/index.js.map +1 -0
- package/dist/skill-templates-N5DU5FNX.js +5 -0
- package/dist/skill-templates-N5DU5FNX.js.map +1 -0
- package/dist/src-XJ6YXNAF.js +15030 -0
- package/dist/src-XJ6YXNAF.js.map +1 -0
- package/package.json +59 -0
- package/templates/README.md +126 -0
- package/templates/apps/admin/messages/en-US.json +33 -0
- package/templates/apps/admin/messages/zh-CN.json +33 -0
- package/templates/apps/admin/src/routes/+layout.svelte.hbs +30 -0
- package/templates/apps/admin/src/routes/+page.svelte.hbs +35 -0
- package/templates/apps/admin/src/routes/admin/+layout.svelte.hbs +44 -0
- package/templates/apps/admin/src/routes/admin/+page.svelte.hbs +45 -0
- package/templates/apps/admin/src/routes/admin/settings/+page.svelte.hbs +37 -0
- package/templates/apps/admin/src/routes/api/health/+server.ts +12 -0
- package/templates/apps/android-app/.env.hbs +3 -0
- package/templates/apps/android-app/capacitor.config.ts.hbs +12 -0
- package/templates/apps/android-app/messages/en-US.json +24 -0
- package/templates/apps/android-app/messages/zh-CN.json +24 -0
- package/templates/apps/android-app/src/lib/api.ts.hbs +30 -0
- package/templates/apps/android-app/src/lib/capacitor.ts +21 -0
- package/templates/apps/android-app/src/routes/+layout.svelte.hbs +42 -0
- package/templates/apps/android-app/src/routes/+layout.ts +7 -0
- package/templates/apps/android-app/src/routes/+page.svelte.hbs +27 -0
- package/templates/apps/api/src/routes/+layout.svelte +14 -0
- package/templates/apps/api/src/routes/+page.svelte +11 -0
- package/templates/apps/api/src/routes/api/v1/health/+server.ts +14 -0
- package/templates/apps/api/src/routes/api/v1/items/+server.ts +37 -0
- package/templates/apps/api/src/routes/api/v1/items/[id]/+server.ts +30 -0
- package/templates/apps/h5/messages/en-US.json +37 -0
- package/templates/apps/h5/messages/zh-CN.json +37 -0
- package/templates/apps/h5/src/routes/+layout.svelte.hbs +57 -0
- package/templates/apps/h5/src/routes/+page.svelte.hbs +27 -0
- package/templates/apps/h5/src/routes/discover/+page.svelte.hbs +27 -0
- package/templates/apps/h5/src/routes/profile/+page.svelte.hbs +27 -0
- package/templates/apps/website/messages/en-US.json +32 -0
- package/templates/apps/website/messages/zh-CN.json +32 -0
- package/templates/apps/website/src/routes/+layout.svelte.hbs +66 -0
- package/templates/apps/website/src/routes/+page.svelte.hbs +33 -0
- package/templates/apps/website/src/routes/about/+page.svelte.hbs +27 -0
- package/templates/apps/website/src/routes/api/health/+server.ts +12 -0
- package/templates/apps/website/src/routes/contact/+page.svelte.hbs +59 -0
- package/templates/apps/website/src/routes/services/+page.svelte.hbs +63 -0
- package/templates/base/e2e/helpers.ts +49 -0
- package/templates/base/package.json.hbs +67 -0
- package/templates/base/playwright.config.ts +32 -0
- package/templates/base/src/app.css +2 -0
- package/templates/base/src/app.d.ts.hbs +18 -0
- package/templates/base/src/app.html.hbs +16 -0
- package/templates/base/src/hooks.server.ts.hbs +123 -0
- package/templates/base/src/lib/server/init.ts.hbs +190 -0
- package/templates/base/svelte.config.js.hbs +30 -0
- package/templates/base/tsconfig.json +8 -0
- package/templates/base/vite.config.ts.hbs +27 -0
- package/templates/features/ai/routes/api/chat/+server.ts +26 -0
- package/templates/features/datapipe/routes/api/datapipe/pipeline/+server.ts +41 -0
- package/templates/features/iam/routes-admin/(auth)/+layout.svelte.hbs +21 -0
- package/templates/features/iam/routes-admin/(auth)/auth/login/+page.svelte.hbs +98 -0
- package/templates/features/iam/routes-admin/(auth)/auth/register/+page.svelte.hbs +124 -0
- package/templates/features/iam/routes-h5/auth/login/+page.svelte.hbs +98 -0
- package/templates/features/iam/routes-h5/auth/register/+page.svelte.hbs +114 -0
- package/templates/features/iam/routes-shared/api/auth/login/+server.ts +32 -0
- package/templates/features/iam/routes-shared/api/auth/logout/+server.ts +15 -0
- package/templates/features/iam/routes-shared/api/auth/me/+server.ts +12 -0
- package/templates/features/iam/routes-shared/api/auth/register/+server.ts +26 -0
- package/templates/features/payment/routes/api/v1/payment/create/+server.ts +30 -0
- package/templates/features/payment/routes/api/v1/payment/notify/[provider]/+server.ts +42 -0
- package/templates/features/payment/routes/api/v1/payment/query/[orderNo]/+server.ts +25 -0
- package/templates/features/storage/routes/api/upload/+server.ts +35 -0
- package/templates/features/vecdb/routes/api/vecdb/search/+server.ts +36 -0
- package/templates/i18n/project.inlang/settings.json +11 -0
- package/templates/skills/AGENTS.md +26 -0
- package/templates/skills/CLAUDE.md +12 -0
- package/templates/skills/copilot-instructions.md +61 -0
- package/templates/skills/hai-ai/SKILL.md +721 -0
- package/templates/skills/hai-api-client/SKILL.md +355 -0
- package/templates/skills/hai-app-create/SKILL.md +371 -0
- package/templates/skills/hai-app-review/SKILL.md +156 -0
- package/templates/skills/hai-app-tests/SKILL.md +468 -0
- package/templates/skills/hai-audit/SKILL.md +145 -0
- package/templates/skills/hai-build/SKILL.md +319 -0
- package/templates/skills/hai-cache/SKILL.md +190 -0
- package/templates/skills/hai-capacitor/SKILL.md +377 -0
- package/templates/skills/hai-core/SKILL.md +484 -0
- package/templates/skills/hai-crypto/SKILL.md +198 -0
- package/templates/skills/hai-datapipe/SKILL.md +220 -0
- package/templates/skills/hai-deploy/SKILL.md +216 -0
- package/templates/skills/hai-iam/SKILL.md +361 -0
- package/templates/skills/hai-kit/SKILL.md +484 -0
- package/templates/skills/hai-payment/SKILL.md +275 -0
- package/templates/skills/hai-reach/SKILL.md +315 -0
- package/templates/skills/hai-reldb/SKILL.md +465 -0
- package/templates/skills/hai-scheduler/SKILL.md +188 -0
- package/templates/skills/hai-storage/SKILL.md +276 -0
- package/templates/skills/hai-ui/SKILL.md +438 -0
- package/templates/skills/hai-vecdb/SKILL.md +279 -0
- package/templates/skills/opencode.json +11 -0
|
@@ -0,0 +1,361 @@
|
|
|
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
|
+
> ⚠️ **服务端模块(Node.js only)。** 浏览器端通过 `api.call(iamEndpoints.xxx)` 或 `apiFetch` 调用 IAM API 端点(参见 hai-api-client / hai-kit SKILL)。`@h-ai/iam/api` 子路径导出的 Zod Schema 和 EndpointDef 可在浏览器端使用。
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## 依赖
|
|
19
|
+
|
|
20
|
+
| 模块 | 用途 | 是否必需 | 初始化要求 |
|
|
21
|
+
| --- | --- | --- | --- |
|
|
22
|
+
| `@h-ai/reldb` | 数据库(用户/角色/权限持久化) | **必需** | 需在 `iam.init()` 前初始化 |
|
|
23
|
+
| `@h-ai/cache` | 缓存(会话/OTP/重置令牌/权限缓存) | **必需** | 需在 `iam.init()` 前初始化 |
|
|
24
|
+
| `@h-ai/crypto` | 密码哈希 | 内部使用 | 自动初始化 |
|
|
25
|
+
| `@h-ai/audit` | 审计日志(RBAC 关键操作记录) | 内部使用 | 已初始化时自动写入 |
|
|
26
|
+
|
|
27
|
+
## 适用场景
|
|
28
|
+
|
|
29
|
+
- 用户注册与登录(密码/OTP/LDAP/API Key)
|
|
30
|
+
- Bearer Token 认证与管理(TokenPair: accessToken + refreshToken)
|
|
31
|
+
- Token 刷新(rotation 策略)与吊销
|
|
32
|
+
- RBAC 角色/权限的创建、分配与检查
|
|
33
|
+
- API Key 管理(创建、吊销、验证)
|
|
34
|
+
- 密码重置流程
|
|
35
|
+
- 与 kit 集成的认证守卫
|
|
36
|
+
- API 契约定义(`@h-ai/iam/api`)
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## 使用步骤
|
|
41
|
+
|
|
42
|
+
### 1. 配置
|
|
43
|
+
|
|
44
|
+
```yaml
|
|
45
|
+
# config/_iam.yml
|
|
46
|
+
password:
|
|
47
|
+
minLength: ${HAI_IAM_PASSWORD_MIN_LENGTH:8}
|
|
48
|
+
requireUppercase: true
|
|
49
|
+
requireNumber: true
|
|
50
|
+
session:
|
|
51
|
+
maxAge: ${HAI_IAM_SESSION_MAX_AGE:86400}
|
|
52
|
+
refreshTokenMaxAge: ${HAI_IAM_REFRESH_TOKEN_MAX_AGE:604800} # 7 天
|
|
53
|
+
sliding: true
|
|
54
|
+
singleDevice: false
|
|
55
|
+
login:
|
|
56
|
+
password: true
|
|
57
|
+
otp: false
|
|
58
|
+
register:
|
|
59
|
+
enabled: true
|
|
60
|
+
security:
|
|
61
|
+
maxLoginAttempts: ${HAI_IAM_MAX_LOGIN_ATTEMPTS:5}
|
|
62
|
+
lockoutDuration: 900
|
|
63
|
+
rbac:
|
|
64
|
+
defaultRole: user
|
|
65
|
+
superAdminRole: super_admin
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### 2. 初始化
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
import { cache } from '@h-ai/cache'
|
|
72
|
+
import { reldb } from '@h-ai/reldb'
|
|
73
|
+
import { iam } from '@h-ai/iam'
|
|
74
|
+
|
|
75
|
+
// 必须先初始化 reldb 和 cache
|
|
76
|
+
await reldb.init(core.config.get('db'))
|
|
77
|
+
await cache.init(core.config.get('cache'))
|
|
78
|
+
|
|
79
|
+
// IAM 自动使用已初始化的 reldb 和 cache 单例
|
|
80
|
+
await iam.init({
|
|
81
|
+
...core.config.get('iam'),
|
|
82
|
+
seedDefaultData: true,
|
|
83
|
+
})
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
初始化时自动创建数据库表(5 张):`hai_iam_users`、`hai_iam_roles`、`hai_iam_permissions`、`hai_iam_role_permissions`、`hai_iam_user_roles`。
|
|
87
|
+
|
|
88
|
+
Token 和 OTP 存储在 cache 中(不落库)。refreshToken 使用独立的 cache key 存储。
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## 核心 API
|
|
93
|
+
|
|
94
|
+
### 认证 — `iam.auth`
|
|
95
|
+
|
|
96
|
+
| 方法 | 签名 | 说明 |
|
|
97
|
+
| ------------------ | ----------------------------------------------------------- | ---------------------------------- |
|
|
98
|
+
| `login` | `({ identifier, password }) => Promise<HaiResult<AuthResult>>` | 密码登录(用户名/邮箱/手机号) |
|
|
99
|
+
| `loginWithOtp` | `({ identifier, code }) => Promise<HaiResult<AuthResult>>` | OTP 验证码登录 |
|
|
100
|
+
| `loginWithLdap` | `({ username, password }) => Promise<HaiResult<AuthResult>>` | LDAP 登录 |
|
|
101
|
+
| `loginWithApiKey` | `({ key }) => Promise<HaiResult<AuthResult>>` | API Key 登录 |
|
|
102
|
+
| `sendOtp` | `(identifier) => Promise<HaiResult<{ expiresAt }>>` | 发送 OTP 验证码 |
|
|
103
|
+
| `verifyToken` | `(token) => Promise<HaiResult<Session>>` | 验证令牌(推荐入口,委托 session) |
|
|
104
|
+
| `logout` | `(token) => Promise<HaiResult<void>>` | 登出 |
|
|
105
|
+
| `registerAndLogin` | `(options: RegisterOptions) => Promise<HaiResult<AuthResult>>` | 注册并登录(一站式) |
|
|
106
|
+
|
|
107
|
+
**AuthResult**(已改造为 TokenPair):
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
interface AuthResult {
|
|
111
|
+
user: User
|
|
112
|
+
tokens: TokenPair
|
|
113
|
+
roles: string[]
|
|
114
|
+
permissions: string[]
|
|
115
|
+
agreements?: AgreementDisplay
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
interface TokenPair {
|
|
119
|
+
accessToken: string
|
|
120
|
+
refreshToken: string
|
|
121
|
+
expiresIn: number
|
|
122
|
+
tokenType: 'Bearer'
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### 会话管理 — `iam.session`
|
|
127
|
+
|
|
128
|
+
| 方法 | 签名 | 说明 |
|
|
129
|
+
| ------------------- | -------------------------------------------------------------------- | --------------------------------------- |
|
|
130
|
+
| `create` | `({ userId, username, roles, source? }) => Promise<HaiResult<Session>>` | 创建会话(返回含 TokenPair 的 session) |
|
|
131
|
+
| `get` | `(token) => Promise<HaiResult<Session \| null>>` | 获取会话(滑动续期自动延长) |
|
|
132
|
+
| `verifyToken` | `(token) => Promise<HaiResult<Session>>` | 验证令牌 |
|
|
133
|
+
| `update` | `(token, data) => Promise<HaiResult<void>>` | 更新会话字段(浅合并 data) |
|
|
134
|
+
| `delete` | `(token) => Promise<HaiResult<void>>` | 删除会话 |
|
|
135
|
+
| `deleteByUserId` | `(userId) => Promise<HaiResult<number>>` | 删除用户所有会话(返回删除数量) |
|
|
136
|
+
| `refresh` | `(refreshToken) => Promise<HaiResult<TokenPair>>` | 刷新 Token(rotation:旧 Token 失效) |
|
|
137
|
+
| `revokeRefresh` | `(refreshToken) => Promise<HaiResult<void>>` | 吊销 refreshToken |
|
|
138
|
+
| `patchUserSessions` | `(userId, updates: SessionFieldUpdates) => Promise<HaiResult<void>>` | 批量更新用户所有会话的 roles/permissions |
|
|
139
|
+
|
|
140
|
+
**Token 刷新(rotation 策略)**:
|
|
141
|
+
|
|
142
|
+
- `refresh()` 会删除旧的 accessToken + refreshToken
|
|
143
|
+
- 创建全新的 session,返回新的 TokenPair
|
|
144
|
+
- 旧 refreshToken 立即失效,防止重放攻击
|
|
145
|
+
|
|
146
|
+
### 用户管理 — `iam.user`
|
|
147
|
+
|
|
148
|
+
| 方法 | 签名 | 说明 |
|
|
149
|
+
| -------------------------- | ----------------------------------------------------------------------- | ------------------------------------ |
|
|
150
|
+
| `register` | `(input) => Promise<HaiResult<RegisterResult>>` | 用户注册 |
|
|
151
|
+
| `getUser` | `(id, options?) => Promise<HaiResult<User \| null>>` | 获取用户(可选 include: ['roles']) |
|
|
152
|
+
| `getCurrentUser` | `(token) => Promise<HaiResult<User>>` | 通过令牌获取当前用户 |
|
|
153
|
+
| `updateCurrentUser` | `(token, data: UpdateCurrentUserInput) => Promise<HaiResult<User>>` | 当前用户修改个人资料(白名单字段) |
|
|
154
|
+
| `listUsers` | `(options?: ListUsersOptions) => Promise<HaiResult<PaginatedResult<User>>>` | 用户列表(分页 + 搜索 + 过滤) |
|
|
155
|
+
| `updateUser` | `(id, data) => Promise<HaiResult<User>>` | 更新用户信息 |
|
|
156
|
+
| `deleteUser` | `(id) => Promise<HaiResult<void>>` | 删除用户(自动清除会话) |
|
|
157
|
+
| `changePassword` | `(id, old, new) => Promise<HaiResult<void>>` | 修改密码(自动清除会话) |
|
|
158
|
+
| `changeCurrentUserPassword`| `(token, old, new) => Promise<HaiResult<void>>` | 当前用户修改密码(通过令牌识别) |
|
|
159
|
+
| `requestPasswordReset` | `(email) => Promise<HaiResult<void>>` | 请求密码重置(防枚举) |
|
|
160
|
+
| `confirmPasswordReset` | `(token, newPassword) => Promise<HaiResult<void>>` | 确认密码重置(自动清除会话) |
|
|
161
|
+
| `adminResetPassword` | `(userId, newPassword) => Promise<HaiResult<void>>` | 管理员直接重置密码 |
|
|
162
|
+
| `validatePassword` | `(password) => HaiResult<void>` | 验证密码强度(**同步方法**) |
|
|
163
|
+
|
|
164
|
+
### RBAC 授权 — `iam.authz`
|
|
165
|
+
|
|
166
|
+
| 方法 | 签名 | 说明 |
|
|
167
|
+
| -------------------------- | -------------------------------------------------------------------------------- | -------------------------------------------- |
|
|
168
|
+
| `checkPermission` | `(userId, permission) => Promise<HaiResult<boolean>>` | 检查权限(超管自动通过,支持通配符) |
|
|
169
|
+
| `getUserPermissions` | `(userId) => Promise<HaiResult<Permission[]>>` | 获取用户权限列表(通过角色聚合去重) |
|
|
170
|
+
| `getUserRoles` | `(userId) => Promise<HaiResult<Role[]>>` | 获取用户角色列表 |
|
|
171
|
+
| `getUserRolesForMany` | `(userIds) => Promise<HaiResult<Map<string, Role[]>>>` | 批量获取多用户角色(避免 N+1) |
|
|
172
|
+
| `assignRole` | `(userId, roleId, tx?) => Promise<HaiResult<void>>` | 分配角色给用户 |
|
|
173
|
+
| `removeRole` | `(userId, roleId, tx?) => Promise<HaiResult<void>>` | 移除用户角色 |
|
|
174
|
+
| `syncRoles` | `(userId, roleIds, tx?) => Promise<HaiResult<void>>` | 同步用户角色(替换为目标列表) |
|
|
175
|
+
| `createRole` | `({ code, name, description? }, tx?) => Promise<HaiResult<Role>>` | 创建角色 |
|
|
176
|
+
| `getRole` | `(roleId) => Promise<HaiResult<Role \| null>>` | 获取角色 |
|
|
177
|
+
| `getRoleByCode` | `(code) => Promise<HaiResult<Role \| null>>` | 根据代码获取角色 |
|
|
178
|
+
| `getAllRoles` | `(options?) => Promise<HaiResult<PaginatedResult<Role>>>` | 获取所有角色(分页) |
|
|
179
|
+
| `updateRole` | `(roleId, data, tx?) => Promise<HaiResult<Role>>` | 更新角色 |
|
|
180
|
+
| `deleteRole` | `(roleId, tx?) => Promise<HaiResult<void>>` | 删除角色(级联清理关联 + 会话同步) |
|
|
181
|
+
| `createPermission` | `({ code, name, type?, resource?, action? }, tx?) => Promise<HaiResult<Permission>>`| 创建权限 |
|
|
182
|
+
| `getPermission` | `(permissionId) => Promise<HaiResult<Permission \| null>>` | 获取权限 |
|
|
183
|
+
| `getPermissionByCode` | `(code) => Promise<HaiResult<Permission \| null>>` | 根据代码获取权限 |
|
|
184
|
+
| `getAllPermissions` | `(options?: PermissionQueryOptions) => Promise<HaiResult<PaginatedResult<Permission>>>` | 获取所有权限(分页 + 类型/关键字筛选) |
|
|
185
|
+
| `deletePermission` | `(permissionId, tx?) => Promise<HaiResult<void>>` | 删除权限(级联清理关联 + 会话同步) |
|
|
186
|
+
| `assignPermissionToRole` | `(roleId, permId, tx?) => Promise<HaiResult<void>>` | 分配权限给角色 |
|
|
187
|
+
| `removePermissionFromRole` | `(roleId, permId, tx?) => Promise<HaiResult<void>>` | 移除角色权限 |
|
|
188
|
+
| `getRolePermissions` | `(roleId) => Promise<HaiResult<Permission[]>>` | 获取角色的权限列表 |
|
|
189
|
+
| `getRolePermissionsForMany`| `(roleIds) => Promise<HaiResult<Map<string, Permission[]>>>` | 批量获取多角色权限(避免 N+1) |
|
|
190
|
+
|
|
191
|
+
**通配符规则**:`admin:*` 匹配 `admin:read`、`admin:write` 等。超管角色自动拥有所有权限。
|
|
192
|
+
|
|
193
|
+
### API Key 管理 — `iam.apiKey`
|
|
194
|
+
|
|
195
|
+
| 方法 | 签名 | 说明 |
|
|
196
|
+
| -------------- | ----------------------------------------------------------------------- | -------------------------------------- |
|
|
197
|
+
| `createApiKey` | `(userId, options: CreateApiKeyOptions) => Promise<HaiResult<CreateApiKeyResult>>` | 创建 API Key(明文密钥仅返回一次) |
|
|
198
|
+
| `listApiKeys` | `(userId) => Promise<HaiResult<ApiKey[]>>` | 列出用户的所有 API Key |
|
|
199
|
+
| `getApiKey` | `(keyId) => Promise<HaiResult<ApiKey \| null>>` | 获取 API Key 详情 |
|
|
200
|
+
| `revokeApiKey` | `(keyId) => Promise<HaiResult<void>>` | 吊销/删除 API Key |
|
|
201
|
+
| `verifyApiKey` | `(rawKey) => Promise<HaiResult<ApiKey>>` | 验证 API Key 并返回实体(含用户 ID) |
|
|
202
|
+
|
|
203
|
+
> 需在 `login.apikey: true` 时才会初始化此子模块,否则访问返回 `NOT_INITIALIZED` 错误。
|
|
204
|
+
|
|
205
|
+
---
|
|
206
|
+
|
|
207
|
+
## API 契约 — `@h-ai/iam/api`
|
|
208
|
+
|
|
209
|
+
```typescript
|
|
210
|
+
import { iamEndpoints } from '@h-ai/iam/api'
|
|
211
|
+
|
|
212
|
+
// 9 个端点契约,含 Zod schema
|
|
213
|
+
iamEndpoints.login // POST /api/v1/auth/login
|
|
214
|
+
iamEndpoints.loginWithOtp // POST /api/v1/auth/login-otp
|
|
215
|
+
iamEndpoints.sendOtp // POST /api/v1/auth/send-otp
|
|
216
|
+
iamEndpoints.logout // POST /api/v1/auth/logout
|
|
217
|
+
iamEndpoints.register // POST /api/v1/auth/register
|
|
218
|
+
iamEndpoints.currentUser // GET /api/v1/auth/me
|
|
219
|
+
iamEndpoints.refreshToken // POST /api/v1/auth/refresh
|
|
220
|
+
iamEndpoints.changePassword // POST /api/v1/auth/change-password
|
|
221
|
+
iamEndpoints.resetPassword // POST /api/v1/auth/reset-password
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
服务端使用 `kit.fromContract()`,客户端使用 `api.call()`:
|
|
225
|
+
|
|
226
|
+
```typescript
|
|
227
|
+
// 服务端
|
|
228
|
+
export const POST = kit.fromContract(iamEndpoints.login, async (input) => {
|
|
229
|
+
const result = await iam.auth.login(input)
|
|
230
|
+
return result.success ? kit.response.ok(result.data) : kit.response.unauthorized()
|
|
231
|
+
})
|
|
232
|
+
|
|
233
|
+
// 客户端
|
|
234
|
+
const result = await api.call(iamEndpoints.login, { username, password })
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
---
|
|
238
|
+
|
|
239
|
+
## 错误码 — `HaiIamError`
|
|
240
|
+
|
|
241
|
+
| 错误码 | code | 说明 |
|
|
242
|
+
|--------|------|------|
|
|
243
|
+
| `HaiIamError.AUTH_FAILED` | `hai:iam:001` | 认证失败 |
|
|
244
|
+
| `HaiIamError.INVALID_CREDENTIALS` | `hai:iam:002` | 凭证无效 |
|
|
245
|
+
| `HaiIamError.USER_NOT_FOUND` | `hai:iam:003` | 用户不存在 |
|
|
246
|
+
| `HaiIamError.USER_DISABLED` | `hai:iam:004` | 用户已禁用 |
|
|
247
|
+
| `HaiIamError.USER_LOCKED` | `hai:iam:005` | 用户已锁定 |
|
|
248
|
+
| `HaiIamError.USER_ALREADY_EXISTS` | `hai:iam:006` | 用户已存在 |
|
|
249
|
+
| `HaiIamError.PASSWORD_EXPIRED` | `hai:iam:007` | 密码已过期 |
|
|
250
|
+
| `HaiIamError.PASSWORD_POLICY_VIOLATION` | `hai:iam:008` | 密码不符合策略 |
|
|
251
|
+
| `HaiIamError.OTP_INVALID` | `hai:iam:009` | 验证码无效 |
|
|
252
|
+
| `HaiIamError.NOT_INITIALIZED` | `hai:iam:010` | 未初始化 |
|
|
253
|
+
| `HaiIamError.OTP_RESEND_TOO_FAST` | `hai:iam:011` | 发送过于频繁 |
|
|
254
|
+
| `HaiIamError.LOGIN_DISABLED` | `hai:iam:012` | 登录方式已禁用 |
|
|
255
|
+
| `HaiIamError.REGISTER_DISABLED` | `hai:iam:013` | 注册已禁用 |
|
|
256
|
+
| `HaiIamError.STRATEGY_NOT_SUPPORTED` | `hai:iam:014` | 认证策略不支持 |
|
|
257
|
+
| `HaiIamError.APIKEY_INVALID` | `hai:iam:015` | API Key 无效 |
|
|
258
|
+
| `HaiIamError.APIKEY_EXPIRED` | `hai:iam:016` | API Key 已过期 |
|
|
259
|
+
| `HaiIamError.APIKEY_DISABLED` | `hai:iam:017` | API Key 已禁用 |
|
|
260
|
+
| `HaiIamError.APIKEY_NOT_FOUND` | `hai:iam:018` | API Key 不存在 |
|
|
261
|
+
| `HaiIamError.RESET_TOKEN_INVALID` | `hai:iam:019` | 重置令牌无效 |
|
|
262
|
+
| `HaiIamError.RESET_TOKEN_EXPIRED` | `hai:iam:020` | 重置令牌已过期 |
|
|
263
|
+
| `HaiIamError.RESET_TOKEN_MAX_ATTEMPTS` | `hai:iam:021` | 重置验证次数超限 |
|
|
264
|
+
| `HaiIamError.OTP_EXPIRED` | `hai:iam:022` | 验证码已过期 |
|
|
265
|
+
| `HaiIamError.SESSION_NOT_FOUND` | `hai:iam:101` | 会话不存在 |
|
|
266
|
+
| `HaiIamError.SESSION_EXPIRED` | `hai:iam:102` | 会话已过期 |
|
|
267
|
+
| `HaiIamError.SESSION_INVALID` | `hai:iam:103` | 会话无效 |
|
|
268
|
+
| `HaiIamError.SESSION_CREATE_FAILED` | `hai:iam:104` | 会话创建失败 |
|
|
269
|
+
| `HaiIamError.TOKEN_EXPIRED` | `hai:iam:105` | 令牌已过期 |
|
|
270
|
+
| `HaiIamError.TOKEN_INVALID` | `hai:iam:106` | 令牌无效 |
|
|
271
|
+
| `HaiIamError.TOKEN_REFRESH_FAILED` | `hai:iam:107` | 令牌刷新失败 |
|
|
272
|
+
| `HaiIamError.PERMISSION_DENIED` | `hai:iam:201` | 权限不足 |
|
|
273
|
+
| `HaiIamError.ROLE_NOT_FOUND` | `hai:iam:202` | 角色不存在 |
|
|
274
|
+
| `HaiIamError.PERMISSION_NOT_FOUND` | `hai:iam:203` | 权限不存在 |
|
|
275
|
+
| `HaiIamError.ROLE_ALREADY_EXISTS` | `hai:iam:204` | 角色已存在 |
|
|
276
|
+
| `HaiIamError.PERMISSION_ALREADY_EXISTS` | `hai:iam:205` | 权限已存在 |
|
|
277
|
+
| `HaiIamError.LDAP_CONNECTION_FAILED` | `hai:iam:301` | LDAP 连接失败 |
|
|
278
|
+
| `HaiIamError.LDAP_BIND_FAILED` | `hai:iam:302` | LDAP 绑定失败 |
|
|
279
|
+
| `HaiIamError.LDAP_SEARCH_FAILED` | `hai:iam:303` | LDAP 搜索失败 |
|
|
280
|
+
| `HaiIamError.REPOSITORY_ERROR` | `hai:iam:401` | 存储层操作错误 |
|
|
281
|
+
| `HaiIamError.NOT_FOUND` | `hai:iam:402` | 资源不存在 |
|
|
282
|
+
| `HaiIamError.CONFLICT` | `hai:iam:403` | 资源冲突 |
|
|
283
|
+
| `HaiIamError.FORBIDDEN` | `hai:iam:501` | 禁止访问 |
|
|
284
|
+
| `HaiIamError.INVALID_ARGUMENT` | `hai:iam:502` | 参数无效 |
|
|
285
|
+
| `HaiIamError.CONFIG_ERROR` | `hai:iam:901` | 配置错误 |
|
|
286
|
+
| `HaiIamError.INTERNAL_ERROR` | `hai:iam:999` | 内部错误 |
|
|
287
|
+
|
|
288
|
+
---
|
|
289
|
+
|
|
290
|
+
## 常见模式
|
|
291
|
+
|
|
292
|
+
### 与 kit 集成(hooks.server.ts)
|
|
293
|
+
|
|
294
|
+
```typescript
|
|
295
|
+
import { iam } from '$lib/server/init'
|
|
296
|
+
import { kit } from '@h-ai/kit'
|
|
297
|
+
|
|
298
|
+
const haiHandle = kit.createHandle({
|
|
299
|
+
validateSession: async (token) => {
|
|
300
|
+
const result = await iam.auth.verifyToken(token)
|
|
301
|
+
if (!result.success)
|
|
302
|
+
return null
|
|
303
|
+
const roles = await iam.authz.getUserRoles(result.data.userId)
|
|
304
|
+
return {
|
|
305
|
+
userId: result.data.userId,
|
|
306
|
+
roles: roles.success ? roles.data.map(r => r.code) : [],
|
|
307
|
+
permissions: [],
|
|
308
|
+
}
|
|
309
|
+
},
|
|
310
|
+
guards: [
|
|
311
|
+
{
|
|
312
|
+
guard: kit.guard.auth({ apiMode: true }),
|
|
313
|
+
paths: ['/api/*'],
|
|
314
|
+
exclude: ['/api/auth/*'],
|
|
315
|
+
},
|
|
316
|
+
],
|
|
317
|
+
})
|
|
318
|
+
|
|
319
|
+
export const handle = kit.sequence(haiHandle)
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
### 客户端认证流程
|
|
323
|
+
|
|
324
|
+
```typescript
|
|
325
|
+
import { iamEndpoints } from '@h-ai/iam/api'
|
|
326
|
+
|
|
327
|
+
// 登录 → 保存 Token
|
|
328
|
+
const login = await api.call(iamEndpoints.login, { username, password })
|
|
329
|
+
if (login.success) {
|
|
330
|
+
await api.auth.setTokens(login.data.tokens)
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// 获取当前用户
|
|
334
|
+
const me = await api.call(iamEndpoints.currentUser, {})
|
|
335
|
+
|
|
336
|
+
// Token 过期时,api-client 自动调用 refreshUrl 刷新
|
|
337
|
+
// 刷新失败时,清除 Token 并跳转登录页
|
|
338
|
+
|
|
339
|
+
// 登出
|
|
340
|
+
await api.call(iamEndpoints.logout, {})
|
|
341
|
+
await api.auth.clear()
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
### 自动会话失效
|
|
345
|
+
|
|
346
|
+
以下操作自动清除受影响用户的所有活跃会话:
|
|
347
|
+
|
|
348
|
+
- `deleteUser` / `updateUser({ enabled: false })` / `changePassword` / `confirmPasswordReset`
|
|
349
|
+
- `assignRole` / `removeRole` / `deleteRole` 会同步更新活跃会话中的角色列表
|
|
350
|
+
|
|
351
|
+
---
|
|
352
|
+
|
|
353
|
+
## 相关 Skills
|
|
354
|
+
|
|
355
|
+
- `hai-build`:模块初始化顺序(db → cache → iam)
|
|
356
|
+
- `hai-kit`:SvelteKit 集成(Bearer Token + 契约处理)
|
|
357
|
+
- `hai-api-client`:客户端契约调用
|
|
358
|
+
- `hai-reldb`:底层数据存储
|
|
359
|
+
- `hai-cache`:Token 与权限缓存
|
|
360
|
+
- `hai-crypto`:密码哈希(内部依赖)
|
|
361
|
+
- `hai-ui`:IAM 场景组件(LoginForm/RegisterForm 等)
|