@nsxbet/admin-sdk 0.1.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.
Files changed (159) hide show
  1. package/README.md +680 -0
  2. package/dist/auth/client/in-memory.d.ts +27 -0
  3. package/dist/auth/client/in-memory.d.ts.map +1 -0
  4. package/dist/auth/client/in-memory.js +242 -0
  5. package/dist/auth/client/index.d.ts +7 -0
  6. package/dist/auth/client/index.d.ts.map +1 -0
  7. package/dist/auth/client/index.js +7 -0
  8. package/dist/auth/client/interface.d.ts +115 -0
  9. package/dist/auth/client/interface.d.ts.map +1 -0
  10. package/dist/auth/client/interface.js +7 -0
  11. package/dist/auth/client/keycloak.d.ts +19 -0
  12. package/dist/auth/client/keycloak.d.ts.map +1 -0
  13. package/dist/auth/client/keycloak.js +126 -0
  14. package/dist/auth/components/UserSelector.d.ts +19 -0
  15. package/dist/auth/components/UserSelector.d.ts.map +1 -0
  16. package/dist/auth/components/UserSelector.js +100 -0
  17. package/dist/auth/components/index.d.ts +5 -0
  18. package/dist/auth/components/index.d.ts.map +1 -0
  19. package/dist/auth/components/index.js +4 -0
  20. package/dist/auth/index.d.ts +7 -0
  21. package/dist/auth/index.d.ts.map +1 -0
  22. package/dist/auth/index.js +7 -0
  23. package/dist/components/AuthProvider.d.ts +48 -0
  24. package/dist/components/AuthProvider.d.ts.map +1 -0
  25. package/dist/components/AuthProvider.js +117 -0
  26. package/dist/hooks/useAuth.d.ts +21 -0
  27. package/dist/hooks/useAuth.d.ts.map +1 -0
  28. package/dist/hooks/useAuth.js +34 -0
  29. package/dist/hooks/useFetch.d.ts +8 -0
  30. package/dist/hooks/useFetch.d.ts.map +1 -0
  31. package/dist/hooks/useFetch.js +31 -0
  32. package/dist/hooks/useI18n.d.ts +46 -0
  33. package/dist/hooks/useI18n.d.ts.map +1 -0
  34. package/dist/hooks/useI18n.js +95 -0
  35. package/dist/hooks/usePlatformAPI.d.ts +12 -0
  36. package/dist/hooks/usePlatformAPI.d.ts.map +1 -0
  37. package/dist/hooks/usePlatformAPI.js +10 -0
  38. package/dist/hooks/useTelemetry.d.ts +17 -0
  39. package/dist/hooks/useTelemetry.d.ts.map +1 -0
  40. package/dist/hooks/useTelemetry.js +36 -0
  41. package/dist/i18n/config.d.ts +26 -0
  42. package/dist/i18n/config.d.ts.map +1 -0
  43. package/dist/i18n/config.js +92 -0
  44. package/dist/i18n/index.d.ts +6 -0
  45. package/dist/i18n/index.d.ts.map +1 -0
  46. package/dist/i18n/index.js +4 -0
  47. package/dist/i18n/locales/en-US.json +144 -0
  48. package/dist/i18n/locales/es.json +144 -0
  49. package/dist/i18n/locales/pt-BR.json +144 -0
  50. package/dist/i18n/locales/ro.json +144 -0
  51. package/dist/index.d.ts +27 -0
  52. package/dist/index.d.ts.map +1 -0
  53. package/dist/index.js +30 -0
  54. package/dist/registry/AdminShellRegistry.d.ts +140 -0
  55. package/dist/registry/AdminShellRegistry.d.ts.map +1 -0
  56. package/dist/registry/AdminShellRegistry.js +237 -0
  57. package/dist/registry/client/http.d.ts +21 -0
  58. package/dist/registry/client/http.d.ts.map +1 -0
  59. package/dist/registry/client/http.js +107 -0
  60. package/dist/registry/client/in-memory.d.ts +36 -0
  61. package/dist/registry/client/in-memory.d.ts.map +1 -0
  62. package/dist/registry/client/in-memory.js +242 -0
  63. package/dist/registry/client/index.d.ts +7 -0
  64. package/dist/registry/client/index.d.ts.map +1 -0
  65. package/dist/registry/client/index.js +5 -0
  66. package/dist/registry/client/interface.d.ts +96 -0
  67. package/dist/registry/client/interface.d.ts.map +1 -0
  68. package/dist/registry/client/interface.js +7 -0
  69. package/dist/registry/index.d.ts +12 -0
  70. package/dist/registry/index.d.ts.map +1 -0
  71. package/dist/registry/index.js +8 -0
  72. package/dist/registry/types/index.d.ts +9 -0
  73. package/dist/registry/types/index.d.ts.map +1 -0
  74. package/dist/registry/types/index.js +6 -0
  75. package/dist/registry/types/manifest.d.ts +98 -0
  76. package/dist/registry/types/manifest.d.ts.map +1 -0
  77. package/dist/registry/types/manifest.js +81 -0
  78. package/dist/registry/types/module.d.ts +115 -0
  79. package/dist/registry/types/module.d.ts.map +1 -0
  80. package/dist/registry/types/module.js +6 -0
  81. package/dist/router/DynamicModule.d.ts +50 -0
  82. package/dist/router/DynamicModule.d.ts.map +1 -0
  83. package/dist/router/DynamicModule.js +141 -0
  84. package/dist/router/index.d.ts +2 -0
  85. package/dist/router/index.d.ts.map +1 -0
  86. package/dist/router/index.js +1 -0
  87. package/dist/shell/AdminShell.d.ts +38 -0
  88. package/dist/shell/AdminShell.d.ts.map +1 -0
  89. package/dist/shell/AdminShell.js +299 -0
  90. package/dist/shell/BackofficeShell.d.ts +38 -0
  91. package/dist/shell/BackofficeShell.d.ts.map +1 -0
  92. package/dist/shell/BackofficeShell.js +299 -0
  93. package/dist/shell/components/CommandPalette.d.ts +8 -0
  94. package/dist/shell/components/CommandPalette.d.ts.map +1 -0
  95. package/dist/shell/components/CommandPalette.js +197 -0
  96. package/dist/shell/components/HomePage.d.ts +2 -0
  97. package/dist/shell/components/HomePage.d.ts.map +1 -0
  98. package/dist/shell/components/HomePage.js +32 -0
  99. package/dist/shell/components/LeftNav.d.ts +7 -0
  100. package/dist/shell/components/LeftNav.d.ts.map +1 -0
  101. package/dist/shell/components/LeftNav.js +247 -0
  102. package/dist/shell/components/MainContent.d.ts +9 -0
  103. package/dist/shell/components/MainContent.d.ts.map +1 -0
  104. package/dist/shell/components/MainContent.js +88 -0
  105. package/dist/shell/components/ModuleOverview.d.ts +7 -0
  106. package/dist/shell/components/ModuleOverview.d.ts.map +1 -0
  107. package/dist/shell/components/ModuleOverview.js +40 -0
  108. package/dist/shell/components/ProfilePage.d.ts +2 -0
  109. package/dist/shell/components/ProfilePage.d.ts.map +1 -0
  110. package/dist/shell/components/ProfilePage.js +30 -0
  111. package/dist/shell/components/RegistryPage.d.ts +8 -0
  112. package/dist/shell/components/RegistryPage.d.ts.map +1 -0
  113. package/dist/shell/components/RegistryPage.js +129 -0
  114. package/dist/shell/components/SettingsPage.d.ts +2 -0
  115. package/dist/shell/components/SettingsPage.d.ts.map +1 -0
  116. package/dist/shell/components/SettingsPage.js +60 -0
  117. package/dist/shell/components/TopBar.d.ts +8 -0
  118. package/dist/shell/components/TopBar.d.ts.map +1 -0
  119. package/dist/shell/components/TopBar.js +61 -0
  120. package/dist/shell/components/index.d.ts +10 -0
  121. package/dist/shell/components/index.d.ts.map +1 -0
  122. package/dist/shell/components/index.js +7 -0
  123. package/dist/shell/components/theme-provider.d.ts +15 -0
  124. package/dist/shell/components/theme-provider.d.ts.map +1 -0
  125. package/dist/shell/components/theme-provider.js +39 -0
  126. package/dist/shell/index.d.ts +9 -0
  127. package/dist/shell/index.d.ts.map +1 -0
  128. package/dist/shell/index.js +8 -0
  129. package/dist/shell/search/fuzzy.d.ts +18 -0
  130. package/dist/shell/search/fuzzy.d.ts.map +1 -0
  131. package/dist/shell/search/fuzzy.js +121 -0
  132. package/dist/shell/search/index.d.ts +3 -0
  133. package/dist/shell/search/index.d.ts.map +1 -0
  134. package/dist/shell/search/index.js +1 -0
  135. package/dist/shell/telemetry.d.ts +7 -0
  136. package/dist/shell/telemetry.d.ts.map +1 -0
  137. package/dist/shell/telemetry.js +25 -0
  138. package/dist/shell/types.d.ts +110 -0
  139. package/dist/shell/types.d.ts.map +1 -0
  140. package/dist/shell/types.js +4 -0
  141. package/dist/tailwind/index.d.ts +20 -0
  142. package/dist/tailwind/index.d.ts.map +1 -0
  143. package/dist/tailwind/index.js +42 -0
  144. package/dist/types/keycloak.d.ts +26 -0
  145. package/dist/types/keycloak.d.ts.map +1 -0
  146. package/dist/types/keycloak.js +1 -0
  147. package/dist/types/platform.d.ts +83 -0
  148. package/dist/types/platform.d.ts.map +1 -0
  149. package/dist/types/platform.js +5 -0
  150. package/dist/vite/config.d.ts +71 -0
  151. package/dist/vite/config.d.ts.map +1 -0
  152. package/dist/vite/config.js +87 -0
  153. package/dist/vite/index.d.ts +18 -0
  154. package/dist/vite/index.d.ts.map +1 -0
  155. package/dist/vite/index.js +17 -0
  156. package/dist/vite/plugins.d.ts +44 -0
  157. package/dist/vite/plugins.d.ts.map +1 -0
  158. package/dist/vite/plugins.js +74 -0
  159. package/package.json +86 -0
@@ -0,0 +1,242 @@
1
+ /**
2
+ * In-Memory Auth Client
3
+ *
4
+ * Provides fake authentication for development and testing.
5
+ * Users can be selected from a predefined list or created custom.
6
+ */
7
+ /**
8
+ * Default mock users
9
+ */
10
+ const DEFAULT_MOCK_USERS = [
11
+ {
12
+ id: 'admin-user',
13
+ email: 'admin@example.com',
14
+ displayName: 'Admin User',
15
+ roles: [
16
+ 'admin',
17
+ // Tasks - full access
18
+ 'admin.tasks.view',
19
+ 'admin.tasks.edit',
20
+ 'admin.tasks.delete',
21
+ // Users - full access
22
+ 'admin.users.view',
23
+ 'admin.users.edit',
24
+ 'admin.users.delete',
25
+ ],
26
+ },
27
+ {
28
+ id: 'editor-user',
29
+ email: 'editor@example.com',
30
+ displayName: 'Editor User',
31
+ roles: [
32
+ // Tasks - can create/edit, no delete
33
+ 'admin.tasks.view',
34
+ 'admin.tasks.edit',
35
+ // Users - view only
36
+ 'admin.users.view',
37
+ ],
38
+ },
39
+ {
40
+ id: 'viewer-user',
41
+ email: 'viewer@example.com',
42
+ displayName: 'Viewer User',
43
+ roles: [
44
+ // View only - no edit or delete
45
+ 'admin.tasks.view',
46
+ 'admin.users.view',
47
+ ],
48
+ },
49
+ {
50
+ id: 'no-access-user',
51
+ email: 'noaccess@example.com',
52
+ displayName: 'No Access User',
53
+ roles: [],
54
+ },
55
+ ];
56
+ const DEFAULT_STORAGE_KEY = '@nsxbet/auth';
57
+ /**
58
+ * Create an in-memory auth client for development/testing
59
+ */
60
+ export function createInMemoryAuthClient(options = {}) {
61
+ const { users: customUsers = [], replaceDefaults = false, storageKey = DEFAULT_STORAGE_KEY, } = options;
62
+ // Merge users
63
+ const predefinedUsers = replaceDefaults ? customUsers : [...DEFAULT_MOCK_USERS, ...customUsers];
64
+ // State
65
+ let selectedUser = null;
66
+ const subscribers = new Set();
67
+ /**
68
+ * Load state from localStorage
69
+ */
70
+ function loadStorage() {
71
+ try {
72
+ const data = localStorage.getItem(storageKey);
73
+ if (data) {
74
+ return JSON.parse(data);
75
+ }
76
+ }
77
+ catch {
78
+ // Ignore parse errors
79
+ }
80
+ return { selectedUserId: null, customUsers: [] };
81
+ }
82
+ /**
83
+ * Save state to localStorage
84
+ */
85
+ function saveStorage(data) {
86
+ localStorage.setItem(storageKey, JSON.stringify(data));
87
+ }
88
+ /**
89
+ * Get all available users (predefined + custom)
90
+ */
91
+ function getAllUsers() {
92
+ const storage = loadStorage();
93
+ return [...predefinedUsers, ...storage.customUsers];
94
+ }
95
+ /**
96
+ * Find user by ID
97
+ */
98
+ function findUser(userId) {
99
+ return getAllUsers().find((u) => u.id === userId);
100
+ }
101
+ /**
102
+ * Notify subscribers of state change
103
+ */
104
+ function notifySubscribers() {
105
+ const state = {
106
+ isAuthenticated: selectedUser !== null,
107
+ user: selectedUser ? {
108
+ id: selectedUser.id,
109
+ email: selectedUser.email,
110
+ displayName: selectedUser.displayName,
111
+ } : null,
112
+ };
113
+ subscribers.forEach((callback) => callback(state));
114
+ }
115
+ /**
116
+ * Convert MockUser to User
117
+ */
118
+ function toUser(mockUser) {
119
+ return {
120
+ id: mockUser.id,
121
+ email: mockUser.email,
122
+ displayName: mockUser.displayName,
123
+ };
124
+ }
125
+ // Public API
126
+ const client = {
127
+ type: 'in-memory',
128
+ async initialize() {
129
+ const storage = loadStorage();
130
+ if (storage.selectedUserId) {
131
+ const user = findUser(storage.selectedUserId);
132
+ if (user) {
133
+ selectedUser = user;
134
+ return true;
135
+ }
136
+ }
137
+ return false;
138
+ },
139
+ isAuthenticated() {
140
+ return selectedUser !== null;
141
+ },
142
+ getUser() {
143
+ return selectedUser ? toUser(selectedUser) : null;
144
+ },
145
+ async getAccessToken() {
146
+ if (!selectedUser) {
147
+ throw new Error('Not authenticated');
148
+ }
149
+ return `mock-token-${selectedUser.id}-${Date.now()}`;
150
+ },
151
+ hasPermission(permission) {
152
+ if (!selectedUser) {
153
+ return false;
154
+ }
155
+ // '*' means all permissions
156
+ if (selectedUser.roles.includes('*')) {
157
+ return true;
158
+ }
159
+ // Check exact match or wildcard
160
+ return selectedUser.roles.some((role) => {
161
+ if (role === permission)
162
+ return true;
163
+ // Support wildcard like 'admin.*'
164
+ if (role.endsWith('.*')) {
165
+ const prefix = role.slice(0, -2);
166
+ return permission.startsWith(prefix);
167
+ }
168
+ return false;
169
+ });
170
+ },
171
+ logout() {
172
+ selectedUser = null;
173
+ const storage = loadStorage();
174
+ storage.selectedUserId = null;
175
+ saveStorage(storage);
176
+ notifySubscribers();
177
+ },
178
+ subscribe(callback) {
179
+ subscribers.add(callback);
180
+ return () => {
181
+ subscribers.delete(callback);
182
+ };
183
+ },
184
+ getAvailableUsers() {
185
+ return getAllUsers();
186
+ },
187
+ login(userId) {
188
+ const user = findUser(userId);
189
+ if (!user) {
190
+ throw new Error(`User not found: ${userId}`);
191
+ }
192
+ selectedUser = user;
193
+ const storage = loadStorage();
194
+ storage.selectedUserId = userId;
195
+ saveStorage(storage);
196
+ notifySubscribers();
197
+ },
198
+ createCustomUser(userData) {
199
+ const id = `custom-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
200
+ const newUser = { ...userData, id };
201
+ // Save to storage
202
+ const storage = loadStorage();
203
+ storage.customUsers.push(newUser);
204
+ saveStorage(storage);
205
+ // Login as the new user
206
+ client.login(id);
207
+ return newUser;
208
+ },
209
+ isCustomUser(userId) {
210
+ // Custom users have IDs starting with 'custom-'
211
+ return userId.startsWith('custom-');
212
+ },
213
+ deleteCustomUser(userId) {
214
+ // Cannot delete predefined users
215
+ if (!client.isCustomUser(userId)) {
216
+ return false;
217
+ }
218
+ const storage = loadStorage();
219
+ const index = storage.customUsers.findIndex((u) => u.id === userId);
220
+ if (index === -1) {
221
+ return false;
222
+ }
223
+ // Remove from storage
224
+ storage.customUsers.splice(index, 1);
225
+ // If deleted user was logged in, logout
226
+ if (storage.selectedUserId === userId) {
227
+ storage.selectedUserId = null;
228
+ selectedUser = null;
229
+ }
230
+ saveStorage(storage);
231
+ notifySubscribers();
232
+ return true;
233
+ },
234
+ };
235
+ return client;
236
+ }
237
+ /**
238
+ * Clear in-memory auth storage (useful for tests)
239
+ */
240
+ export function clearInMemoryAuth(storageKey = DEFAULT_STORAGE_KEY) {
241
+ localStorage.removeItem(storageKey);
242
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Auth Client exports
3
+ */
4
+ export type { AuthClient, InMemoryAuthClient, MockUser, AuthState, AuthStateCallback, } from './interface';
5
+ export { createInMemoryAuthClient, clearInMemoryAuth, type InMemoryAuthClientOptions, } from './in-memory';
6
+ export { createKeycloakAuthClient, type KeycloakAuthClientOptions, } from './keycloak';
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/auth/client/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,YAAY,EACV,UAAU,EACV,kBAAkB,EAClB,QAAQ,EACR,SAAS,EACT,iBAAiB,GAClB,MAAM,aAAa,CAAC;AAGrB,OAAO,EACL,wBAAwB,EACxB,iBAAiB,EACjB,KAAK,yBAAyB,GAC/B,MAAM,aAAa,CAAC;AAGrB,OAAO,EACL,wBAAwB,EACxB,KAAK,yBAAyB,GAC/B,MAAM,YAAY,CAAC"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Auth Client exports
3
+ */
4
+ // In-memory client
5
+ export { createInMemoryAuthClient, clearInMemoryAuth, } from './in-memory';
6
+ // Keycloak client
7
+ export { createKeycloakAuthClient, } from './keycloak';
@@ -0,0 +1,115 @@
1
+ /**
2
+ * Auth Client Interface
3
+ *
4
+ * Defines the interface for authentication operations.
5
+ * Can be implemented by in-memory (mock) or Keycloak clients.
6
+ */
7
+ import type { User } from '../../types/platform';
8
+ /**
9
+ * Mock user for in-memory auth client
10
+ */
11
+ export interface MockUser {
12
+ id: string;
13
+ email: string;
14
+ displayName: string;
15
+ roles: string[];
16
+ }
17
+ /**
18
+ * Auth client state
19
+ */
20
+ export interface AuthState {
21
+ isAuthenticated: boolean;
22
+ user: User | null;
23
+ }
24
+ /**
25
+ * Callback for auth state changes
26
+ */
27
+ export type AuthStateCallback = (state: AuthState) => void;
28
+ /**
29
+ * Auth Client Interface
30
+ *
31
+ * Usage:
32
+ * ```typescript
33
+ * // Create client
34
+ * const authClient = createInMemoryAuthClient();
35
+ *
36
+ * // Initialize (returns true if already authenticated)
37
+ * const isReady = await authClient.initialize();
38
+ *
39
+ * // Check auth state
40
+ * if (authClient.isAuthenticated()) {
41
+ * const user = authClient.getUser();
42
+ * }
43
+ *
44
+ * // Subscribe to changes
45
+ * const unsubscribe = authClient.subscribe((state) => {
46
+ * console.log('Auth state:', state);
47
+ * });
48
+ *
49
+ * // Logout
50
+ * authClient.logout();
51
+ * ```
52
+ */
53
+ export interface AuthClient {
54
+ /**
55
+ * Client type identifier
56
+ */
57
+ readonly type: 'in-memory' | 'keycloak';
58
+ /**
59
+ * Initialize the auth client
60
+ * @returns true if user is authenticated, false if login/selection is needed
61
+ */
62
+ initialize(): Promise<boolean>;
63
+ /**
64
+ * Check if user is currently authenticated
65
+ */
66
+ isAuthenticated(): boolean;
67
+ /**
68
+ * Get the current user (null if not authenticated)
69
+ */
70
+ getUser(): User | null;
71
+ /**
72
+ * Get access token for API calls
73
+ */
74
+ getAccessToken(): Promise<string>;
75
+ /**
76
+ * Check if user has a specific permission/role
77
+ */
78
+ hasPermission(permission: string): boolean;
79
+ /**
80
+ * Log out the current user
81
+ */
82
+ logout(): void;
83
+ /**
84
+ * Subscribe to auth state changes
85
+ * @returns Unsubscribe function
86
+ */
87
+ subscribe(callback: AuthStateCallback): () => void;
88
+ }
89
+ /**
90
+ * Extended interface for in-memory auth client
91
+ */
92
+ export interface InMemoryAuthClient extends AuthClient {
93
+ readonly type: 'in-memory';
94
+ /**
95
+ * Get all available mock users
96
+ */
97
+ getAvailableUsers(): MockUser[];
98
+ /**
99
+ * Check if a user is a custom user (can be deleted)
100
+ */
101
+ isCustomUser(userId: string): boolean;
102
+ /**
103
+ * Login as a specific user
104
+ */
105
+ login(userId: string): void;
106
+ /**
107
+ * Create and login as a custom user
108
+ */
109
+ createCustomUser(user: Omit<MockUser, 'id'>): MockUser;
110
+ /**
111
+ * Delete a custom user (predefined users cannot be deleted)
112
+ */
113
+ deleteCustomUser(userId: string): boolean;
114
+ }
115
+ //# sourceMappingURL=interface.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interface.d.ts","sourceRoot":"","sources":["../../../src/auth/client/interface.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAEjD;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,eAAe,EAAE,OAAO,CAAC;IACzB,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,CAAC;AAE3D;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,WAAW,UAAU;IACzB;;OAEG;IACH,QAAQ,CAAC,IAAI,EAAE,WAAW,GAAG,UAAU,CAAC;IAExC;;;OAGG;IACH,UAAU,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IAE/B;;OAEG;IACH,eAAe,IAAI,OAAO,CAAC;IAE3B;;OAEG;IACH,OAAO,IAAI,IAAI,GAAG,IAAI,CAAC;IAEvB;;OAEG;IACH,cAAc,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IAElC;;OAEG;IACH,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC;IAE3C;;OAEG;IACH,MAAM,IAAI,IAAI,CAAC;IAEf;;;OAGG;IACH,SAAS,CAAC,QAAQ,EAAE,iBAAiB,GAAG,MAAM,IAAI,CAAC;CACpD;AAED;;GAEG;AACH,MAAM,WAAW,kBAAmB,SAAQ,UAAU;IACpD,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAE3B;;OAEG;IACH,iBAAiB,IAAI,QAAQ,EAAE,CAAC;IAEhC;;OAEG;IACH,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC;IAEtC;;OAEG;IACH,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAE5B;;OAEG;IACH,gBAAgB,CAAC,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,GAAG,QAAQ,CAAC;IAEvD;;OAEG;IACH,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC;CAC3C"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Auth Client Interface
3
+ *
4
+ * Defines the interface for authentication operations.
5
+ * Can be implemented by in-memory (mock) or Keycloak clients.
6
+ */
7
+ export {};
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Keycloak Auth Client
3
+ *
4
+ * Provides real authentication via Keycloak for production environments.
5
+ */
6
+ import type { KeycloakConfig } from '../../types/keycloak';
7
+ import type { AuthClient } from './interface';
8
+ /**
9
+ * Options for creating a Keycloak auth client
10
+ */
11
+ export interface KeycloakAuthClientOptions {
12
+ /** Keycloak configuration */
13
+ config: KeycloakConfig;
14
+ }
15
+ /**
16
+ * Create a Keycloak auth client for production
17
+ */
18
+ export declare function createKeycloakAuthClient(options: KeycloakAuthClientOptions): AuthClient;
19
+ //# sourceMappingURL=keycloak.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keycloak.d.ts","sourceRoot":"","sources":["../../../src/auth/client/keycloak.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAAK,EAAE,cAAc,EAAuB,MAAM,sBAAsB,CAAC;AAChF,OAAO,KAAK,EAAE,UAAU,EAAgC,MAAM,aAAa,CAAC;AAE5E;;GAEG;AACH,MAAM,WAAW,yBAAyB;IACxC,6BAA6B;IAC7B,MAAM,EAAE,cAAc,CAAC;CACxB;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,yBAAyB,GAAG,UAAU,CAwIvF"}
@@ -0,0 +1,126 @@
1
+ /**
2
+ * Keycloak Auth Client
3
+ *
4
+ * Provides real authentication via Keycloak for production environments.
5
+ */
6
+ import Keycloak from 'keycloak-js';
7
+ /**
8
+ * Create a Keycloak auth client for production
9
+ */
10
+ export function createKeycloakAuthClient(options) {
11
+ const { config } = options;
12
+ let keycloak = null;
13
+ let currentUser = null;
14
+ let isInitialized = false;
15
+ const subscribers = new Set();
16
+ /**
17
+ * Parse user from Keycloak token
18
+ */
19
+ function parseUser(tokenParsed) {
20
+ return {
21
+ id: tokenParsed.sub || '',
22
+ email: tokenParsed.email || '',
23
+ displayName: tokenParsed.name || tokenParsed.preferred_username || '',
24
+ };
25
+ }
26
+ /**
27
+ * Notify subscribers of state change
28
+ */
29
+ function notifySubscribers() {
30
+ const state = {
31
+ isAuthenticated: currentUser !== null,
32
+ user: currentUser,
33
+ };
34
+ subscribers.forEach((callback) => callback(state));
35
+ }
36
+ const client = {
37
+ type: 'keycloak',
38
+ async initialize() {
39
+ if (keycloak) {
40
+ return keycloak.authenticated ?? false;
41
+ }
42
+ keycloak = new Keycloak({
43
+ url: config.url || 'http://localhost:8080',
44
+ realm: config.realm || 'admin',
45
+ clientId: config.clientId,
46
+ });
47
+ try {
48
+ const authenticated = await keycloak.init({
49
+ onLoad: 'login-required',
50
+ checkLoginIframe: false,
51
+ pkceMethod: 'S256',
52
+ });
53
+ if (authenticated && keycloak.tokenParsed) {
54
+ currentUser = parseUser(keycloak.tokenParsed);
55
+ }
56
+ isInitialized = true;
57
+ // Notify subscribers that auth is ready - this triggers React re-render
58
+ // which will re-evaluate permissions with the now-available token
59
+ notifySubscribers();
60
+ // Setup token refresh
61
+ keycloak.onTokenExpired = () => {
62
+ keycloak?.updateToken(30).catch(() => {
63
+ console.error('[AuthClient] Failed to refresh token');
64
+ client.logout();
65
+ });
66
+ };
67
+ // Setup auth state changes
68
+ keycloak.onAuthLogout = () => {
69
+ currentUser = null;
70
+ notifySubscribers();
71
+ };
72
+ keycloak.onAuthRefreshError = () => {
73
+ currentUser = null;
74
+ notifySubscribers();
75
+ };
76
+ return authenticated;
77
+ }
78
+ catch (error) {
79
+ console.error('[AuthClient] Keycloak initialization failed:', error);
80
+ throw error;
81
+ }
82
+ },
83
+ isAuthenticated() {
84
+ return keycloak?.authenticated ?? false;
85
+ },
86
+ getUser() {
87
+ return currentUser;
88
+ },
89
+ async getAccessToken() {
90
+ if (!keycloak) {
91
+ throw new Error('Keycloak not initialized');
92
+ }
93
+ try {
94
+ await keycloak.updateToken(5);
95
+ return keycloak.token || '';
96
+ }
97
+ catch (error) {
98
+ console.error('[AuthClient] Failed to refresh token');
99
+ client.logout();
100
+ throw error;
101
+ }
102
+ },
103
+ hasPermission(permission) {
104
+ // If not initialized yet, return true to avoid hiding modules during init
105
+ // The UI will re-render after authentication completes
106
+ if (!isInitialized || !keycloak) {
107
+ return true;
108
+ }
109
+ return keycloak.hasRealmRole(permission);
110
+ },
111
+ logout() {
112
+ currentUser = null;
113
+ if (keycloak) {
114
+ keycloak.logout();
115
+ }
116
+ notifySubscribers();
117
+ },
118
+ subscribe(callback) {
119
+ subscribers.add(callback);
120
+ return () => {
121
+ subscribers.delete(callback);
122
+ };
123
+ },
124
+ };
125
+ return client;
126
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * User Selector Component
3
+ *
4
+ * Displays available mock users for selection in development mode.
5
+ * Uses Brasa Design System tokens for consistent styling.
6
+ */
7
+ import type { InMemoryAuthClient } from '../client/interface';
8
+ interface UserSelectorProps {
9
+ /** The in-memory auth client */
10
+ authClient: InMemoryAuthClient;
11
+ /** Callback when user is selected */
12
+ onUserSelected?: () => void;
13
+ }
14
+ /**
15
+ * Main User Selector Component
16
+ */
17
+ export declare function UserSelector({ authClient, onUserSelected }: UserSelectorProps): import("react/jsx-runtime").JSX.Element;
18
+ export {};
19
+ //# sourceMappingURL=UserSelector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"UserSelector.d.ts","sourceRoot":"","sources":["../../../src/auth/components/UserSelector.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAAE,kBAAkB,EAAY,MAAM,qBAAqB,CAAC;AAExE,UAAU,iBAAiB;IACzB,gCAAgC;IAChC,UAAU,EAAE,kBAAkB,CAAC;IAC/B,qCAAqC;IACrC,cAAc,CAAC,EAAE,MAAM,IAAI,CAAC;CAC7B;AA2ND;;GAEG;AACH,wBAAgB,YAAY,CAAC,EAAE,UAAU,EAAE,cAAc,EAAE,EAAE,iBAAiB,2CAqF7E"}
@@ -0,0 +1,100 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * User Selector Component
4
+ *
5
+ * Displays available mock users for selection in development mode.
6
+ * Uses Brasa Design System tokens for consistent styling.
7
+ */
8
+ import { useState } from 'react';
9
+ import { Crown, Ban, Eye, User, Users, Sparkles, Wrench, Trash2, Plus, ChevronRight } from '@nsxbet/admin-ui';
10
+ /**
11
+ * Get user icon based on roles
12
+ */
13
+ function getUserIcon(roles) {
14
+ const iconClass = "h-7 w-7";
15
+ if (roles.includes('admin') || roles.includes('*')) {
16
+ return _jsx(Crown, { className: `${iconClass} text-amber-500` });
17
+ }
18
+ if (roles.length === 0) {
19
+ return _jsx(Ban, { className: `${iconClass} text-destructive` });
20
+ }
21
+ if (roles.some((r) => r.includes('view') && !r.includes('edit'))) {
22
+ return _jsx(Eye, { className: `${iconClass} text-info` });
23
+ }
24
+ return _jsx(User, { className: `${iconClass} text-muted-foreground` });
25
+ }
26
+ /**
27
+ * Get role badge color using Brasa Design System tokens
28
+ */
29
+ function getRoleBadgeColor(role) {
30
+ if (role === 'admin' || role === '*') {
31
+ // Admin uses accent-odd (gold) - prominent highlight
32
+ return 'bg-accent-odd/20 text-accent-odd border-accent-odd/30';
33
+ }
34
+ if (role.includes('edit')) {
35
+ // Edit permissions use success (green)
36
+ return 'bg-success/20 text-success border-success/30';
37
+ }
38
+ if (role.includes('view')) {
39
+ // View permissions use info (blue)
40
+ return 'bg-info/20 text-info border-info/30';
41
+ }
42
+ // Default uses muted
43
+ return 'bg-muted text-muted-foreground border-border';
44
+ }
45
+ /**
46
+ * User Card Component
47
+ */
48
+ function UserCard({ user, isCustom, onClick, onDelete, }) {
49
+ const icon = getUserIcon(user.roles);
50
+ const handleDelete = (e) => {
51
+ e.stopPropagation();
52
+ if (onDelete && confirm(`Delete user "${user.displayName}"?`)) {
53
+ onDelete();
54
+ }
55
+ };
56
+ return (_jsxs("div", { className: "relative group", children: [_jsx("button", { onClick: onClick, className: "w-full p-4 rounded-xl border border-border bg-card hover:bg-muted/50 hover:border-muted-foreground/30 transition-all duration-200 text-left", children: _jsxs("div", { className: "flex items-start gap-4", children: [_jsx("div", { className: "flex-shrink-0", children: icon }), _jsxs("div", { className: "flex-1 min-w-0", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("h3", { className: "font-semibold text-foreground group-hover:text-foreground transition-colors", children: user.displayName }), isCustom && (_jsx("span", { className: "text-xs px-1.5 py-0.5 rounded bg-primary/20 text-primary border border-primary/30", children: "custom" }))] }), _jsx("p", { className: "text-sm text-muted-foreground truncate", children: user.email }), _jsxs("div", { className: "flex flex-wrap gap-1.5 mt-2", children: [user.roles.length === 0 ? (_jsx("span", { className: "text-xs px-2 py-0.5 rounded-full border bg-destructive/20 text-destructive border-destructive/30", children: "no roles" })) : (user.roles.slice(0, 4).map((role) => (_jsx("span", { className: `text-xs px-2 py-0.5 rounded-full border ${getRoleBadgeColor(role)}`, children: role }, role)))), user.roles.length > 4 && (_jsxs("span", { className: "text-xs px-2 py-0.5 rounded-full border bg-muted text-muted-foreground border-border", children: ["+", user.roles.length - 4, " more"] }))] })] }), _jsx("div", { className: "text-muted-foreground group-hover:text-foreground transition-colors", children: _jsx(ChevronRight, { className: "h-5 w-5" }) })] }) }), isCustom && onDelete && (_jsx("button", { onClick: handleDelete, className: "absolute top-2 right-2 p-1.5 rounded-lg bg-destructive/10 text-destructive opacity-0 group-hover:opacity-100 hover:bg-destructive/20 transition-all", title: "Delete user", children: _jsx(Trash2, { className: "h-4 w-4" }) }))] }));
57
+ }
58
+ /**
59
+ * Custom User Form Component
60
+ */
61
+ function CustomUserForm({ onSubmit, onCancel, }) {
62
+ const [displayName, setDisplayName] = useState('');
63
+ const [email, setEmail] = useState('');
64
+ const [rolesInput, setRolesInput] = useState('');
65
+ const handleSubmit = (e) => {
66
+ e.preventDefault();
67
+ const roles = rolesInput
68
+ .split(',')
69
+ .map((r) => r.trim())
70
+ .filter(Boolean);
71
+ onSubmit({ displayName, email, roles });
72
+ };
73
+ const isValid = displayName.trim() && email.trim();
74
+ return (_jsxs("form", { onSubmit: handleSubmit, className: "p-4 rounded-xl border border-border bg-card", children: [_jsxs("h3", { className: "font-semibold text-foreground mb-4 flex items-center gap-2", children: [_jsx(Sparkles, { className: "h-5 w-5 text-amber-500" }), "Create Custom User"] }), _jsxs("div", { className: "space-y-3", children: [_jsxs("div", { children: [_jsx("label", { className: "block text-sm font-medium text-muted-foreground mb-1", children: "Display Name" }), _jsx("input", { type: "text", value: displayName, onChange: (e) => setDisplayName(e.target.value), placeholder: "John Doe", className: "w-full px-3 py-2 rounded-lg border border-border bg-background text-foreground placeholder-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:border-transparent" })] }), _jsxs("div", { children: [_jsx("label", { className: "block text-sm font-medium text-muted-foreground mb-1", children: "Email" }), _jsx("input", { type: "email", value: email, onChange: (e) => setEmail(e.target.value), placeholder: "john@example.com", className: "w-full px-3 py-2 rounded-lg border border-border bg-background text-foreground placeholder-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:border-transparent" })] }), _jsxs("div", { children: [_jsxs("label", { className: "block text-sm font-medium text-muted-foreground mb-1", children: ["Roles ", _jsx("span", { className: "text-muted-foreground/60", children: "(comma-separated)" })] }), _jsx("input", { type: "text", value: rolesInput, onChange: (e) => setRolesInput(e.target.value), placeholder: "admin, admin.users.view, admin.tasks.edit", className: "w-full px-3 py-2 rounded-lg border border-border bg-background text-foreground placeholder-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:border-transparent" }), _jsxs("p", { className: "text-xs text-muted-foreground mt-1", children: ["Use ", _jsx("code", { className: "px-1 py-0.5 rounded bg-muted", children: "*" }), " for all permissions"] })] })] }), _jsxs("div", { className: "flex gap-2 mt-4", children: [_jsx("button", { type: "button", onClick: onCancel, className: "flex-1 px-4 py-2 rounded-lg border border-border text-foreground hover:bg-muted/50 transition-colors", children: "Cancel" }), _jsx("button", { type: "submit", disabled: !isValid, className: "flex-1 px-4 py-2 rounded-lg bg-primary text-primary-foreground font-medium hover:bg-primary/90 disabled:opacity-50 disabled:cursor-not-allowed transition-colors", children: "Create & Login" })] })] }));
75
+ }
76
+ /**
77
+ * Main User Selector Component
78
+ */
79
+ export function UserSelector({ authClient, onUserSelected }) {
80
+ const [showCustomForm, setShowCustomForm] = useState(false);
81
+ const [, forceUpdate] = useState(0);
82
+ const users = authClient.getAvailableUsers();
83
+ const handleSelectUser = (userId) => {
84
+ authClient.login(userId);
85
+ onUserSelected?.();
86
+ };
87
+ const handleCreateCustomUser = (userData) => {
88
+ authClient.createCustomUser(userData);
89
+ onUserSelected?.();
90
+ };
91
+ const handleDeleteUser = (userId) => {
92
+ authClient.deleteCustomUser(userId);
93
+ // Force re-render to update the list
94
+ forceUpdate((n) => n + 1);
95
+ };
96
+ return (_jsx("div", { className: "min-h-screen bg-background flex items-center justify-center p-4", children: _jsxs("div", { className: "w-full max-w-md", children: [_jsxs("div", { className: "text-center mb-8", children: [_jsx("div", { className: "inline-flex items-center justify-center w-16 h-16 rounded-2xl bg-primary mb-4 shadow-lg shadow-primary/25", children: _jsx(Users, { className: "h-8 w-8 text-primary-foreground" }) }), _jsx("h1", { className: "text-2xl font-bold text-foreground mb-2", children: "Select User" }), _jsx("p", { className: "text-muted-foreground", children: "Choose a mock user to continue in development mode" })] }), _jsx("div", { className: "space-y-3 mb-6", children: users.map((user) => {
97
+ const isCustom = authClient.isCustomUser(user.id);
98
+ return (_jsx(UserCard, { user: user, isCustom: isCustom, onClick: () => handleSelectUser(user.id), onDelete: isCustom ? () => handleDeleteUser(user.id) : undefined }, user.id));
99
+ }) }), _jsxs("div", { className: "relative my-6", children: [_jsx("div", { className: "absolute inset-0 flex items-center", children: _jsx("div", { className: "w-full border-t border-border" }) }), _jsx("div", { className: "relative flex justify-center text-sm", children: _jsx("span", { className: "px-3 bg-background text-muted-foreground", children: "or" }) })] }), showCustomForm ? (_jsx(CustomUserForm, { onSubmit: handleCreateCustomUser, onCancel: () => setShowCustomForm(false) })) : (_jsxs("button", { onClick: () => setShowCustomForm(true), className: "w-full p-4 rounded-xl border border-dashed border-border text-muted-foreground hover:text-foreground hover:border-muted-foreground transition-colors flex items-center justify-center gap-2", children: [_jsx(Plus, { className: "h-5 w-5" }), "Create Custom User"] })), _jsxs("p", { className: "text-center text-xs text-muted-foreground mt-8 flex items-center justify-center gap-1", children: [_jsx(Wrench, { className: "h-3 w-3" }), "Development Mode \u2022 Data stored in localStorage"] })] }) }));
100
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Auth components exports
3
+ */
4
+ export { UserSelector } from './UserSelector';
5
+ //# sourceMappingURL=index.d.ts.map