exguard-backend 1.0.23 → 1.0.24

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/dist/index.cjs CHANGED
@@ -38,6 +38,7 @@ __export(src_exports, {
38
38
  cache: () => cache,
39
39
  createExGuardExpress: () => createExGuardExpress,
40
40
  createExGuardFastify: () => createExGuardFastify,
41
+ createRealtime: () => createRealtime,
41
42
  realtime: () => realtime
42
43
  });
43
44
  module.exports = __toCommonJS(src_exports);
@@ -286,17 +287,57 @@ var ExGuardRealtime = class {
286
287
  handlers = /* @__PURE__ */ new Map();
287
288
  websocket = null;
288
289
  reconnectAttempts = 0;
289
- maxReconnectAttempts = 5;
290
- reconnectDelay = 1e3;
290
+ config;
291
+ currentUrl = null;
292
+ currentToken = null;
293
+ shouldReconnect = true;
294
+ constructor(config = {}) {
295
+ this.config = {
296
+ autoReconnect: config.autoReconnect ?? true,
297
+ maxReconnectAttempts: config.maxReconnectAttempts ?? 5,
298
+ reconnectDelay: config.reconnectDelay ?? 1e3,
299
+ reconnectMaxDelay: config.reconnectMaxDelay ?? 3e4,
300
+ onConnect: config.onConnect ?? (() => {
301
+ }),
302
+ onDisconnect: config.onDisconnect ?? (() => {
303
+ }),
304
+ onError: config.onError ?? ((error) => console.error("[ExGuardRealtime] Error:", error))
305
+ };
306
+ }
291
307
  /**
292
308
  * Connect to realtime server
293
- * @param url - WebSocket URL
294
- * @param token - Optional authentication token
309
+ * @param url - WebSocket URL (e.g., wss://api.example.com/realtime)
310
+ * @param auth - Authentication options (accessToken, apiKey, bearerToken) - optional
295
311
  */
296
- connect(url, token) {
312
+ connect(url, auth) {
313
+ let authToken;
314
+ let authType = null;
315
+ if (typeof auth === "string") {
316
+ authToken = auth;
317
+ authType = "access_token";
318
+ } else if (auth?.accessToken) {
319
+ authToken = auth.accessToken;
320
+ authType = "access_token";
321
+ } else if (auth?.apiKey) {
322
+ authToken = auth.apiKey;
323
+ authType = "api_key";
324
+ } else if (auth?.bearerToken) {
325
+ authToken = auth.bearerToken;
326
+ authType = "bearer";
327
+ }
297
328
  return new Promise((resolve, reject) => {
329
+ this.shouldReconnect = true;
330
+ this.currentUrl = url;
331
+ this.currentToken = authToken ?? null;
298
332
  try {
299
- const wsUrl = token ? `${url}?token=${token}` : url;
333
+ let wsUrl = url;
334
+ if (authToken && authType) {
335
+ if (authType === "bearer") {
336
+ wsUrl = `${url}?bearer=${encodeURIComponent(authToken)}`;
337
+ } else {
338
+ wsUrl = `${url}?${authType}=${encodeURIComponent(authToken)}`;
339
+ }
340
+ }
300
341
  this.websocket = new WebSocket(wsUrl);
301
342
  const timeout = setTimeout(() => {
302
343
  reject(new Error("Connection timeout"));
@@ -305,6 +346,7 @@ var ExGuardRealtime = class {
305
346
  clearTimeout(timeout);
306
347
  this.reconnectAttempts = 0;
307
348
  console.log("[ExGuardRealtime] Connected to realtime server");
349
+ this.config.onConnect();
308
350
  resolve();
309
351
  };
310
352
  this.websocket.onmessage = (event) => {
@@ -318,12 +360,15 @@ var ExGuardRealtime = class {
318
360
  this.websocket.onclose = () => {
319
361
  clearTimeout(timeout);
320
362
  console.log("[ExGuardRealtime] Disconnected from realtime server");
321
- this.handleReconnect(url, token);
363
+ this.config.onDisconnect();
364
+ this.handleReconnect();
322
365
  };
323
366
  this.websocket.onerror = (error) => {
324
367
  clearTimeout(timeout);
325
368
  console.error("[ExGuardRealtime] WebSocket error:", error);
326
- reject(error);
369
+ const err = new Error("WebSocket connection error");
370
+ this.config.onError(err);
371
+ reject(err);
327
372
  };
328
373
  } catch (error) {
329
374
  reject(error);
@@ -334,6 +379,7 @@ var ExGuardRealtime = class {
334
379
  * Disconnect from realtime server
335
380
  */
336
381
  disconnect() {
382
+ this.shouldReconnect = false;
337
383
  if (this.websocket) {
338
384
  this.websocket.close();
339
385
  this.websocket = null;
@@ -357,6 +403,16 @@ var ExGuardRealtime = class {
357
403
  }
358
404
  };
359
405
  }
406
+ /**
407
+ * Subscribe to all realtime events
408
+ */
409
+ subscribeAll(handler) {
410
+ const eventTypes = ["rbac_update", "user_update", "role_update", "permission_update"];
411
+ const unsubscribes = eventTypes.map((type) => this.subscribe(type, handler));
412
+ return () => {
413
+ unsubscribes.forEach((unsub) => unsub());
414
+ };
415
+ }
360
416
  /**
361
417
  * Handle incoming realtime events
362
418
  */
@@ -376,13 +432,19 @@ var ExGuardRealtime = class {
376
432
  /**
377
433
  * Handle reconnection logic
378
434
  */
379
- handleReconnect(url, token) {
380
- if (this.reconnectAttempts < this.maxReconnectAttempts) {
435
+ handleReconnect() {
436
+ if (!this.shouldReconnect || !this.config.autoReconnect || !this.currentUrl) {
437
+ return;
438
+ }
439
+ if (this.reconnectAttempts < this.config.maxReconnectAttempts) {
381
440
  this.reconnectAttempts++;
382
- const delay = this.reconnectDelay * Math.pow(2, this.reconnectAttempts - 1);
441
+ const delay = Math.min(
442
+ this.config.reconnectDelay * Math.pow(2, this.reconnectAttempts - 1),
443
+ this.config.reconnectMaxDelay
444
+ );
383
445
  console.log(`[ExGuardRealtime] Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts})`);
384
446
  setTimeout(() => {
385
- this.connect(url, token).catch((error) => {
447
+ this.connect(this.currentUrl, this.currentToken).catch((error) => {
386
448
  console.error("[ExGuardRealtime] Reconnection failed:", error);
387
449
  });
388
450
  }, delay);
@@ -390,13 +452,63 @@ var ExGuardRealtime = class {
390
452
  console.error("[ExGuardRealtime] Max reconnection attempts reached");
391
453
  }
392
454
  }
455
+ /**
456
+ * Manually trigger reconnection with current auth
457
+ */
458
+ reconnect() {
459
+ if (this.currentUrl && this.currentToken) {
460
+ this.reconnectAttempts = 0;
461
+ return this.connect(this.currentUrl, this.currentToken);
462
+ } else if (this.currentUrl) {
463
+ this.reconnectAttempts = 0;
464
+ return this.connect(this.currentUrl);
465
+ }
466
+ return Promise.reject(new Error("No URL configured"));
467
+ }
393
468
  /**
394
469
  * Check if connected
395
470
  */
396
471
  isConnected() {
397
472
  return this.websocket?.readyState === WebSocket.OPEN;
398
473
  }
474
+ /**
475
+ * Get connection status
476
+ */
477
+ getStatus() {
478
+ if (!this.websocket) return "disconnected";
479
+ switch (this.websocket.readyState) {
480
+ case WebSocket.CONNECTING:
481
+ return "connecting";
482
+ case WebSocket.OPEN:
483
+ return "connected";
484
+ case WebSocket.CLOSING:
485
+ case WebSocket.CLOSED:
486
+ return "disconnected";
487
+ default:
488
+ return "disconnected";
489
+ }
490
+ }
491
+ /**
492
+ * Send a message to the server
493
+ */
494
+ send(event) {
495
+ if (this.websocket && this.websocket.readyState === WebSocket.OPEN) {
496
+ this.websocket.send(JSON.stringify(event));
497
+ }
498
+ }
499
+ /**
500
+ * Update configuration
501
+ */
502
+ updateConfig(config) {
503
+ this.config = {
504
+ ...this.config,
505
+ ...config
506
+ };
507
+ }
399
508
  };
509
+ function createRealtime(config) {
510
+ return new ExGuardRealtime(config);
511
+ }
400
512
  var realtime = new ExGuardRealtime();
401
513
 
402
514
  // src/exguard-backend-enhanced.ts
@@ -406,21 +518,40 @@ var ExGuardBackendEnhanced = class {
406
518
  cache;
407
519
  realtime;
408
520
  userId = null;
521
+ _isRealtimeConnected = false;
409
522
  constructor(config) {
410
523
  this.config = {
411
524
  timeout: 1e4,
412
525
  cache: {
413
526
  enabled: true,
414
527
  ttl: 3e5
415
- // 5 minutes
416
528
  },
417
529
  realtime: {
418
- enabled: false
530
+ enabled: false,
531
+ autoReconnect: true,
532
+ maxReconnectAttempts: 5,
533
+ reconnectDelay: 1e3
419
534
  },
420
535
  ...config
421
536
  };
422
537
  this.cache = cache;
423
- this.realtime = realtime;
538
+ const realtimeConfig = {
539
+ autoReconnect: this.config.realtime?.autoReconnect ?? true,
540
+ maxReconnectAttempts: this.config.realtime?.maxReconnectAttempts ?? 5,
541
+ reconnectDelay: this.config.realtime?.reconnectDelay ?? 1e3,
542
+ onConnect: () => {
543
+ this._isRealtimeConnected = true;
544
+ this.config.realtime?.onConnect?.();
545
+ },
546
+ onDisconnect: () => {
547
+ this._isRealtimeConnected = false;
548
+ this.config.realtime?.onDisconnect?.();
549
+ },
550
+ onError: (error) => {
551
+ this.config.realtime?.onError?.(error);
552
+ }
553
+ };
554
+ this.realtime = createRealtime(realtimeConfig);
424
555
  this.client = import_axios2.default.create({
425
556
  baseURL: this.config.apiUrl,
426
557
  timeout: this.config.timeout,
@@ -435,14 +566,15 @@ var ExGuardBackendEnhanced = class {
435
566
  this.cache.cleanup();
436
567
  }, 6e4);
437
568
  }
438
- /**
439
- * Setup realtime connection and event handlers
440
- */
441
569
  async setupRealtime() {
442
570
  try {
443
- await this.realtime.connect(this.config.realtime.url, this.config.realtime.token);
571
+ await this.realtime.connect(
572
+ this.config.realtime.url,
573
+ this.config.realtime.token
574
+ );
444
575
  this.realtime.subscribe("rbac_update", (event) => {
445
576
  console.log("[ExGuardBackend] RBAC update received:", event);
577
+ this.config.realtime?.onRBACUpdate?.(event);
446
578
  if (event.userId) {
447
579
  this.cache.clearUserCache(event.userId);
448
580
  } else {
@@ -451,17 +583,72 @@ var ExGuardBackendEnhanced = class {
451
583
  });
452
584
  this.realtime.subscribe("user_update", (event) => {
453
585
  console.log("[ExGuardBackend] User update received:", event);
586
+ this.config.realtime?.onUserUpdate?.(event);
454
587
  if (event.userId) {
455
588
  this.cache.clearUserCache(event.userId);
456
589
  }
457
590
  });
591
+ this.realtime.subscribe("role_update", (event) => {
592
+ console.log("[ExGuardBackend] Role update received:", event);
593
+ if (event.userId) {
594
+ this.cache.clearUserCache(event.userId);
595
+ } else {
596
+ this.cache.clear();
597
+ }
598
+ });
599
+ this.realtime.subscribe("permission_update", (event) => {
600
+ console.log("[ExGuardBackend] Permission update received:", event);
601
+ if (event.userId) {
602
+ this.cache.clearUserCache(event.userId);
603
+ } else {
604
+ this.cache.clear();
605
+ }
606
+ });
458
607
  } catch (error) {
459
608
  console.error("[ExGuardBackend] Failed to setup realtime:", error);
460
609
  }
461
610
  }
462
611
  /**
463
- * Extract user ID from JWT token
612
+ * Manually connect to realtime server
464
613
  */
614
+ async connectRealtime(url, token) {
615
+ const wsUrl = url || this.config.realtime?.url;
616
+ const wsToken = token || this.config.realtime?.token;
617
+ if (!wsUrl) {
618
+ throw new Error("WebSocket URL is required");
619
+ }
620
+ await this.setupRealtime();
621
+ }
622
+ /**
623
+ * Disconnect from realtime server
624
+ */
625
+ disconnectRealtime() {
626
+ this.realtime.disconnect();
627
+ }
628
+ /**
629
+ * Check if realtime is connected
630
+ */
631
+ isRealtimeConnected() {
632
+ return this._isRealtimeConnected || this.realtime.isConnected();
633
+ }
634
+ /**
635
+ * Get realtime connection status
636
+ */
637
+ getRealtimeStatus() {
638
+ return this.realtime.getStatus();
639
+ }
640
+ /**
641
+ * Subscribe to realtime events
642
+ */
643
+ subscribeToRealtime(eventType, handler) {
644
+ return this.realtime.subscribe(eventType, handler);
645
+ }
646
+ /**
647
+ * Subscribe to all realtime events
648
+ */
649
+ subscribeToAllRealtime(handler) {
650
+ return this.realtime.subscribeAll(handler);
651
+ }
465
652
  extractUserIdFromToken(token) {
466
653
  try {
467
654
  const payload = JSON.parse(atob(token.split(".")[1]));
@@ -470,16 +657,10 @@ var ExGuardBackendEnhanced = class {
470
657
  return null;
471
658
  }
472
659
  }
473
- /**
474
- * Get cache key for user data
475
- */
476
660
  getCacheKey(token, suffix = "") {
477
661
  const userId = this.extractUserIdFromToken(token) || "unknown";
478
662
  return `user:${userId}:${suffix}`;
479
663
  }
480
- /**
481
- * Get user roles and permissions from cache or API
482
- */
483
664
  async getUserAccess(token) {
484
665
  if (!this.config.cache?.enabled) {
485
666
  return this.fetchUserAccess(token);
@@ -495,9 +676,6 @@ var ExGuardBackendEnhanced = class {
495
676
  this.cache.set(cacheKey, data, this.config.cache.ttl);
496
677
  return data;
497
678
  }
498
- /**
499
- * Fetch user access from API (always hits the server)
500
- */
501
679
  async fetchUserAccess(token) {
502
680
  try {
503
681
  const response = await this.client.get("/guard/me", {
@@ -519,9 +697,6 @@ var ExGuardBackendEnhanced = class {
519
697
  throw error;
520
698
  }
521
699
  }
522
- /**
523
- * Check if user has specific permission (cached)
524
- */
525
700
  async hasPermission(token, permission) {
526
701
  if (!this.config.cache?.enabled) {
527
702
  const userAccess2 = await this.getUserAccess(token);
@@ -543,9 +718,6 @@ var ExGuardBackendEnhanced = class {
543
718
  this.cache.set(cacheKey, hasPermission, this.config.cache.ttl / 2);
544
719
  return hasPermission;
545
720
  }
546
- /**
547
- * Check if user has specific role (cached)
548
- */
549
721
  async hasRole(token, role) {
550
722
  if (!this.config.cache?.enabled) {
551
723
  const userAccess2 = await this.getUserAccess(token);
@@ -563,9 +735,6 @@ var ExGuardBackendEnhanced = class {
563
735
  this.cache.set(cacheKey, hasRole, this.config.cache.ttl / 2);
564
736
  return hasRole;
565
737
  }
566
- /**
567
- * Get all permissions for a specific module (cached)
568
- */
569
738
  async getModulePermissions(token, moduleKey) {
570
739
  if (!this.config.cache?.enabled) {
571
740
  const userAccess2 = await this.getUserAccess(token);
@@ -585,9 +754,6 @@ var ExGuardBackendEnhanced = class {
585
754
  this.cache.set(cacheKey, permissions, this.config.cache.ttl);
586
755
  return permissions;
587
756
  }
588
- /**
589
- * Get user roles (cached)
590
- */
591
757
  async getUserRoles(token) {
592
758
  if (!this.config.cache?.enabled) {
593
759
  const userAccess2 = await this.getUserAccess(token);
@@ -605,9 +771,6 @@ var ExGuardBackendEnhanced = class {
605
771
  this.cache.set(cacheKey, roles, this.config.cache.ttl);
606
772
  return roles;
607
773
  }
608
- /**
609
- * Get user field offices (cached)
610
- */
611
774
  async getUserFieldOffices(token) {
612
775
  if (!this.config.cache?.enabled) {
613
776
  const userAccess2 = await this.getUserAccess(token);
@@ -625,9 +788,6 @@ var ExGuardBackendEnhanced = class {
625
788
  this.cache.set(cacheKey, fieldOffices, this.config.cache.ttl);
626
789
  return fieldOffices;
627
790
  }
628
- /**
629
- * Batch check multiple permissions (optimized)
630
- */
631
791
  async hasPermissions(token, permissions) {
632
792
  const results = {};
633
793
  const uncachedPermissions = [];
@@ -659,9 +819,6 @@ var ExGuardBackendEnhanced = class {
659
819
  }
660
820
  return results;
661
821
  }
662
- /**
663
- * Batch check multiple roles (optimized)
664
- */
665
822
  async hasRoles(token, roles) {
666
823
  const results = {};
667
824
  const uncachedRoles = [];
@@ -691,9 +848,6 @@ var ExGuardBackendEnhanced = class {
691
848
  }
692
849
  return results;
693
850
  }
694
- /**
695
- * Clear cache for a specific user
696
- */
697
851
  clearUserCache(token) {
698
852
  const userId = this.extractUserIdFromToken(token);
699
853
  if (userId) {
@@ -701,15 +855,13 @@ var ExGuardBackendEnhanced = class {
701
855
  console.log(`[ExGuardBackend] Cache cleared for user: ${userId}`);
702
856
  }
703
857
  }
704
- /**
705
- * Get cache statistics
706
- */
858
+ clearAllCache() {
859
+ this.cache.clear();
860
+ console.log("[ExGuardBackend] All cache cleared");
861
+ }
707
862
  getCacheStats() {
708
863
  return this.cache.getStats();
709
864
  }
710
- /**
711
- * Disconnect from realtime server
712
- */
713
865
  disconnect() {
714
866
  this.realtime.disconnect();
715
867
  }
@@ -1406,6 +1558,7 @@ function createGuardContext2(req) {
1406
1558
  cache,
1407
1559
  createExGuardExpress,
1408
1560
  createExGuardFastify,
1561
+ createRealtime,
1409
1562
  realtime
1410
1563
  });
1411
1564
  //# sourceMappingURL=index.cjs.map