@lark-apaas/auth-sdk 0.1.0-alpha.0 → 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/README.md CHANGED
@@ -1,8 +1,8 @@
1
- ## @lark-apaas/auth-sdk
1
+ # @lark-apaas/auth-sdk
2
2
 
3
3
  基于 CASL 的前端鉴权 SDK,封装了权限数据获取、Ability 初始化与 React 集成,帮助你在应用中便捷地做「功能级/按钮级/菜单级/页面级」的权限控制。
4
4
 
5
- ### 安装
5
+ ## 安装
6
6
 
7
7
  ```bash
8
8
  npm i @lark-apaas/auth-sdk
@@ -10,22 +10,22 @@ npm i @lark-apaas/auth-sdk
10
10
  yarn add @lark-apaas/auth-sdk
11
11
  ```
12
12
 
13
- ### 快速开始
13
+ ## 快速开始
14
+
15
+ ### 模版接入
14
16
 
15
17
  ```tsx
16
18
  import React from 'react';
17
- import { AuthProvider, Can, useAbility } from '@lark-apaas/auth-sdk';
19
+ import { AuthProvider, CanRole, useAuthAbility } from '@lark-apaas/auth-sdk';
18
20
 
19
21
  export default function App() {
20
22
  return (
21
23
  <AuthProvider
22
24
  config={{
23
25
  permissionApi: {
24
- baseUrl: 'http://localhost:3000',
25
- endpoint: '/api/users/:userId/permissions',
26
- // 可选:apiToken、timeout、headers、withCredentials
26
+ timeout: 5000,
27
27
  },
28
- autoFetch: true,
28
+ enable: true,
29
29
  onError: (e) => console.error(e),
30
30
  }}
31
31
  >
@@ -33,173 +33,104 @@ export default function App() {
33
33
  </AuthProvider>
34
34
  );
35
35
  }
36
+ ```
37
+
38
+
39
+ ### 开发组件 - 使用 CanRole 组件
40
+
41
+ ```tsx
42
+ import { CanRole } from '@lark-apaas/auth-sdk';
36
43
 
37
44
  function Home() {
38
- const ability = useAbility();
39
45
  return (
40
46
  <div>
41
- <Can I="read" a="Dashboard">
42
- <div>可见的仪表盘</div>
43
- </Can>
44
- <button disabled={ability.cannot('create', 'Task')}>创建任务</button>
47
+ <CanRole roles={['role_admin']}>
48
+ <div>管理员按钮</div>
49
+ </CanRole>
50
+ <CanRole roles={['role_admin', 'role_editor']}>
51
+ <div>编辑按钮</div>
52
+ </CanRole>
45
53
  </div>
46
54
  );
47
55
  }
48
56
  ```
49
57
 
50
- ---
51
-
52
- ## 核心 API
58
+ ### 开发组件 - 使用 AbilityContext 处理复杂场景
53
59
 
54
- ### AuthProvider
55
- - **作用**: 提供 `Ability` 与权限状态上下文,自动/手动拉取权限。
56
- - **Props**:
57
- - `config?: AuthSdkConfig`
58
- - `noPermissionPath?: string` 无权限时的导航路径
59
- - `permissionApi?: PermissionApiConfig` 拉取权限接口配置
60
- - `baseUrl?: string`
61
- - `endpoint?: string`,支持 `:userId` 占位
62
- - `timeout?: number`,默认 5000ms
63
- - `headers?: Record<string,string>`
64
- - `apiToken?: string` 自动加到 Authorization 头
65
- - `withCredentials?: boolean`,默认 true
66
- - `autoFetch?: boolean` 是否初始化自动拉取
67
- - `onError?: (error: Error) => void`
68
- - `onSuccess?: (data: PermissionApiResponse) => void`
69
- - `children: React.ReactNode`
70
-
71
- 示例:
72
60
  ```tsx
73
- <AuthProvider userId="user-123" config={{
74
- permissionApi: { endpoint: '/api/permissions' },
75
- autoFetch: true,
76
- }}>
77
- <App />
78
- {/* Can/Hook 在其子树中生效 */}
79
- </AuthProvider>
61
+ import { useContext } from 'react';
62
+ import { AbilityContext, ROLE_SUBJECT } from '@lark-apaas/auth-sdk';
63
+
64
+ function Home() {
65
+ const ability = useContext(AbilityContext);
66
+ return (
67
+ <div>
68
+ {ability.can('role_admin', ROLE_SUBJECT) || ability.can('role_editor', ROLE_SUBJECT) ? (
69
+ <div>可见的仪表盘</div>
70
+ ) : null}
71
+ </div>
72
+ );
73
+ }
80
74
  ```
81
75
 
82
- ### useAuth
83
- - **作用**: 访问权限状态与方法。
84
- - **返回**:
85
- - `permissions: Permission[]`
86
- - `roles: string[]`
87
- - `userId: string | null`
88
- - `setUserId(userId: string): void`
89
- - `isLoading: boolean`
90
- - `error: Error | null`
91
- - `fetchPermissions(userId?: string): Promise<void>` 手动拉取
92
- - `refetch(): Promise<void>` 按当前 `userId` 重新拉取
76
+ ### 开发组件 - 见 Client Toolkit
93
77
 
94
- ```tsx
95
- const { permissions, roles, isLoading, refetch } = useAuth();
96
- ```
78
+ ---
97
79
 
98
- ### useAbility
99
- - **作用**: 获取 CASL `Ability` 实例,使用 `ability.can(action, subject)` 做任意判断。
80
+ ## 核心 API - 面向 Agent
100
81
 
101
- ```tsx
102
- const ability = useAbility();
103
- const canCreate = ability.can('create', 'Task');
104
- ```
82
+ ### CanRole 组件 (推荐)
105
83
 
106
- ### Can 组件(来自 @casl/react Contextual Can)
107
- - **作用**: 条件渲染,只有当 `I` 对 `a` 可操作时才渲染子内容;也支持 render prop。
84
+ - **作用**: 条件渲染,只有当角色 id 包含在 `roles` 中时才渲染子内容。
108
85
 
109
86
  ```tsx
110
- <Can I="delete" a="Task">
87
+ <CanRole roles={['role_admin', 'role_editor']}>
111
88
  <button>删除任务</button>
112
- </Can>
113
-
114
- <Can I="download" a="Report">{(allowed) => (
115
- <button disabled={!allowed}>下载报表</button>
116
- )}</Can>
89
+ </CanRole>
117
90
  ```
118
91
 
119
- ### 批量权限判断:useCanPermission / CanPermission
120
- - `useCanPermission({ permissions, or })`:一次检查多个权限;`or=true` 表示任一满足即可,否则默认全部需要满足。
121
- - `CanPermission`:同上但以组件方式使用,支持 children 或 render prop。
92
+ ### AbilityContext 提供原子化的权限判断能力
122
93
 
123
- ```tsx
124
- const allowed = useCanPermission({
125
- permissions: [
126
- { action: 'read', subject: 'Report' },
127
- { action: 'download', subject: 'Report' },
128
- ],
129
- or: false,
130
- });
131
-
132
- <CanPermission
133
- permissions={[{ action: 'manage', subject: 'Task' }, { action: 'delete', subject: 'Task' }]}>
134
- <BulkActions />
135
- </CanPermission>
136
- ```
137
-
138
- ### 角色判断:useCanRole / CanRole
139
- - 角色被映射为特殊 subject `@role` 下的 action。
140
- - `useCanRole({ roles, and })`:是否拥有指定多个角色;`and=true` 表示需要同时具备。
141
- - `CanRole`:以组件形式使用。
94
+ - **作用**: 从 React 上下文获取 CASL `Ability` 实例,使用 `ability.can(action, subject)` 做权限判断。
142
95
 
143
96
  ```tsx
144
- const isAdmin = useCanRole({ roles: ['admin_role'] });
97
+ import { useContext } from 'react';
98
+ import { AbilityContext } from '@lark-apaas/auth-sdk';
145
99
 
146
- <CanRole roles={[ 'admin_role', 'ops_role' ]} and>
147
- <DangerZone />
148
- </CanRole>
100
+ const ability = useContext(AbilityContext);
101
+ const canCreate = ability.can('role_editor', '@role');
149
102
  ```
150
103
 
151
- ### 路由守卫:CanRoute
152
- - **作用**: 路由级别的权限守卫,支持权限和角色双重检查,无权限时可自动重定向。
104
+ ## 核心 API - 面向接入方
105
+
106
+ ### AuthProvider
107
+
108
+ - **作用**: 提供 `Ability` 与权限状态上下文,自动/手动拉取权限。
153
109
  - **Props**:
154
- - `permissions?: Array<{ action: string; subject: string }>` 需要检查的权限列表
155
- - `roles?: string[]` 需要检查的角色列表
156
- - `children: React.ReactNode` 受保护的内容
157
- - `fallback?: React.ReactNode` 无权限时渲染的内容(优先级最高)
158
- - `redirectTo?: string` 无权限时重定向的路径(优先级次之)
159
- - 如果既没有 `fallback` 也没有 `redirectTo`,会尝试使用 `AuthProvider` 配置的 `noPermissionPath`
160
- - 如果都没有配置,则返回 `null`
110
+ - `config?: AuthSdkConfig`
111
+ - `enable?: boolean` 是否启用权限 SDK,默认 false
112
+ - `permissionApi?: PermissionApiConfig` 拉取权限接口配置
113
+ - `timeout?: number`,默认 5000ms
114
+ - `headers?: Record<string,string>` 自定义请求头
115
+ - `onError?: (error: Error) => void`
116
+ - `onSuccess?: (data: PermissionApiResponse) => void`
117
+ - `children: React.ReactNode`
118
+
119
+ 示例:
161
120
 
162
121
  ```tsx
163
- import { CanRoute } from '@lark-apaas/auth-sdk';
164
- import { Routes, Route } from 'react-router-dom';
165
-
166
- // 基础用法:权限检查
167
- <CanRoute permissions={[{ action: 'read', subject: 'Dashboard' }]}>
168
- <Dashboard />
169
- </CanRoute>
170
-
171
- // 角色检查
172
- <CanRoute roles={['admin', 'manager']}>
173
- <AdminPanel />
174
- </CanRoute>
175
-
176
- // 自定义重定向
177
- <CanRoute
178
- permissions={[{ action: 'manage', subject: 'User' }]}
179
- redirectTo="/unauthorized"
180
- >
181
- <UserManagement />
182
- </CanRoute>
183
-
184
- // 自定义 fallback 内容
185
- <CanRoute
186
- roles={['admin']}
187
- fallback={<div>您没有管理员权限</div>}
188
- >
189
- <AdminSettings />
190
- </CanRoute>
191
-
192
- // 在路由中使用
193
- <Routes>
194
- <Route path="/admin" element={
195
- <CanRoute permissions={[{ action: 'manage', subject: 'Task' }]}>
196
- <AdminPage />
197
- </CanRoute>
198
- } />
199
- </Routes>
122
+ <AuthProvider config={{
123
+ enable: true,
124
+ }}>
125
+ <App />
126
+ {/* Can/Hook 在其子树中生效 */}
127
+ </AuthProvider>
200
128
  ```
201
129
 
130
+ ## 核心 API - 面向 SDK 开发者
131
+
202
132
  ### PermissionClient
133
+
203
134
  - **作用**: 独立的权限数据获取客户端,供非 React 场景(或自定义状态管理)使用。
204
135
  - **方法**:
205
136
  - `constructor(config?: PermissionApiConfig)`
@@ -215,6 +146,7 @@ const data = await client.fetchPermissions('user-123');
215
146
  ```
216
147
 
217
148
  ### 能力工厂(与 CASL 交互)
149
+
218
150
  - `createAbility({ permissions?: Permission[], roles?: string[] }): Ability`
219
151
  - `updateAbility(ability: Ability, { permissions?: Permission[], roles?: string[] }): void`
220
152
  - `convertPermissionsToRules(permissions: Permission[], roles: string[]): CaslRule[]`
@@ -232,120 +164,20 @@ updateAbility(ability, { permissions: [{ id: 'p1', name: 'Task Read', sub: 'Task
232
164
  ---
233
165
 
234
166
  ## 类型与再导出
235
- - 从本包导出的类型:`Action`、`Subject`、`Permission`、`UserRole`、`PermissionApiResponse`、`PermissionApiConfig`、`AuthSdkConfig`、`CaslRule`。
236
- - 便捷再导出:`Ability`、`AbilityBuilder`、`AbilityClass`(来自 `@casl/ability`)。
237
167
 
238
- ---
168
+ - 从本包导出的类型:`PermissionApiResponse`、`PermissionApiConfig`、`AuthSdkConfig`、`CaslRule`。
169
+ - 最终用户代码中仅需要使用到的 API 有:
170
+ - `CanRole` 组件: 条件渲染,只有当角色 id 包含在 `roles` 中时才渲染子内容。
171
+ - `AbilityContext`: 从 React 上下文获取 CASL `Ability` 实例,用于权限判断。
172
+ - `ROLE_SUBJECT`: 特殊 subject `@role` 常量,用于角色判断。
239
173
 
240
174
  ## 集成建议与最佳实践
241
- - **权限接口返回**:`{ userId, roles: (string|UserRole)[], permissions: Permission[] }`,其中 `roles` 支持字符串或对象;SDK 会标准化为字符串数组。
242
- - **错误处理**:实现 `onError` 上报或提示;`onSuccess` 可做埋点。
243
- - **渲染时机**:根据 `useAuth()` 的 `isLoading`/`error` 渲染 Loading/Error 页,避免闪烁。
244
- - **与路由结合**:使用 `CanRoute` 组件做页面级访问控制,支持自动重定向和自定义 fallback 内容。
245
- - **路由守卫最佳实践**:
246
- - 优先使用 `fallback` 属性展示友好的无权限提示
247
- - 使用 `redirectTo` 重定向到专门的错误页面
248
- - 在 `AuthProvider` 中配置全局的 `noPermissionPath` 作为兜底
249
-
250
- ---
251
-
252
- ## 进阶示例
253
-
254
- ### 菜单按权限过滤
255
- ```tsx
256
- import { useAbility } from '@lark-apaas/auth-sdk';
257
-
258
- const menus = [
259
- { name: 'Dashboard', path: '/dashboard', p: { action: 'read', subject: 'Dashboard' } },
260
- { name: 'Users', path: '/users', p: { action: 'read', subject: 'User' } },
261
- { name: 'Settings', path: '/settings', p: { action: 'manage', subject: 'Settings' } },
262
- ];
263
-
264
- function Nav() {
265
- const ability = useAbility();
266
- return (
267
- <nav>
268
- {menus.map(m => ability.can(m.p.action, m.p.subject) && (
269
- <a key={m.path} href={m.path}>{m.name}</a>
270
- ))}
271
- </nav>
272
- );
273
- }
274
- ```
275
-
276
- ### 批量操作区
277
- ```tsx
278
- import { BatchCan } from '@lark-apaas/auth-sdk';
279
-
280
- <BatchCan permissions={[{ action: 'delete', subject: 'Task' }, { action: 'manage', subject: 'Task' }]}>
281
- <div className="bulk-actions">
282
- <button>批量删除</button>
283
- <button>导出全部</button>
284
- </div>
285
- </BatchCan>
286
- ```
287
-
288
- ### 路由级权限控制
289
- ```tsx
290
- import { CanRoute } from '@lark-apaas/auth-sdk';
291
- import { Routes, Route, Navigate } from 'react-router-dom';
292
175
 
293
- function App() {
294
- return (
295
- <Routes>
296
- {/* 公开路由 */}
297
- <Route path="/" element={<HomePage />} />
298
- <Route path="/login" element={<LoginPage />} />
299
-
300
- {/* 受保护的路由 */}
301
- <Route path="/dashboard" element={
302
- <CanRoute permissions={[{ action: 'read', subject: 'Dashboard' }]}>
303
- <Dashboard />
304
- </CanRoute>
305
- } />
306
-
307
- {/* 管理员路由 */}
308
- <Route path="/admin" element={
309
- <CanRoute
310
- roles={['admin']}
311
- fallback={<Navigate to="/unauthorized" replace />}
312
- >
313
- <AdminPanel />
314
- </CanRoute>
315
- } />
316
-
317
- {/* 多权限检查 */}
318
- <Route path="/reports" element={
319
- <CanRoute
320
- permissions={[
321
- { action: 'read', subject: 'Report' },
322
- { action: 'export', subject: 'Report' }
323
- ]}
324
- redirectTo="/no-access"
325
- >
326
- <ReportsPage />
327
- </CanRoute>
328
- } />
329
-
330
- {/* 错误页面 */}
331
- <Route path="/unauthorized" element={<UnauthorizedPage />} />
332
- <Route path="/no-access" element={<NoAccessPage />} />
333
- </Routes>
334
- );
335
- }
336
- ```
337
-
338
- ---
339
-
340
- ## 常见问题(FAQ)
341
- - **如何做角色判断?** 使用 `useCanRole`/`CanRole`;角色被映射为对特殊 subject `@role` 的 action。
342
- - **如何使用路由守卫?** 使用 `CanRoute` 组件包装需要保护的路由内容,支持权限和角色双重检查。
343
- - **CanRoute 的重定向优先级?** `fallback` > `redirectTo` > `noPermissionPath`(AuthProvider 配置)> `null`。
344
- - **CanRoute 需要 react-router-dom 依赖吗?** 是的,因为使用了 `Navigate` 和 `useLocation`,请确保项目中已安装。
176
+ - **直接使用组件**:`CanRole` 组件是直接使用的主要入口,可以使用 `AbilityContext` 获取 `Ability` 实例,在特殊场景下手动判断权限。
177
+ - **与路由结合**:页面级的访问控制需要结合路由库(如 `react-router-dom`)和 `AbilityContext` 来自行实现。
345
178
 
346
179
  ---
347
180
 
348
181
  ## 许可
349
- MIT
350
-
351
182
 
183
+ MIT
@@ -1,59 +1,19 @@
1
1
  import React from 'react';
2
- import { Ability } from '@casl/ability';
3
- import type { AuthSdkConfig, Permission } from './types';
2
+ import { MongoAbility } from '@casl/ability';
3
+ import type { AuthSdkConfig } from './types';
4
4
  /**
5
5
  * Ability Context - 用于在组件树中共享 Ability 实例
6
6
  */
7
- export declare const AbilityContext: React.Context<Ability<import("@casl/ability").AbilityTuple, import("@casl/ability").MongoQuery>>;
8
- /** 权限点位鉴权相关的 Hooks 和组件 */
9
- /**
10
- * Can 组件 - 基于 Context 的条件渲染组件
11
- * 使用 @casl/react 的 createContextualCan 创建,复用 CASL 的官方实现
12
- */
13
- export declare const Can: React.FunctionComponent<import("@casl/react").BoundCanProps<Ability<import("@casl/ability").AbilityTuple, import("@casl/ability").MongoQuery>>>;
14
- export interface UseCanPermissionProps {
15
- permissions?: Array<{
16
- action: string;
17
- subject: string;
18
- }>;
19
- or?: boolean;
20
- }
21
- export interface CanPermissionProps extends UseCanPermissionProps {
22
- children: React.ReactNode | ((allowed: boolean) => React.ReactNode);
23
- }
24
- export declare const useCanPermission: ({ permissions, or }: UseCanPermissionProps) => boolean;
25
- export declare function CanPermission({ permissions, children, or }: {
26
- permissions: Array<{
27
- action: string;
28
- subject: string;
29
- }>;
30
- children: React.ReactNode | ((allowed: boolean) => React.ReactNode);
31
- or?: boolean;
32
- }): import("react/jsx-runtime").JSX.Element | null;
33
- /** 角色鉴权相关的 Hooks 和组件 */
34
- export interface UseCanRoleProps {
35
- roles?: string[];
36
- and?: boolean;
37
- }
38
- export declare const useCanRole: ({ roles, and }: UseCanRoleProps) => boolean;
39
- export interface CanRoleProps extends UseCanRoleProps {
40
- children: React.ReactNode | ((allowed: boolean) => React.ReactNode);
41
- }
42
- export declare const CanRole: ({ roles, and, children }: CanRoleProps) => import("react/jsx-runtime").JSX.Element | null;
7
+ export declare const AbilityContext: React.Context<MongoAbility<import("@casl/ability").AbilityTuple, import("@casl/ability").MongoQuery>>;
43
8
  /**
44
9
  * Auth 状态 Context 类型定义
45
10
  * 存储权限数据和加载状态
46
11
  */
47
12
  interface AuthStateContextValue {
48
- noPermissionPath?: string;
49
- permissions: Permission[];
50
- roles: string[];
51
- userId: string | null;
52
- setUserId: (userId: string) => void;
13
+ ability: MongoAbility;
53
14
  isLoading: boolean;
54
15
  error: Error | null;
55
16
  fetchPermissions: (userId?: string) => Promise<void>;
56
- refetch: () => Promise<void>;
57
17
  }
58
18
  /**
59
19
  * Auth Provider Props
@@ -61,7 +21,6 @@ interface AuthStateContextValue {
61
21
  export interface AuthProviderProps {
62
22
  children: React.ReactNode;
63
23
  config?: AuthSdkConfig;
64
- userId?: string;
65
24
  }
66
25
  /**
67
26
  * Auth Provider 组件
@@ -87,6 +46,13 @@ export interface AuthProviderProps {
87
46
  * ```
88
47
  */
89
48
  export declare function AuthProvider({ children, config }: AuthProviderProps): import("react/jsx-runtime").JSX.Element;
49
+ /**
50
+ * 获取 Ability 实例
51
+ *
52
+ * @param permissionApiConfig - 权限 API 配置
53
+ * @returns Ability 实例或错误
54
+ */
55
+ export declare function getAbility(permissionApiConfig: AuthSdkConfig['permissionApi']): Promise<Error | MongoAbility<import("@casl/ability").AbilityTuple, import("@casl/ability").MongoQuery>>;
90
56
  /**
91
57
  * useAuth Hook - 获取权限数据和加载状态
92
58
  *
@@ -110,14 +76,14 @@ export declare function AuthProvider({ children, config }: AuthProviderProps): i
110
76
  */
111
77
  export declare function useAuth(): AuthStateContextValue;
112
78
  /**
113
- * useAbility Hook - 获取 Ability 实例
79
+ * useAuthAbility Hook - 获取 Ability 实例
114
80
  *
115
81
  * @example
116
82
  * ```tsx
117
- * import { useAbility } from '@lark-apaas/auth-sdk';
83
+ * import { useAuthAbility } from '@lark-apaas/auth-sdk';
118
84
  *
119
85
  * function MyComponent() {
120
- * const ability = useAbility();
86
+ * const ability = useAuthAbility();
121
87
  *
122
88
  * return (
123
89
  * <button disabled={ability.cannot('create', 'Task')}>
@@ -127,32 +93,46 @@ export declare function useAuth(): AuthStateContextValue;
127
93
  * }
128
94
  * ```
129
95
  */
130
- export declare function useAbility(): Ability;
96
+ export declare function useAuthAbility(): MongoAbility;
131
97
  /**
132
- * usePermissions Hook - 获取权限列表
98
+ * Can Component - 基于 Ability 实例的条件渲染组件
133
99
  *
134
100
  * @example
135
101
  * ```tsx
102
+ * import { Can } from '@lark-apaas/auth-sdk';
103
+ *
136
104
  * function MyComponent() {
137
- * const permissions = usePermissions();
138
- * return <div>You have {permissions.length} permissions</div>;
105
+ * return (
106
+ * <Can I="Admin" a="@role">
107
+ * <TaskList />
108
+ * </Can>
109
+ * );
139
110
  * }
140
111
  * ```
141
112
  */
142
- export declare function usePermissions(): Permission[];
113
+ export declare const Can: React.FunctionComponent<import("@casl/react").BoundCanProps<MongoAbility<import("@casl/ability").AbilityTuple, import("@casl/ability").MongoQuery>>>;
114
+ export declare const useCanRole: ({ roles }: {
115
+ roles: string[];
116
+ }) => boolean;
143
117
  /**
144
- * useRoles Hook - 获取角色列表
118
+ * CanRole Component - 基于 Ability 实例的角色条件渲染组件
145
119
  *
146
120
  * @example
147
121
  * ```tsx
122
+ * import { CanRole } from '@lark-apaas/auth-sdk';
123
+ *
148
124
  * function MyComponent() {
149
- * const roles = useRoles();
150
- * return <div>Your roles: {roles.join(', ')}</div>;
125
+ * return (
126
+ * <CanRole roles={['role_admin', 'role_editor']}>
127
+ * <TaskList />
128
+ * </CanRole>
129
+ * );
151
130
  * }
152
131
  * ```
153
132
  */
154
- export declare function useRoles(): string[];
155
- export declare function useUserId(): [string | null, (userId: string) => void];
156
- export declare function useNoPermissionPath(): string | undefined;
133
+ export declare function CanRole({ children, roles, }: {
134
+ children: React.ReactNode;
135
+ roles: string[];
136
+ }): import("react/jsx-runtime").JSX.Element | null;
157
137
  export {};
158
138
  //# sourceMappingURL=AuthProvider.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"AuthProvider.d.ts","sourceRoot":"","sources":["../src/AuthProvider.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"AuthProvider.d.ts","sourceRoot":"","sources":["../src/AuthProvider.tsx"],"names":[],"mappings":"AAAA,OAAO,KAMN,MAAM,OAAO,CAAC;AACf,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAK7C;;GAEG;AACH,eAAO,MAAM,cAAc,uGAE1B,CAAC;AAEF;;;GAGG;AACH,UAAU,qBAAqB;IAC7B,OAAO,EAAE,YAAY,CAAC;IACtB,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,gBAAgB,EAAE,CAAC,MAAM,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACtD;AAOD;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,MAAM,CAAC,EAAE,aAAa,CAAC;CACxB;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,YAAY,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,iBAAiB,2CA2DnE;AAED;;;;;GAKG;AACH,wBAAsB,UAAU,CAC9B,mBAAmB,EAAE,aAAa,CAAC,eAAe,CAAC,2GAiBpD;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,OAAO,IAAI,qBAAqB,CAQ/C;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,cAAc,IAAI,YAAY,CAE7C;AAED;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,GAAG,sJAA+C,CAAC;AAEhE,eAAO,MAAM,UAAU,GAAa,WAAW;IAAE,KAAK,EAAE,MAAM,EAAE,CAAA;CAAE,KAAG,OASpE,CAAC;AAEF;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,OAAO,CAAC,EACtB,QAAQ,EACR,KAAK,GACN,EAAE;IACD,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB,kDAIA"}
@@ -1,70 +1,25 @@
1
1
  import { Fragment, jsx } from "react/jsx-runtime";
2
2
  import { createContext, useCallback, useContext, useEffect, useState } from "react";
3
- import { createContextualCan } from "@casl/react";
4
3
  import { ROLE_SUBJECT, createAbility, updateAbility } from "./ability-factory.js";
5
4
  import { PermissionClient } from "./permission-client.js";
5
+ import { createContextualCan } from "@casl/react";
6
6
  const AbilityContext = /*#__PURE__*/ createContext(createAbility({
7
7
  permissions: [],
8
8
  roles: []
9
9
  }));
10
- const Can = createContextualCan(AbilityContext.Consumer);
11
- const useCanPermission = function({ permissions, or }) {
12
- const ability = useAbility();
13
- let allowed = false;
14
- allowed = or ? !permissions || !permissions.length || permissions.length > 0 && permissions.some(({ action, subject })=>ability.can(action, subject)) : !permissions || !permissions.length || permissions.length > 0 && permissions.every(({ action, subject })=>ability.can(action, subject));
15
- return !!allowed;
16
- };
17
- function CanPermission({ permissions, children, or }) {
18
- const allowed = useCanPermission({
19
- permissions,
20
- or
21
- });
22
- if ('function' == typeof children) return /*#__PURE__*/ jsx(Fragment, {
23
- children: children(allowed)
24
- });
25
- return allowed ? /*#__PURE__*/ jsx(Fragment, {
26
- children: children
27
- }) : null;
28
- }
29
- const useCanRole = function({ roles, and }) {
30
- const ability = useAbility();
31
- let allowed = false;
32
- allowed = and ? !roles || 0 === roles.length || roles.length > 0 && roles.every((role)=>ability.can(role, ROLE_SUBJECT)) : !roles || 0 === roles.length || roles.length > 0 && roles.some((role)=>ability.can(role, ROLE_SUBJECT));
33
- return !!allowed;
34
- };
35
- const CanRole = function({ roles, and, children }) {
36
- const allowed = useCanRole({
37
- roles,
38
- and
39
- });
40
- if ('function' == typeof children) return /*#__PURE__*/ jsx(Fragment, {
41
- children: children(allowed)
42
- });
43
- return allowed ? /*#__PURE__*/ jsx(Fragment, {
44
- children: children
45
- }) : null;
46
- };
47
10
  const AuthStateContext = /*#__PURE__*/ createContext(null);
48
11
  function AuthProvider({ children, config }) {
49
12
  const [ability] = useState(()=>createAbility({}));
50
- const [permissions, setPermissions] = useState([]);
51
- const [roles, setRoles] = useState([]);
52
- const [userId, setUserId] = useState(null);
53
13
  const [isLoading, setIsLoading] = useState(false);
54
14
  const [error, setError] = useState(null);
55
15
  const [client] = useState(()=>new PermissionClient(config?.permissionApi));
56
- const fetchPermissions = useCallback(async (uid)=>{
16
+ const fetchPermissions = useCallback(async ()=>{
57
17
  setIsLoading(true);
58
18
  setError(null);
59
19
  try {
60
- const data = await client.fetchPermissions(uid);
61
- const normalizedRoles = data.roles.map((role)=>'string' == typeof role ? role : role.name);
62
- setPermissions(data.permissions);
63
- setRoles(normalizedRoles);
64
- setUserId(data.userId);
20
+ const data = await client.fetchPermissions();
65
21
  updateAbility(ability, {
66
- permissions: data.permissions,
67
- roles: normalizedRoles
22
+ roles: data.roles
68
23
  });
69
24
  config?.onSuccess?.(data);
70
25
  } catch (err) {
@@ -79,28 +34,17 @@ function AuthProvider({ children, config }) {
79
34
  client,
80
35
  config
81
36
  ]);
82
- const refetch = useCallback(async ()=>{
83
- await fetchPermissions(userId);
84
- }, [
85
- userId,
86
- fetchPermissions
87
- ]);
88
37
  useEffect(()=>{
89
- if (config?.autoFetch !== false) fetchPermissions();
38
+ if (config?.enable !== false) fetchPermissions();
90
39
  }, [
91
- config?.autoFetch,
40
+ config?.enable,
92
41
  fetchPermissions
93
42
  ]);
94
43
  const stateContextValue = {
95
- permissions,
96
- roles,
97
- userId,
98
- setUserId,
99
- noPermissionPath: config?.noPermissionPath,
44
+ ability,
100
45
  isLoading,
101
46
  error,
102
- fetchPermissions,
103
- refetch
47
+ fetchPermissions
104
48
  };
105
49
  return /*#__PURE__*/ jsx(AbilityContext.Provider, {
106
50
  value: ability,
@@ -110,31 +54,40 @@ function AuthProvider({ children, config }) {
110
54
  })
111
55
  });
112
56
  }
57
+ async function getAbility(permissionApiConfig) {
58
+ const ability = createAbility({});
59
+ const client = new PermissionClient(permissionApiConfig);
60
+ try {
61
+ const data = await client.fetchPermissions();
62
+ updateAbility(ability, {
63
+ roles: data.roles
64
+ });
65
+ } catch (err) {
66
+ const error = err instanceof Error ? err : new Error(String(err));
67
+ return error;
68
+ }
69
+ return ability;
70
+ }
113
71
  function useAuth() {
114
72
  const context = useContext(AuthStateContext);
115
73
  if (!context) throw new Error('useAuth must be used within an AuthProvider');
116
74
  return context;
117
75
  }
118
- function useAbility() {
76
+ function useAuthAbility() {
119
77
  return useContext(AbilityContext);
120
78
  }
121
- function usePermissions() {
122
- const { permissions } = useAuth();
123
- return permissions;
124
- }
125
- function useRoles() {
126
- const { roles } = useAuth();
127
- return roles;
128
- }
129
- function useUserId() {
130
- const { userId, setUserId } = useAuth();
131
- return [
132
- userId,
133
- setUserId
134
- ];
135
- }
136
- function useNoPermissionPath() {
137
- const { noPermissionPath } = useAuth();
138
- return noPermissionPath;
79
+ const Can = createContextualCan(AbilityContext.Consumer);
80
+ const useCanRole = function({ roles }) {
81
+ const ability = useContext(AbilityContext);
82
+ const allowed = !roles || 0 === roles.length || roles.length > 0 && roles.some((role)=>ability.can(role, ROLE_SUBJECT));
83
+ return !!allowed;
84
+ };
85
+ function CanRole({ children, roles }) {
86
+ const allowed = useCanRole({
87
+ roles
88
+ });
89
+ return allowed ? /*#__PURE__*/ jsx(Fragment, {
90
+ children: children
91
+ }) : null;
139
92
  }
140
- export { AbilityContext, AuthProvider, Can, CanPermission, CanRole, useAbility, useAuth, useCanPermission, useCanRole, useNoPermissionPath, usePermissions, useRoles, useUserId };
93
+ export { AbilityContext, AuthProvider, Can, CanRole, getAbility, useAuth, useAuthAbility, useCanRole };
@@ -1,4 +1,4 @@
1
- import { Ability } from '@casl/ability';
1
+ import { MongoAbility as Ability } from '@casl/ability';
2
2
  import type { Permission, CaslRule } from './types';
3
3
  export declare const ROLE_SUBJECT = "@role";
4
4
  /**
@@ -6,9 +6,9 @@ export declare const ROLE_SUBJECT = "@role";
6
6
  */
7
7
  export declare function convertPermissionsToRules(permissions: Permission[], roles: string[]): CaslRule[];
8
8
  /**
9
- * 创建 Ability 实例
9
+ * 创建 MongoAbility 实例
10
10
  */
11
- export declare function createAbility({ permissions, roles }: {
11
+ export declare function createAbility({ permissions, roles, }: {
12
12
  permissions?: Permission[];
13
13
  roles?: string[];
14
14
  }): Ability;
@@ -1 +1 @@
1
- {"version":3,"file":"ability-factory.d.ts","sourceRoot":"","sources":["../src/ability-factory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAgC,MAAM,eAAe,CAAC;AACtE,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEpD,eAAO,MAAM,YAAY,UAAU,CAAC;AACpC;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,WAAW,EAAE,UAAU,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,QAAQ,EAAE,CAoBhG;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,EAAC,WAAW,EAAE,KAAK,EAAC,EAAE;IAAC,WAAW,CAAC,EAAE,UAAU,EAAE,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAA;CAAC,GAAG,OAAO,CAkB3G;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,OAAO,EAAE,EAAC,WAAW,EAAE,KAAK,EAAC,EAAE;IAAC,WAAW,CAAC,EAAE,UAAU,EAAE,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAA;CAAC,GAAG,IAAI,CAG1H"}
1
+ {"version":3,"file":"ability-factory.d.ts","sourceRoot":"","sources":["../src/ability-factory.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,IAAI,OAAO,EAGxB,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEpD,eAAO,MAAM,YAAY,UAAU,CAAC;AACpC;;GAEG;AACH,wBAAgB,yBAAyB,CACvC,WAAW,EAAE,UAAU,EAAE,EACzB,KAAK,EAAE,MAAM,EAAE,GACd,QAAQ,EAAE,CAmBZ;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,EAC5B,WAAW,EACX,KAAK,GACN,EAAE;IACD,WAAW,CAAC,EAAE,UAAU,EAAE,CAAC;IAC3B,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;CAClB,GAAG,OAAO,CAkBV;AAED;;GAEG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,OAAO,EAChB,EAAE,WAAW,EAAE,KAAK,EAAE,EAAE;IAAE,WAAW,CAAC,EAAE,UAAU,EAAE,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,GACvE,IAAI,CAGN"}
@@ -1,4 +1,4 @@
1
- import { Ability, AbilityBuilder } from "@casl/ability";
1
+ import { AbilityBuilder, createMongoAbility } from "@casl/ability";
2
2
  const ROLE_SUBJECT = '@role';
3
3
  function convertPermissionsToRules(permissions, roles) {
4
4
  const rules = [];
@@ -13,7 +13,7 @@ function convertPermissionsToRules(permissions, roles) {
13
13
  return rules;
14
14
  }
15
15
  function createAbility({ permissions, roles }) {
16
- const { build, can } = new AbilityBuilder(Ability);
16
+ const { build, can } = new AbilityBuilder(createMongoAbility);
17
17
  const rules = convertPermissionsToRules(permissions || [], roles || []);
18
18
  for (const rule of rules)if (Array.isArray(rule.action)) for (const action of rule.action)can(action, rule.subject, rule.fields);
19
19
  else can(rule.action, rule.subject, rule.fields);
package/lib/index.d.ts CHANGED
@@ -4,12 +4,9 @@
4
4
  * 基于 CASL 的前端鉴权 SDK
5
5
  * 封装了权限数据获取和 Ability 初始化逻辑
6
6
  */
7
- export type { Action, Subject, Permission, UserRole, PermissionApiResponse, PermissionApiConfig, AuthSdkConfig, CaslRule, } from './types';
8
- export { createAbility, updateAbility, convertPermissionsToRules, } from './ability-factory';
7
+ export type { PermissionApiResponse, PermissionApiConfig, AuthSdkConfig, } from './types';
8
+ export { ROLE_SUBJECT } from './ability-factory';
9
9
  export { PermissionClient } from './permission-client';
10
- export { AuthProvider, Can, useCanPermission, CanPermission, useCanRole, CanRole, useAuth, useAbility, usePermissions, useRoles, AbilityContext, useUserId, useNoPermissionPath, } from './AuthProvider';
11
- export { CanRoute } from './CanRoute';
12
- export type { AuthProviderProps, UseCanPermissionProps, UseCanRoleProps, CanPermissionProps, CanRoleProps, } from './AuthProvider';
13
- export type { CanRouteProps } from './CanRoute';
14
- export { Ability, AbilityBuilder, type AbilityClass } from '@casl/ability';
10
+ export { AuthProvider, useAuth, CanRole, AbilityContext } from './AuthProvider';
11
+ export type { AuthProviderProps } from './AuthProvider';
15
12
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,YAAY,EACV,MAAM,EACN,OAAO,EACP,UAAU,EACV,QAAQ,EACR,qBAAqB,EACrB,mBAAmB,EACnB,aAAa,EACb,QAAQ,GACT,MAAM,SAAS,CAAC;AAGjB,OAAO,EACL,aAAa,EACb,aAAa,EACb,yBAAyB,GAC1B,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAGvD,OAAO,EACL,YAAY,EACZ,GAAG,EACH,gBAAgB,EAChB,aAAa,EACb,UAAU,EACV,OAAO,EACP,OAAO,EACP,UAAU,EACV,cAAc,EACd,QAAQ,EACR,cAAc,EACd,SAAS,EACT,mBAAmB,GACpB,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEtC,YAAY,EACV,iBAAiB,EACjB,qBAAqB,EACrB,eAAe,EACf,kBAAkB,EAClB,YAAY,GACb,MAAM,gBAAgB,CAAC;AAExB,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAGhD,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,KAAK,YAAY,EAAE,MAAM,eAAe,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,YAAY,EACV,qBAAqB,EACrB,mBAAmB,EACnB,aAAa,GACd,MAAM,SAAS,CAAC;AAGjB,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAGvD,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAEhF,YAAY,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC"}
package/lib/index.js CHANGED
@@ -1,6 +1,4 @@
1
- import { convertPermissionsToRules, createAbility, updateAbility } from "./ability-factory.js";
1
+ import { ROLE_SUBJECT } from "./ability-factory.js";
2
2
  import { PermissionClient } from "./permission-client.js";
3
- import { AbilityContext, AuthProvider, Can, CanPermission, CanRole, useAbility, useAuth, useCanPermission, useCanRole, useNoPermissionPath, usePermissions, useRoles, useUserId } from "./AuthProvider.js";
4
- import { CanRoute } from "./CanRoute.js";
5
- import { Ability, AbilityBuilder } from "@casl/ability";
6
- export { Ability, AbilityBuilder, AbilityContext, AuthProvider, Can, CanPermission, CanRole, CanRoute, PermissionClient, convertPermissionsToRules, createAbility, updateAbility, useAbility, useAuth, useCanPermission, useCanRole, useNoPermissionPath, usePermissions, useRoles, useUserId };
3
+ import { AbilityContext, AuthProvider, CanRole, useAuth } from "./AuthProvider.js";
4
+ export { AbilityContext, AuthProvider, CanRole, PermissionClient, ROLE_SUBJECT, useAuth };
@@ -7,9 +7,8 @@ export declare class PermissionClient {
7
7
  constructor(config?: PermissionApiConfig);
8
8
  /**
9
9
  * 获取用户权限数据
10
- * @param userId - 用户ID(可选,用于向后兼容。如果端点包含 :userId,则会替换)
11
10
  */
12
- fetchPermissions(userId?: string): Promise<PermissionApiResponse>;
11
+ fetchPermissions(): Promise<PermissionApiResponse>;
13
12
  /**
14
13
  * 更新配置
15
14
  */
@@ -1 +1 @@
1
- {"version":3,"file":"permission-client.d.ts","sourceRoot":"","sources":["../src/permission-client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAY1E;;GAEG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,MAAM,CAAsB;gBAExB,MAAM,CAAC,EAAE,mBAAmB;IAOxC;;;OAGG;IACG,gBAAgB,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,CAAC;IAsEvE;;OAEG;IACH,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,mBAAmB,CAAC,GAAG,IAAI;IAOxD;;OAEG;IACH,SAAS,IAAI,mBAAmB;CAGjC"}
1
+ {"version":3,"file":"permission-client.d.ts","sourceRoot":"","sources":["../src/permission-client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAU1E;;GAEG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,MAAM,CAAsB;gBAExB,MAAM,CAAC,EAAE,mBAAmB;IAOxC;;OAEG;IACG,gBAAgB,IAAI,OAAO,CAAC,qBAAqB,CAAC;IAyDxD;;OAEG;IACH,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,mBAAmB,CAAC,GAAG,IAAI;IAOxD;;OAEG;IACH,SAAS,IAAI,mBAAmB;CAGjC"}
@@ -1,8 +1,6 @@
1
1
  const DEFAULT_CONFIG = {
2
2
  baseUrl: '',
3
- endpoint: '/api/permissions',
4
- timeout: 5000,
5
- withCredentials: true
3
+ timeout: 5000
6
4
  };
7
5
  class PermissionClient {
8
6
  constructor(config){
@@ -11,35 +9,34 @@ class PermissionClient {
11
9
  ...config
12
10
  };
13
11
  }
14
- async fetchPermissions(userId) {
15
- const { baseUrl = DEFAULT_CONFIG.baseUrl, endpoint = DEFAULT_CONFIG.endpoint, timeout = DEFAULT_CONFIG.timeout, headers = {}, apiToken, withCredentials = DEFAULT_CONFIG.withCredentials } = this.config;
16
- let finalEndpoint = endpoint;
17
- if (userId) finalEndpoint = endpoint.includes(':userId') ? endpoint.replace(':userId', userId) : `${endpoint}?mockUserId=${userId}`;
18
- const url = `${baseUrl}${finalEndpoint}`;
12
+ async fetchPermissions() {
13
+ const { timeout = DEFAULT_CONFIG.timeout, headers = {} } = this.config;
14
+ const { appId } = window;
15
+ if (!appId) throw new Error('appId is required');
16
+ const url = `/spark/app/${appId}/runtime/api/v1/permissions/roles`;
19
17
  const requestHeaders = {
20
18
  'Content-Type': 'application/json',
21
19
  ...headers
22
20
  };
23
- if (apiToken) requestHeaders['Authorization'] = `Bearer ${apiToken}`;
24
21
  const controller = new AbortController();
25
22
  const timeoutId = setTimeout(()=>controller.abort(), timeout);
26
23
  try {
27
24
  const response = await fetch(url, {
28
- method: 'GET',
25
+ method: 'POST',
29
26
  headers: requestHeaders,
30
27
  signal: controller.signal,
31
- credentials: withCredentials ? 'include' : 'same-origin'
28
+ credentials: 'include'
32
29
  });
33
30
  clearTimeout(timeoutId);
34
31
  if (!response.ok) throw new Error(`Permission API returned ${response.status}: ${response.statusText}`);
35
32
  const data = await response.json();
36
33
  return {
37
- ...data,
38
- fetchedAt: data.fetchedAt || new Date().toISOString()
34
+ roles: data.data?.roleList || [],
35
+ fetchedAt: new Date()
39
36
  };
40
37
  } catch (error) {
41
38
  clearTimeout(timeoutId);
42
- if ('AbortError' === error.name) throw new Error(`Permission API request timeout after ${timeout}ms`);
39
+ if (error instanceof Error && 'AbortError' === error.name) throw new Error(`Permission API request timeout after ${timeout}ms`);
43
40
  throw error;
44
41
  }
45
42
  }
package/lib/types.d.ts CHANGED
@@ -27,9 +27,7 @@ export interface UserRole {
27
27
  * 权限 API 响应数据
28
28
  */
29
29
  export interface PermissionApiResponse {
30
- userId: string;
31
- roles: (string | UserRole)[];
32
- permissions: Permission[];
30
+ roles: string[];
33
31
  fetchedAt?: string | Date;
34
32
  }
35
33
  /**
@@ -41,11 +39,6 @@ export interface PermissionApiConfig {
41
39
  * @default ''
42
40
  */
43
41
  baseUrl?: string;
44
- /**
45
- * API 端点路径
46
- * @default '/api/permissions'
47
- */
48
- endpoint?: string;
49
42
  /**
50
43
  * 请求超时时间(毫秒)
51
44
  * @default 5000
@@ -55,34 +48,20 @@ export interface PermissionApiConfig {
55
48
  * 自定义请求头
56
49
  */
57
50
  headers?: Record<string, string>;
58
- /**
59
- * API Token,会自动添加到 Authorization header
60
- */
61
- apiToken?: string;
62
- /**
63
- * 是否携带凭证(cookies)
64
- * @default true
65
- */
66
- withCredentials?: boolean;
67
51
  }
68
52
  /**
69
53
  * Auth SDK 配置选项
70
54
  */
71
55
  export interface AuthSdkConfig {
72
- /**
73
- * 无权限路径
74
- * @default '/no-access'
75
- */
76
- noPermissionPath?: string;
77
56
  /**
78
57
  * 权限 API 配置
79
58
  */
80
59
  permissionApi?: PermissionApiConfig;
81
60
  /**
82
- * 是否在初始化时自动获取权限
61
+ * 是否在初始化时使用获取权限
83
62
  * @default false
84
63
  */
85
- autoFetch?: boolean;
64
+ enable?: boolean;
86
65
  /**
87
66
  * 获取权限失败时的错误处理
88
67
  */
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,MAAM,MAAM,GAAG,QAAQ,GAAG,MAAM,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,MAAM,CAAC;AAEjF;;GAEG;AACH,MAAM,MAAM,OAAO,GAAG,MAAM,CAAC;AAE7B;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,OAAO,CAAC;IACb,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,CAAC,MAAM,GAAG,QAAQ,CAAC,EAAE,CAAC;IAC7B,WAAW,EAAE,UAAU,EAAE,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAEjC;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;;OAGG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B;;;OAGG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B;;OAEG;IACH,aAAa,CAAC,EAAE,mBAAmB,CAAC;IAEpC;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB;;OAEG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IAEjC;;OAEG;IACH,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,qBAAqB,KAAK,IAAI,CAAC;CACnD;AAED;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC1B,OAAO,EAAE,OAAO,GAAG,OAAO,EAAE,CAAC;IAC7B,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,MAAM,MAAM,GACd,QAAQ,GACR,MAAM,GACN,QAAQ,GACR,QAAQ,GACR,QAAQ,GACR,MAAM,CAAC;AAEX;;GAEG;AACH,MAAM,MAAM,OAAO,GAAG,MAAM,CAAC;AAE7B;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,OAAO,CAAC;IACb,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B;;OAEG;IACH,aAAa,CAAC,EAAE,mBAAmB,CAAC;IAEpC;;;OAGG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB;;OAEG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IAEjC;;OAEG;IACH,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,qBAAqB,KAAK,IAAI,CAAC;CACnD;AAED;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC1B,OAAO,EAAE,OAAO,GAAG,OAAO,EAAE,CAAC;IAC7B,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lark-apaas/auth-sdk",
3
- "version": "0.1.0-alpha.0",
3
+ "version": "0.1.0-alpha.10",
4
4
  "description": "基于 CASL 的前端鉴权 SDK",
5
5
  "types": "./lib/index.d.ts",
6
6
  "main": "./lib/index.js",
package/lib/CanRoute.d.ts DELETED
@@ -1,42 +0,0 @@
1
- import { type ReactNode } from 'react';
2
- /**
3
- * CanRoute 组件 Props
4
- */
5
- export interface CanRouteProps {
6
- permissions?: Array<{
7
- action: string;
8
- subject: string;
9
- }>;
10
- roles?: string[];
11
- children: ReactNode;
12
- redirectTo?: string;
13
- fallback?: ReactNode;
14
- }
15
- /**
16
- * CanRoute 组件 - 路由级别的权限守卫
17
- *
18
- * 基于权限和角色进行路由级别的访问控制
19
- * 当权限或角色检查失败时,会渲染 fallback 内容或返回 null
20
- *
21
- * @example
22
- * ```tsx
23
- * import { CanRoute } from '@lark-apaas/auth-sdk';
24
- * import { Navigate, useLocation } from 'react-router-dom';
25
- *
26
- * function ProtectedRoute() {
27
- * const location = useLocation();
28
- *
29
- * return (
30
- * <CanRoute
31
- * permissions={[{ action: 'read', subject: 'Task' }]}
32
- * roles={['admin']}
33
- * fallback={<Navigate to="/forbidden" replace state={{ from: location }} />}
34
- * >
35
- * <TaskList />
36
- * </CanRoute>
37
- * );
38
- * }
39
- * ```
40
- */
41
- export declare function CanRoute({ permissions, roles, children, fallback, redirectTo }: CanRouteProps): import("react/jsx-runtime").JSX.Element | null;
42
- //# sourceMappingURL=CanRoute.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"CanRoute.d.ts","sourceRoot":"","sources":["../src/CanRoute.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAItC;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,WAAW,CAAC,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACzD,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,EAAE,SAAS,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,QAAQ,CAAC,EAAE,WAAW,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAe,EAAE,UAAe,EAAE,EAAE,aAAa,kDAezG"}
package/lib/CanRoute.js DELETED
@@ -1,27 +0,0 @@
1
- import { Fragment, jsx } from "react/jsx-runtime";
2
- import { Navigate, useLocation } from "react-router-dom";
3
- import { useCanPermission, useCanRole, useNoPermissionPath } from "./AuthProvider.js";
4
- function CanRoute({ permissions, roles, children, fallback = null, redirectTo = '' }) {
5
- const noPermissionPath = useNoPermissionPath();
6
- const location = useLocation();
7
- let allPass = true;
8
- if (!useCanPermission({
9
- permissions
10
- })) allPass = false;
11
- if (!useCanRole({
12
- roles
13
- })) allPass = false;
14
- if (!allPass) return fallback ? /*#__PURE__*/ jsx(Fragment, {
15
- children: fallback
16
- }) : redirectTo || noPermissionPath ? /*#__PURE__*/ jsx(Navigate, {
17
- to: redirectTo || noPermissionPath,
18
- replace: true,
19
- state: {
20
- from: location
21
- }
22
- }) : null;
23
- return /*#__PURE__*/ jsx(Fragment, {
24
- children: children
25
- });
26
- }
27
- export { CanRoute };
@@ -1,42 +0,0 @@
1
- import { type ReactNode } from 'react';
2
- /**
3
- * RouteGuard 组件 Props
4
- */
5
- export interface RouteGuardProps {
6
- permissions?: Array<{
7
- action: string;
8
- subject: string;
9
- }>;
10
- roles?: string[];
11
- children: ReactNode;
12
- redirectTo?: string;
13
- fallback?: ReactNode;
14
- }
15
- /**
16
- * RouteGuard 组件 - 路由级别的权限守卫
17
- *
18
- * 基于权限和角色进行路由级别的访问控制
19
- * 当权限或角色检查失败时,会渲染 fallback 内容或返回 null
20
- *
21
- * @example
22
- * ```tsx
23
- * import { RouteGuard } from '@lark-apaas/auth-sdk';
24
- * import { Navigate, useLocation } from 'react-router-dom';
25
- *
26
- * function ProtectedRoute() {
27
- * const location = useLocation();
28
- *
29
- * return (
30
- * <RouteGuard
31
- * permissions={[{ action: 'read', subject: 'Task' }]}
32
- * roles={['admin']}
33
- * fallback={<Navigate to="/forbidden" replace state={{ from: location }} />}
34
- * >
35
- * <TaskList />
36
- * </RouteGuard>
37
- * );
38
- * }
39
- * ```
40
- */
41
- export declare function RouteGuard({ permissions, roles, children, fallback, redirectTo }: RouteGuardProps): import("react/jsx-runtime").JSX.Element | null;
42
- //# sourceMappingURL=RouteGuard.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"RouteGuard.d.ts","sourceRoot":"","sources":["../src/RouteGuard.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAGtC;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,WAAW,CAAC,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACzD,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,EAAE,SAAS,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,UAAU,CAAC,EAAE,WAAW,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAe,EAAE,UAAe,EAAE,EAAE,eAAe,kDAe7G"}
package/lib/RouteGuard.js DELETED
@@ -1,27 +0,0 @@
1
- import { Fragment, jsx } from "react/jsx-runtime";
2
- import { Navigate, useLocation } from "react-router-dom";
3
- import { useCanPermission, useCanRole, useNoPermissionPath } from "./AuthProvider.js";
4
- function RouteGuard({ permissions, roles, children, fallback = null, redirectTo = '' }) {
5
- const noPermissionPath = useNoPermissionPath();
6
- const location = useLocation();
7
- let allPass = true;
8
- if (!useCanPermission({
9
- permissions
10
- })) allPass = false;
11
- if (!useCanRole({
12
- roles
13
- })) allPass = false;
14
- if (!allPass) return fallback ? /*#__PURE__*/ jsx(Fragment, {
15
- children: fallback
16
- }) : redirectTo || noPermissionPath ? /*#__PURE__*/ jsx(Navigate, {
17
- to: redirectTo || noPermissionPath,
18
- replace: true,
19
- state: {
20
- from: location
21
- }
22
- }) : null;
23
- return /*#__PURE__*/ jsx(Fragment, {
24
- children: children
25
- });
26
- }
27
- export { RouteGuard };