@lego-box/shell 1.0.5 → 1.0.7

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 (49) hide show
  1. package/.krasrc +13 -0
  2. package/dist/emulator/lego-box-shell-1.0.7.tgz +0 -0
  3. package/package.json +6 -3
  4. package/postcss.config.js +6 -0
  5. package/src/auth/auth-store.ts +33 -0
  6. package/src/auth/auth.ts +176 -0
  7. package/src/components/ProtectedPage.tsx +48 -0
  8. package/src/config/env.node.ts +38 -0
  9. package/src/config/env.ts +105 -0
  10. package/src/context/AbilityContext.tsx +213 -0
  11. package/src/context/PiralInstanceContext.tsx +17 -0
  12. package/src/hooks/index.ts +11 -0
  13. package/src/hooks/useAuditLogs.ts +190 -0
  14. package/src/hooks/useDebounce.ts +34 -0
  15. package/src/hooks/usePermissionGuard.tsx +39 -0
  16. package/src/hooks/usePermissions.ts +190 -0
  17. package/src/hooks/useRoles.ts +233 -0
  18. package/src/hooks/useTickets.ts +214 -0
  19. package/src/hooks/useUserLogins.ts +39 -0
  20. package/src/hooks/useUsers.ts +252 -0
  21. package/src/index.html +16 -0
  22. package/src/index.tsx +296 -0
  23. package/src/layout.tsx +246 -0
  24. package/src/migrations/config.ts +62 -0
  25. package/src/migrations/dev-migrations.ts +75 -0
  26. package/src/migrations/index.ts +13 -0
  27. package/src/migrations/run-migrations.ts +187 -0
  28. package/src/migrations/runner.ts +925 -0
  29. package/src/migrations/types.ts +207 -0
  30. package/src/migrations/utils.ts +264 -0
  31. package/src/pages/AuditLogsPage.tsx +378 -0
  32. package/src/pages/ContactSupportPage.tsx +610 -0
  33. package/src/pages/LandingPage.tsx +221 -0
  34. package/src/pages/LoginPage.tsx +217 -0
  35. package/src/pages/MigrationsPage.tsx +1364 -0
  36. package/src/pages/ProfilePage.tsx +335 -0
  37. package/src/pages/SettingsPage.tsx +101 -0
  38. package/src/pages/SystemHealthCheckPage.tsx +144 -0
  39. package/src/pages/UserManagementPage.tsx +1010 -0
  40. package/src/piral/api.ts +39 -0
  41. package/src/piral/auth-casl.ts +56 -0
  42. package/src/piral/menu.ts +102 -0
  43. package/src/piral/piral.json +4 -0
  44. package/src/services/telemetry.ts +84 -0
  45. package/src/styles/globals.css +1351 -0
  46. package/src/utils/auditLogger.ts +68 -0
  47. package/tailwind.config.js +86 -0
  48. package/webpack.config.js +89 -0
  49. package/dist/emulator/lego-box-shell-1.0.5.tgz +0 -0
@@ -0,0 +1,207 @@
1
+ /**
2
+ * Migration System Types and Interfaces
3
+ *
4
+ * This file defines the core types for the RBAC migration system.
5
+ */
6
+
7
+ /**
8
+ * Represents a single database migration
9
+ */
10
+ export interface Migration {
11
+ id: string;
12
+ filename: string;
13
+ timestamp: number;
14
+ name: string;
15
+ status: 'pending' | 'applied' | 'failed' | 'rolled_back';
16
+ appliedAt: string | null;
17
+ errorMessage?: string;
18
+ checksum: string;
19
+ batch: number;
20
+ }
21
+
22
+ /**
23
+ * Migration history entry for audit trail
24
+ */
25
+ export interface MigrationHistory {
26
+ id: string;
27
+ migrationId: string;
28
+ action: 'apply' | 'rollback' | 'fail';
29
+ executedAt: string;
30
+ executedBy: string;
31
+ errorMessage?: string;
32
+ duration: number;
33
+ }
34
+
35
+ /**
36
+ * Field type for PocketBase collection schema
37
+ */
38
+ export type FieldType =
39
+ | 'text'
40
+ | 'number'
41
+ | 'bool'
42
+ | 'email'
43
+ | 'url'
44
+ | 'date'
45
+ | 'select'
46
+ | 'json'
47
+ | 'file'
48
+ | 'relation';
49
+
50
+ /**
51
+ * Field schema definition
52
+ */
53
+ export interface FieldSchema {
54
+ name: string;
55
+ type: FieldType;
56
+ required?: boolean;
57
+ unique?: boolean;
58
+ options?: {
59
+ min?: number;
60
+ max?: number;
61
+ pattern?: string;
62
+ values?: string[];
63
+ maxSelect?: number;
64
+ collectionId?: string;
65
+ cascadeDelete?: boolean;
66
+ [key: string]: any;
67
+ };
68
+ }
69
+
70
+ /**
71
+ * Collection schema definition
72
+ */
73
+ export interface CollectionSchema {
74
+ name: string;
75
+ type?: 'base' | 'auth' | 'view';
76
+ fields: FieldSchema[];
77
+ indexes?: string[];
78
+ listRule?: string | null;
79
+ viewRule?: string | null;
80
+ createRule?: string | null;
81
+ updateRule?: string | null;
82
+ deleteRule?: string | null;
83
+ options?: {
84
+ allowEmailAuth?: boolean;
85
+ allowOAuth2Auth?: boolean;
86
+ allowUsernameAuth?: boolean;
87
+ exceptEmailDomains?: string[] | null;
88
+ onlyEmailDomains?: string[] | null;
89
+ minPasswordLength?: number;
90
+ requireEmail?: boolean;
91
+ };
92
+ }
93
+
94
+ /**
95
+ * Permission action types
96
+ */
97
+ export type PermissionAction = 'create' | 'read' | 'update' | 'delete';
98
+
99
+ /**
100
+ * Permission definition
101
+ */
102
+ export interface Permission {
103
+ id?: string;
104
+ name: string;
105
+ action: PermissionAction;
106
+ collection: string;
107
+ description?: string;
108
+ resourceType: 'collection' | 'field' | 'custom';
109
+ resourceName?: string;
110
+ }
111
+
112
+ /**
113
+ * Role definition
114
+ */
115
+ export interface Role {
116
+ id?: string;
117
+ name: string;
118
+ description?: string;
119
+ permissions: string[];
120
+ organization: string;
121
+ isDefault?: boolean;
122
+ }
123
+
124
+ /**
125
+ * Organization definition
126
+ */
127
+ export interface Organization {
128
+ id?: string;
129
+ name: string;
130
+ slug: string;
131
+ description?: string;
132
+ settings?: Record<string, any>;
133
+ isActive?: boolean;
134
+ }
135
+
136
+ /**
137
+ * Extended User with RBAC fields
138
+ */
139
+ export interface RBACUser {
140
+ id?: string;
141
+ email: string;
142
+ name: string;
143
+ avatar?: string;
144
+ organization: string;
145
+ role: string;
146
+ is_superuser?: boolean;
147
+ isActive?: boolean;
148
+ permissions?: string[];
149
+ }
150
+
151
+ /**
152
+ * Migration context passed to each migration
153
+ */
154
+ export interface MigrationContext {
155
+ pb: any; // PocketBase instance
156
+ log: (message: string, type?: 'info' | 'success' | 'error' | 'warn') => void;
157
+ createCollection: (schema: CollectionSchema) => Promise<void>;
158
+ updateCollection: (name: string, schema: Partial<CollectionSchema>) => Promise<void>;
159
+ deleteCollection: (name: string) => Promise<void>;
160
+ collectionExists: (name: string) => Promise<boolean>;
161
+ insert: (collection: string, data: any) => Promise<any>;
162
+ update: (collection: string, id: string, data: any) => Promise<any>;
163
+ delete: (collection: string, id: string) => Promise<void>;
164
+ }
165
+
166
+ /**
167
+ * Migration function signature
168
+ */
169
+ export type MigrationFunction = (context: MigrationContext) => Promise<void>;
170
+
171
+ /**
172
+ * Migration module interface
173
+ */
174
+ export interface MigrationModule {
175
+ up: MigrationFunction;
176
+ down?: MigrationFunction;
177
+ id: string;
178
+ name: string;
179
+ }
180
+
181
+ /**
182
+ * Options for migration runner
183
+ */
184
+ export interface MigrationRunnerOptions {
185
+ pocketbaseUrl: string;
186
+ adminEmail?: string;
187
+ adminPassword?: string;
188
+ migrationsPath: string;
189
+ autoMigrate?: boolean;
190
+ onProgress?: (message: string, type?: 'info' | 'success' | 'error' | 'warn') => void;
191
+ }
192
+
193
+ /**
194
+ * API Rule generator function type
195
+ */
196
+ export type APIRuleGenerator = (collectionName: string, action: PermissionAction) => string;
197
+
198
+ /**
199
+ * Migration log entry for console logs
200
+ */
201
+ export interface MigrationLog {
202
+ id?: string;
203
+ message: string;
204
+ type: 'command' | 'info' | 'success' | 'error';
205
+ timestamp: string;
206
+ sessionId?: string;
207
+ }
@@ -0,0 +1,264 @@
1
+ /**
2
+ * RBAC Utilities
3
+ *
4
+ * Helper functions for managing permissions, roles, and API rules dynamically.
5
+ */
6
+
7
+ import type { PermissionAction, Permission, Role, Organization } from './types';
8
+
9
+ /**
10
+ * Generate a permission name from action and collection
11
+ */
12
+ export function generatePermissionName(action: PermissionAction, collection: string): string {
13
+ return `${action}:${collection}`;
14
+ }
15
+
16
+ /**
17
+ * Parse a permission name into action and collection
18
+ */
19
+ export function parsePermissionName(name: string): { action: PermissionAction; collection: string } | null {
20
+ const parts = name.split(':');
21
+ if (parts.length !== 2) return null;
22
+
23
+ const action = parts[0] as PermissionAction;
24
+ if (!['create', 'read', 'update', 'delete'].includes(action)) return null;
25
+
26
+ return { action, collection: parts[1] };
27
+ }
28
+
29
+ /**
30
+ * Generate all CRUD permissions for a collection
31
+ */
32
+ export function generateCollectionPermissions(collection: string): string[] {
33
+ const actions: PermissionAction[] = ['create', 'read', 'update', 'delete'];
34
+ return actions.map(action => generatePermissionName(action, collection));
35
+ }
36
+
37
+ /**
38
+ * Generate API rule for a collection and action
39
+ */
40
+ export function generateAPIRule(collection: string, action: PermissionAction): string {
41
+ const actionMap: Record<PermissionAction, string> = {
42
+ create: 'create',
43
+ read: 'read',
44
+ update: 'update',
45
+ delete: 'delete',
46
+ };
47
+
48
+ // Superusers can do everything
49
+ // Users with the specific permission in their role can access if in same organization
50
+ return `(@request.auth.is_superuser = true) || (@request.auth.role.permissions ?~ "${collection}:${actionMap[action]}" && @request.auth.organization = organization)`;
51
+ }
52
+
53
+ /**
54
+ * Generate all API rules for a collection
55
+ */
56
+ export function generateAllAPIRules(collection: string): {
57
+ listRule: string;
58
+ viewRule: string;
59
+ createRule: string;
60
+ updateRule: string;
61
+ deleteRule: string;
62
+ } {
63
+ return {
64
+ listRule: generateAPIRule(collection, 'read'),
65
+ viewRule: generateAPIRule(collection, 'read'),
66
+ createRule: generateAPIRule(collection, 'create'),
67
+ updateRule: generateAPIRule(collection, 'update'),
68
+ deleteRule: generateAPIRule(collection, 'delete'),
69
+ };
70
+ }
71
+
72
+ /**
73
+ * Check if user has a specific permission
74
+ */
75
+ export function hasPermission(
76
+ userPermissions: string[],
77
+ rolePermissions: string[],
78
+ requiredPermission: string
79
+ ): boolean {
80
+ // Direct user permissions take precedence
81
+ if (userPermissions.includes(requiredPermission)) return true;
82
+
83
+ // Check role permissions
84
+ if (rolePermissions.includes(requiredPermission)) return true;
85
+
86
+ // Check wildcard permissions (e.g., "read:*" grants read access to all)
87
+ const [action] = requiredPermission.split(':');
88
+ if (userPermissions.includes(`${action}:*`)) return true;
89
+ if (rolePermissions.includes(`${action}:*`)) return true;
90
+
91
+ return false;
92
+ }
93
+
94
+ /**
95
+ * Check if user has permission for a specific action on a collection
96
+ */
97
+ export function hasCollectionPermission(
98
+ userPermissions: string[],
99
+ rolePermissions: string[],
100
+ collection: string,
101
+ action: PermissionAction
102
+ ): boolean {
103
+ const permissionName = generatePermissionName(action, collection);
104
+ return hasPermission(userPermissions, rolePermissions, permissionName);
105
+ }
106
+
107
+ /**
108
+ * Get all permissions for a user combining role and direct permissions
109
+ */
110
+ export function getAllPermissions(
111
+ userPermissions: string[] = [],
112
+ rolePermissions: string[] = []
113
+ ): string[] {
114
+ return Array.from(new Set([...rolePermissions, ...userPermissions]));
115
+ }
116
+
117
+ /**
118
+ * Role permission builder
119
+ * Fluent API for building role permissions
120
+ */
121
+ export class RolePermissionBuilder {
122
+ private permissions: string[] = [];
123
+
124
+ /**
125
+ * Grant permission for an action on a collection
126
+ */
127
+ can(action: PermissionAction, collection: string): this {
128
+ this.permissions.push(generatePermissionName(action, collection));
129
+ return this;
130
+ }
131
+
132
+ /**
133
+ * Grant all CRUD permissions for a collection
134
+ */
135
+ canManage(collection: string): this {
136
+ this.permissions.push(...generateCollectionPermissions(collection));
137
+ return this;
138
+ }
139
+
140
+ /**
141
+ * Grant read permission for a collection
142
+ */
143
+ canRead(collection: string): this {
144
+ this.permissions.push(generatePermissionName('read', collection));
145
+ return this;
146
+ }
147
+
148
+ /**
149
+ * Grant create permission for a collection
150
+ */
151
+ canCreate(collection: string): this {
152
+ this.permissions.push(generatePermissionName('create', collection));
153
+ return this;
154
+ }
155
+
156
+ /**
157
+ * Grant update permission for a collection
158
+ */
159
+ canUpdate(collection: string): this {
160
+ this.permissions.push(generatePermissionName('update', collection));
161
+ return this;
162
+ }
163
+
164
+ /**
165
+ * Grant delete permission for a collection
166
+ */
167
+ canDelete(collection: string): this {
168
+ this.permissions.push(generatePermissionName('delete', collection));
169
+ return this;
170
+ }
171
+
172
+ /**
173
+ * Build and return the permissions array
174
+ */
175
+ build(): string[] {
176
+ return Array.from(new Set(this.permissions));
177
+ }
178
+ }
179
+
180
+ /**
181
+ * Create a new permission builder
182
+ */
183
+ export function definePermissions(): RolePermissionBuilder {
184
+ return new RolePermissionBuilder();
185
+ }
186
+
187
+ /**
188
+ * Default role definitions
189
+ */
190
+ export const DEFAULT_ROLES = {
191
+ superuser: (orgId: string) => ({
192
+ name: 'Superuser',
193
+ description: 'Full system access with all permissions',
194
+ organization: orgId,
195
+ isDefault: false,
196
+ }),
197
+
198
+ admin: (orgId: string) => ({
199
+ name: 'Admin',
200
+ description: 'Organization administrator',
201
+ organization: orgId,
202
+ isDefault: false,
203
+ }),
204
+
205
+ user: (orgId: string) => ({
206
+ name: 'User',
207
+ description: 'Standard user',
208
+ organization: orgId,
209
+ isDefault: true,
210
+ }),
211
+ };
212
+
213
+ /**
214
+ * Default permissions for standard user role
215
+ */
216
+ export function getDefaultUserPermissions(): string[] {
217
+ return [
218
+ 'read:rbac_users',
219
+ 'update:rbac_users',
220
+ 'read:organizations',
221
+ 'read:roles',
222
+ 'read:permissions',
223
+ ];
224
+ }
225
+
226
+ /**
227
+ * Validate permission name format
228
+ */
229
+ export function isValidPermissionName(name: string): boolean {
230
+ return parsePermissionName(name) !== null;
231
+ }
232
+
233
+ /**
234
+ * Extract collection names from permissions
235
+ */
236
+ export function extractCollections(permissions: string[]): string[] {
237
+ const collections = new Set<string>();
238
+ for (const perm of permissions) {
239
+ const parsed = parsePermissionName(perm);
240
+ if (parsed) {
241
+ collections.add(parsed.collection);
242
+ }
243
+ }
244
+ return Array.from(collections);
245
+ }
246
+
247
+ /**
248
+ * Group permissions by collection
249
+ */
250
+ export function groupPermissionsByCollection(permissions: string[]): Record<string, PermissionAction[]> {
251
+ const grouped: Record<string, PermissionAction[]> = {};
252
+
253
+ for (const perm of permissions) {
254
+ const parsed = parsePermissionName(perm);
255
+ if (parsed) {
256
+ if (!grouped[parsed.collection]) {
257
+ grouped[parsed.collection] = [];
258
+ }
259
+ grouped[parsed.collection].push(parsed.action);
260
+ }
261
+ }
262
+
263
+ return grouped;
264
+ }