exguard-client 1.0.0
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/IMPLEMENTATION_GUIDE.md +609 -0
- package/README.md +332 -0
- package/dist/index.cjs +1002 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +438 -0
- package/dist/index.d.ts +438 -0
- package/dist/index.js +975 -0
- package/dist/index.js.map +1 -0
- package/package.json +60 -0
- package/scripts/setup.js +197 -0
|
@@ -0,0 +1,609 @@
|
|
|
1
|
+
# ExGuard Client Implementation Guide
|
|
2
|
+
|
|
3
|
+
Complete step-by-step guide to implement @empowerx/exguard-client in your React application.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [Prerequisites](#prerequisites)
|
|
8
|
+
- [Installation](#installation)
|
|
9
|
+
- [Project Setup](#project-setup)
|
|
10
|
+
- [Configuration](#configuration)
|
|
11
|
+
- [Basic Usage](#basic-usage)
|
|
12
|
+
- [Advanced Features](#advanced-features)
|
|
13
|
+
- [Best Practices](#best-practices)
|
|
14
|
+
- [Troubleshooting](#troubleshooting)
|
|
15
|
+
|
|
16
|
+
## Prerequisites
|
|
17
|
+
|
|
18
|
+
Before installing ExGuard client, ensure you have:
|
|
19
|
+
|
|
20
|
+
1. **Node.js** >= 18.0.0
|
|
21
|
+
2. **React** >= 18.0.0 or >= 19.0.0
|
|
22
|
+
3. **TypeScript** (recommended for type safety)
|
|
23
|
+
4. **ExGuard Backend API** running and accessible
|
|
24
|
+
|
|
25
|
+
### Required Peer Dependencies
|
|
26
|
+
|
|
27
|
+
```json
|
|
28
|
+
{
|
|
29
|
+
"react": "^18.0.0 || ^19.0.0",
|
|
30
|
+
"react-dom": "^18.0.0 || ^19.0.0",
|
|
31
|
+
"react-router": "^7.0.0",
|
|
32
|
+
"axios": "^1.0.0",
|
|
33
|
+
"socket.io-client": "^4.0.0"
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Installation
|
|
38
|
+
|
|
39
|
+
### Step 1: Install the Package
|
|
40
|
+
|
|
41
|
+
Using pnpm (recommended):
|
|
42
|
+
```bash
|
|
43
|
+
pnpm add @empowerx/exguard-client
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Using npm:
|
|
47
|
+
```bash
|
|
48
|
+
npm install @empowerx/exguard-client
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Using yarn:
|
|
52
|
+
```bash
|
|
53
|
+
yarn add @empowerx/exguard-client
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Step 2: Install Peer Dependencies
|
|
57
|
+
|
|
58
|
+
If not already installed:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
# Using pnpm
|
|
62
|
+
pnpm add react react-dom react-router axios socket.io-client
|
|
63
|
+
|
|
64
|
+
# Using npm
|
|
65
|
+
npm install react react-dom react-router axios socket.io-client
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Project Setup
|
|
69
|
+
|
|
70
|
+
### Option 1: Using the Setup Script (Recommended)
|
|
71
|
+
|
|
72
|
+
The package includes a setup script that creates a clean module structure in your project:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
npx exguard-setup
|
|
76
|
+
# or
|
|
77
|
+
pnpm exguard-setup
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
This creates:
|
|
81
|
+
```
|
|
82
|
+
src/features/exguard/
|
|
83
|
+
├── index.ts # Re-exports from @empowerx/exguard-client
|
|
84
|
+
├── README.md # Module-specific documentation
|
|
85
|
+
└── config/
|
|
86
|
+
└── exguard.config.ts # Optional configuration file
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
**Benefits:**
|
|
90
|
+
- Clean separation between package and application code
|
|
91
|
+
- Easy to customize or extend ExGuard functionality
|
|
92
|
+
- Consistent import paths across your application
|
|
93
|
+
- Type-safe re-exports
|
|
94
|
+
|
|
95
|
+
### Option 2: Direct Package Imports
|
|
96
|
+
|
|
97
|
+
You can also import directly from the package without the setup script:
|
|
98
|
+
|
|
99
|
+
```tsx
|
|
100
|
+
import { PermissionGuard, useUserAccess } from '@empowerx/exguard-client';
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Configuration
|
|
104
|
+
|
|
105
|
+
### Step 1: Configure ExGuard API URL
|
|
106
|
+
|
|
107
|
+
In your application entry point (`main.tsx` or `App.tsx`):
|
|
108
|
+
|
|
109
|
+
```tsx
|
|
110
|
+
// src/main.tsx
|
|
111
|
+
import { StrictMode } from 'react';
|
|
112
|
+
import { createRoot } from 'react-dom/client';
|
|
113
|
+
import { ExGuardRealtimeProvider, setExGuardConfig } from '@/features/exguard';
|
|
114
|
+
import App from './App';
|
|
115
|
+
|
|
116
|
+
// Configure ExGuard API URL
|
|
117
|
+
setExGuardConfig({
|
|
118
|
+
apiUrl: import.meta.env.VITE_GUARD_APP_URL as string,
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
createRoot(document.getElementById('root')!).render(
|
|
122
|
+
<StrictMode>
|
|
123
|
+
<ExGuardRealtimeProvider>
|
|
124
|
+
<App />
|
|
125
|
+
</ExGuardRealtimeProvider>
|
|
126
|
+
</StrictMode>
|
|
127
|
+
);
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Step 2: Environment Variables
|
|
131
|
+
|
|
132
|
+
Create or update your `.env` file:
|
|
133
|
+
|
|
134
|
+
**For Vite:**
|
|
135
|
+
```env
|
|
136
|
+
VITE_GUARD_APP_URL=https://your-exguard-api.com
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
**For Next.js:**
|
|
140
|
+
```env
|
|
141
|
+
NEXT_PUBLIC_GUARD_APP_URL=https://your-exguard-api.com
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
**For Create React App:**
|
|
145
|
+
```env
|
|
146
|
+
REACT_APP_GUARD_APP_URL=https://your-exguard-api.com
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Step 3: Wrap Your Application
|
|
150
|
+
|
|
151
|
+
Ensure your app is wrapped with `ExGuardRealtimeProvider`:
|
|
152
|
+
|
|
153
|
+
```tsx
|
|
154
|
+
// src/App.tsx
|
|
155
|
+
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
156
|
+
import { RouterProvider } from 'react-router';
|
|
157
|
+
import { ExGuardRealtimeProvider, setExGuardConfig } from '@/features/exguard';
|
|
158
|
+
import router from './router';
|
|
159
|
+
|
|
160
|
+
const queryClient = new QueryClient();
|
|
161
|
+
|
|
162
|
+
// Configure before rendering
|
|
163
|
+
setExGuardConfig({
|
|
164
|
+
apiUrl: import.meta.env.VITE_GUARD_APP_URL as string,
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
function App() {
|
|
168
|
+
return (
|
|
169
|
+
<QueryClientProvider client={queryClient}>
|
|
170
|
+
<ExGuardRealtimeProvider>
|
|
171
|
+
<RouterProvider router={router} />
|
|
172
|
+
</ExGuardRealtimeProvider>
|
|
173
|
+
</QueryClientProvider>
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export default App;
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## Basic Usage
|
|
181
|
+
|
|
182
|
+
### 1. Route Protection with PermissionGuard
|
|
183
|
+
|
|
184
|
+
Protect entire routes or components based on module access:
|
|
185
|
+
|
|
186
|
+
```tsx
|
|
187
|
+
// src/features/profiles/pages/ProfilesPage.tsx
|
|
188
|
+
import { PermissionGuard } from '@/features/exguard';
|
|
189
|
+
import { ProfilesList } from '../components/ProfilesList';
|
|
190
|
+
|
|
191
|
+
export function ProfilesPage() {
|
|
192
|
+
return (
|
|
193
|
+
<PermissionGuard
|
|
194
|
+
module="EXID"
|
|
195
|
+
fallbackPath="/unauthorized"
|
|
196
|
+
>
|
|
197
|
+
<ProfilesList />
|
|
198
|
+
</PermissionGuard>
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### 2. Permission-Based Feature Access
|
|
204
|
+
|
|
205
|
+
Protect specific features or actions:
|
|
206
|
+
|
|
207
|
+
```tsx
|
|
208
|
+
// src/features/profiles/pages/ProfileDetailPage.tsx
|
|
209
|
+
import { PermissionGuard } from '@/features/exguard';
|
|
210
|
+
|
|
211
|
+
export function ProfileDetailPage() {
|
|
212
|
+
return (
|
|
213
|
+
<div>
|
|
214
|
+
<h1>Profile Details</h1>
|
|
215
|
+
|
|
216
|
+
{/* Only users with profile:update permission can see this */}
|
|
217
|
+
<PermissionGuard
|
|
218
|
+
module="EXID"
|
|
219
|
+
permission="profile:update"
|
|
220
|
+
requirePermission={true}
|
|
221
|
+
>
|
|
222
|
+
<button>Edit Profile</button>
|
|
223
|
+
</PermissionGuard>
|
|
224
|
+
|
|
225
|
+
{/* Only users with profile:delete permission can see this */}
|
|
226
|
+
<PermissionGuard
|
|
227
|
+
module="EXID"
|
|
228
|
+
permission="profile:delete"
|
|
229
|
+
requirePermission={true}
|
|
230
|
+
>
|
|
231
|
+
<button>Delete Profile</button>
|
|
232
|
+
</PermissionGuard>
|
|
233
|
+
</div>
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### 3. Using useUserAccess Hook
|
|
239
|
+
|
|
240
|
+
Access user permissions programmatically:
|
|
241
|
+
|
|
242
|
+
```tsx
|
|
243
|
+
// src/features/profiles/components/ProfileActions.tsx
|
|
244
|
+
import { useUserAccess } from '@/features/exguard';
|
|
245
|
+
|
|
246
|
+
export function ProfileActions({ profileId }: { profileId: string }) {
|
|
247
|
+
const { hasModuleAccess, hasPermission, isLoading } = useUserAccess();
|
|
248
|
+
|
|
249
|
+
if (isLoading) {
|
|
250
|
+
return <div>Loading permissions...</div>;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// Check module access
|
|
254
|
+
if (!hasModuleAccess('EXID')) {
|
|
255
|
+
return <div>Access denied to EXID module</div>;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
return (
|
|
259
|
+
<div className="flex gap-2">
|
|
260
|
+
{/* Show edit button only if user has permission */}
|
|
261
|
+
{hasPermission('EXID', 'profile:update') && (
|
|
262
|
+
<button onClick={() => handleEdit(profileId)}>
|
|
263
|
+
Edit
|
|
264
|
+
</button>
|
|
265
|
+
)}
|
|
266
|
+
|
|
267
|
+
{/* Show delete button only if user has permission */}
|
|
268
|
+
{hasPermission('EXID', 'profile:delete') && (
|
|
269
|
+
<button onClick={() => handleDelete(profileId)}>
|
|
270
|
+
Delete
|
|
271
|
+
</button>
|
|
272
|
+
)}
|
|
273
|
+
|
|
274
|
+
{/* View is always available if user has module access */}
|
|
275
|
+
<button onClick={() => handleView(profileId)}>
|
|
276
|
+
View
|
|
277
|
+
</button>
|
|
278
|
+
</div>
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
### 4. API Functions
|
|
284
|
+
|
|
285
|
+
Verify tokens and get user access data:
|
|
286
|
+
|
|
287
|
+
```tsx
|
|
288
|
+
// src/features/auth/hooks/useAuth.ts
|
|
289
|
+
import { verifyToken, getUserAccess } from '@/features/exguard';
|
|
290
|
+
|
|
291
|
+
export function useAuth() {
|
|
292
|
+
const checkAuthentication = async () => {
|
|
293
|
+
try {
|
|
294
|
+
const result = await verifyToken();
|
|
295
|
+
|
|
296
|
+
if (!result.isValid) {
|
|
297
|
+
// Token is invalid, redirect to login
|
|
298
|
+
window.location.href = '/login';
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// Get user's access data
|
|
303
|
+
const userAccess = await getUserAccess();
|
|
304
|
+
console.log('User modules:', userAccess.modules);
|
|
305
|
+
console.log('User roles:', userAccess.roles);
|
|
306
|
+
|
|
307
|
+
return userAccess;
|
|
308
|
+
} catch (error) {
|
|
309
|
+
console.error('Authentication failed:', error);
|
|
310
|
+
throw error;
|
|
311
|
+
}
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
return { checkAuthentication };
|
|
315
|
+
}
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
## Advanced Features
|
|
319
|
+
|
|
320
|
+
### 1. Realtime RBAC Updates
|
|
321
|
+
|
|
322
|
+
Listen to real-time permission changes:
|
|
323
|
+
|
|
324
|
+
```tsx
|
|
325
|
+
// src/features/admin/pages/UsersPage.tsx
|
|
326
|
+
import { useEffect, useState } from 'react';
|
|
327
|
+
import {
|
|
328
|
+
useExGuardRealtime,
|
|
329
|
+
useExGuardRealtimeSubscription,
|
|
330
|
+
EXGUARD_RBAC_EVENTS
|
|
331
|
+
} from '@/features/exguard';
|
|
332
|
+
|
|
333
|
+
export function UsersPage() {
|
|
334
|
+
const [users, setUsers] = useState([]);
|
|
335
|
+
const { isConnected } = useExGuardRealtime();
|
|
336
|
+
|
|
337
|
+
// Subscribe to user RBAC updates
|
|
338
|
+
useExGuardRealtimeSubscription(
|
|
339
|
+
EXGUARD_RBAC_EVENTS.USER_ROLES_UPDATED,
|
|
340
|
+
(payload) => {
|
|
341
|
+
console.log('User roles updated:', payload);
|
|
342
|
+
// Refresh user list or update specific user
|
|
343
|
+
refreshUsers();
|
|
344
|
+
}
|
|
345
|
+
);
|
|
346
|
+
|
|
347
|
+
// Subscribe to permission changes
|
|
348
|
+
useExGuardRealtimeSubscription(
|
|
349
|
+
EXGUARD_RBAC_EVENTS.RBAC_PERMISSIONS_UPDATED,
|
|
350
|
+
(payload) => {
|
|
351
|
+
console.log('Permissions updated:', payload);
|
|
352
|
+
// Refresh current user's access data
|
|
353
|
+
window.location.reload(); // or use a more elegant refresh
|
|
354
|
+
}
|
|
355
|
+
);
|
|
356
|
+
|
|
357
|
+
return (
|
|
358
|
+
<div>
|
|
359
|
+
<div>WebSocket: {isConnected ? '✅ Connected' : '❌ Disconnected'}</div>
|
|
360
|
+
{/* User list */}
|
|
361
|
+
</div>
|
|
362
|
+
);
|
|
363
|
+
}
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
### 2. Custom RBAC Refresh Logic
|
|
367
|
+
|
|
368
|
+
Register callbacks for when RBAC data needs to be refreshed:
|
|
369
|
+
|
|
370
|
+
```tsx
|
|
371
|
+
// src/providers/AppProvider.tsx
|
|
372
|
+
import { useEffect } from 'react';
|
|
373
|
+
import { useExGuardRealtime, getUserAccess } from '@/features/exguard';
|
|
374
|
+
import { useQueryClient } from '@tanstack/react-query';
|
|
375
|
+
|
|
376
|
+
export function AppProvider({ children }) {
|
|
377
|
+
const { registerRbacRefreshCallback } = useExGuardRealtime();
|
|
378
|
+
const queryClient = useQueryClient();
|
|
379
|
+
|
|
380
|
+
useEffect(() => {
|
|
381
|
+
// Register callback to refresh user data when RBAC changes
|
|
382
|
+
const unregister = registerRbacRefreshCallback(async () => {
|
|
383
|
+
console.log('RBAC data changed, refreshing...');
|
|
384
|
+
|
|
385
|
+
// Refetch user access data
|
|
386
|
+
const newAccess = await getUserAccess();
|
|
387
|
+
|
|
388
|
+
// Update React Query cache
|
|
389
|
+
queryClient.setQueryData(['userAccess'], newAccess);
|
|
390
|
+
|
|
391
|
+
// Invalidate related queries
|
|
392
|
+
queryClient.invalidateQueries({ queryKey: ['permissions'] });
|
|
393
|
+
queryClient.invalidateQueries({ queryKey: ['modules'] });
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
return unregister; // Cleanup on unmount
|
|
397
|
+
}, [registerRbacRefreshCallback, queryClient]);
|
|
398
|
+
|
|
399
|
+
return <>{children}</>;
|
|
400
|
+
}
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
### 3. Token Management
|
|
404
|
+
|
|
405
|
+
Access and manage authentication tokens:
|
|
406
|
+
|
|
407
|
+
```tsx
|
|
408
|
+
// src/utils/auth.ts
|
|
409
|
+
import { EXGUARD_STORAGE_KEYS } from '@/features/exguard';
|
|
410
|
+
|
|
411
|
+
export function getAccessToken(): string | null {
|
|
412
|
+
return localStorage.getItem(EXGUARD_STORAGE_KEYS.ACCESS_TOKEN);
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
export function getIdToken(): string | null {
|
|
416
|
+
return localStorage.getItem(EXGUARD_STORAGE_KEYS.ID_TOKEN);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
export function clearTokens(): void {
|
|
420
|
+
localStorage.removeItem(EXGUARD_STORAGE_KEYS.ACCESS_TOKEN);
|
|
421
|
+
localStorage.removeItem(EXGUARD_STORAGE_KEYS.ID_TOKEN);
|
|
422
|
+
localStorage.removeItem(EXGUARD_STORAGE_KEYS.REFRESH_TOKEN);
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
// Use in axios interceptor
|
|
426
|
+
import axios from 'axios';
|
|
427
|
+
|
|
428
|
+
axios.interceptors.request.use((config) => {
|
|
429
|
+
const token = getAccessToken();
|
|
430
|
+
if (token) {
|
|
431
|
+
config.headers.Authorization = `Bearer ${token}`;
|
|
432
|
+
}
|
|
433
|
+
return config;
|
|
434
|
+
});
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
## Best Practices
|
|
438
|
+
|
|
439
|
+
### 1. Import Pattern
|
|
440
|
+
|
|
441
|
+
**✅ DO:** Use the generated exguard module for imports
|
|
442
|
+
```tsx
|
|
443
|
+
import { PermissionGuard, useUserAccess } from '@/features/exguard';
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
**❌ DON'T:** Mix package and module imports
|
|
447
|
+
```tsx
|
|
448
|
+
// Avoid this - choose one pattern
|
|
449
|
+
import { PermissionGuard } from '@empowerx/exguard-client';
|
|
450
|
+
import { useUserAccess } from '@/features/exguard';
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
### 2. Permission Guard Placement
|
|
454
|
+
|
|
455
|
+
**✅ DO:** Place PermissionGuard at route level
|
|
456
|
+
```tsx
|
|
457
|
+
<Route
|
|
458
|
+
path="/profiles"
|
|
459
|
+
element={
|
|
460
|
+
<PermissionGuard module="EXID">
|
|
461
|
+
<ProfilesPage />
|
|
462
|
+
</PermissionGuard>
|
|
463
|
+
}
|
|
464
|
+
/>
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
**✅ DO:** Use for feature-level protection
|
|
468
|
+
```tsx
|
|
469
|
+
<PermissionGuard module="EXID" permission="profile:delete">
|
|
470
|
+
<DeleteButton />
|
|
471
|
+
</PermissionGuard>
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
### 3. Error Handling
|
|
475
|
+
|
|
476
|
+
Always handle permission errors gracefully:
|
|
477
|
+
|
|
478
|
+
```tsx
|
|
479
|
+
import { useUserAccess } from '@/features/exguard';
|
|
480
|
+
|
|
481
|
+
export function MyComponent() {
|
|
482
|
+
const { hasModuleAccess, error, isLoading } = useUserAccess();
|
|
483
|
+
|
|
484
|
+
if (isLoading) {
|
|
485
|
+
return <LoadingSpinner />;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
if (error) {
|
|
489
|
+
return <ErrorMessage message="Failed to load permissions" />;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
if (!hasModuleAccess('EXID')) {
|
|
493
|
+
return <AccessDenied />;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
return <ProtectedContent />;
|
|
497
|
+
}
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
### 4. Performance
|
|
501
|
+
|
|
502
|
+
Use `enabled` option to control when to fetch permissions:
|
|
503
|
+
|
|
504
|
+
```tsx
|
|
505
|
+
const { hasModuleAccess } = useUserAccess({
|
|
506
|
+
enabled: isAuthenticated, // Only fetch when authenticated
|
|
507
|
+
refetchInterval: 60000, // Refetch every minute
|
|
508
|
+
});
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
### 5. TypeScript Usage
|
|
512
|
+
|
|
513
|
+
Leverage full type safety:
|
|
514
|
+
|
|
515
|
+
```tsx
|
|
516
|
+
import type {
|
|
517
|
+
UserAccessData,
|
|
518
|
+
ModulePermissions,
|
|
519
|
+
RealtimeEventPayload
|
|
520
|
+
} from '@/features/exguard';
|
|
521
|
+
|
|
522
|
+
interface MyComponentProps {
|
|
523
|
+
userAccess: UserAccessData;
|
|
524
|
+
onPermissionChange: (payload: RealtimeEventPayload) => void;
|
|
525
|
+
}
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
## Troubleshooting
|
|
529
|
+
|
|
530
|
+
### Issue: "Cannot find module '@/features/exguard'"
|
|
531
|
+
|
|
532
|
+
**Solution:** Ensure you've run the setup script and your tsconfig paths are configured:
|
|
533
|
+
|
|
534
|
+
```json
|
|
535
|
+
// tsconfig.json
|
|
536
|
+
{
|
|
537
|
+
"compilerOptions": {
|
|
538
|
+
"baseUrl": ".",
|
|
539
|
+
"paths": {
|
|
540
|
+
"@/*": ["./src/*"]
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
```
|
|
545
|
+
|
|
546
|
+
### Issue: WebSocket not connecting
|
|
547
|
+
|
|
548
|
+
**Solution:** Check that:
|
|
549
|
+
1. ExGuard API URL is correct
|
|
550
|
+
2. User is authenticated (has valid tokens)
|
|
551
|
+
3. CORS is properly configured on the backend
|
|
552
|
+
4. ExGuardRealtimeProvider is wrapping your app
|
|
553
|
+
|
|
554
|
+
```tsx
|
|
555
|
+
// Debug connection
|
|
556
|
+
const { isConnected } = useExGuardRealtime();
|
|
557
|
+
console.log('WebSocket connected:', isConnected);
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
### Issue: Permissions not updating in real-time
|
|
561
|
+
|
|
562
|
+
**Solution:** Ensure you're listening to the correct events:
|
|
563
|
+
|
|
564
|
+
```tsx
|
|
565
|
+
import { EXGUARD_RBAC_EVENTS } from '@/features/exguard';
|
|
566
|
+
|
|
567
|
+
// Listen to general RBAC updates
|
|
568
|
+
useExGuardRealtimeSubscription(
|
|
569
|
+
EXGUARD_RBAC_EVENTS.RBAC_PERMISSIONS_UPDATED,
|
|
570
|
+
(payload) => {
|
|
571
|
+
// Refresh user access data
|
|
572
|
+
window.location.reload();
|
|
573
|
+
}
|
|
574
|
+
);
|
|
575
|
+
```
|
|
576
|
+
|
|
577
|
+
### Issue: TypeScript errors after installation
|
|
578
|
+
|
|
579
|
+
**Solution:** Rebuild the package and clear TypeScript cache:
|
|
580
|
+
|
|
581
|
+
```bash
|
|
582
|
+
cd packages/exguard-client
|
|
583
|
+
pnpm build
|
|
584
|
+
|
|
585
|
+
# In your app
|
|
586
|
+
rm -rf node_modules/.cache
|
|
587
|
+
pnpm install
|
|
588
|
+
```
|
|
589
|
+
|
|
590
|
+
### Issue: Module not found errors for old imports
|
|
591
|
+
|
|
592
|
+
**Solution:** Old realtime provider files need to be deleted. Remove:
|
|
593
|
+
- `src/app/providers/realtime-*.ts*`
|
|
594
|
+
- `src/shared/hooks/use-realtime.ts`
|
|
595
|
+
- `src/shared/lib/realtime*.ts`
|
|
596
|
+
|
|
597
|
+
Then update all imports to use `@/features/exguard`.
|
|
598
|
+
|
|
599
|
+
## Support
|
|
600
|
+
|
|
601
|
+
For additional help:
|
|
602
|
+
|
|
603
|
+
1. Check the [README.md](./README.md) for API reference
|
|
604
|
+
2. Review example implementations in the test applications
|
|
605
|
+
3. Contact the EmpowerX development team
|
|
606
|
+
|
|
607
|
+
## License
|
|
608
|
+
|
|
609
|
+
MIT
|