@experteam-mx/ngx-services 20.8.6 → 20.8.9

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.
@@ -5582,16 +5582,80 @@ var DocumentStatusCode;
5582
5582
  DocumentStatusCode[DocumentStatusCode["WITH_ERRORS"] = 3] = "WITH_ERRORS";
5583
5583
  })(DocumentStatusCode || (DocumentStatusCode = {}));
5584
5584
 
5585
+ const SOCKETS_AUTH_PATH = '/sockets/auth';
5585
5586
  class WebSocketsService {
5586
- pusher;
5587
+ pusher = null;
5588
+ appKey = null;
5589
+ connectPromise = null;
5587
5590
  environments = inject(ENVIRONMENT_TOKEN);
5588
- constructor() {
5589
- this.pusher = null;
5590
- this.connect()
5591
- .then((pusher) => {
5592
- if (!pusher)
5593
- return;
5591
+ getAuthToken = null;
5592
+ channelAuthHeaders = {};
5593
+ /**
5594
+ * Stores the Pusher/Soketi application key without opening a connection.
5595
+ * The first `channelSub` / `eventBind` opens Pusher via `ensureConnected`.
5596
+ *
5597
+ * @param {string} appKey - The Pusher/Soketi application key.
5598
+ */
5599
+ setAppKey(appKey) {
5600
+ this.appKey = appKey;
5601
+ }
5602
+ /**
5603
+ * Registers a live token provider used when authorizing private/presence channels.
5604
+ * The provider is called at subscribe/authorize time; the token is not snapshotted here.
5605
+ *
5606
+ * @param {() => string | null} getToken - Returns the current session token, or null.
5607
+ */
5608
+ setAuthTokenProvider(getToken) {
5609
+ this.getAuthToken = getToken;
5610
+ }
5611
+ /**
5612
+ * Connects to Pusher using the given app key and env `sockets` url/port/debug.
5613
+ * No-ops if already connected with the same key; reconnects if the key changed.
5614
+ * Resolves false when `sockets` is missing from the environment.
5615
+ * Apps should store the key with `setAppKey` and let subscribe open the client.
5616
+ *
5617
+ * @param {string} appKey - The Pusher/Soketi application key.
5618
+ * @return {Promise<Pusher | false>} The Pusher instance, or false when sockets are not configured.
5619
+ */
5620
+ connect(appKey) {
5621
+ this.setAppKey(appKey);
5622
+ return new Promise((resolve) => {
5623
+ if (this.pusher && this.appKey === appKey) {
5624
+ return resolve(this.pusher);
5625
+ }
5626
+ const { sockets } = this.environments;
5627
+ if (!sockets)
5628
+ return resolve(false);
5629
+ if (this.pusher) {
5630
+ this.pusher.disconnect();
5631
+ this.pusher = null;
5632
+ }
5633
+ const options = {
5634
+ wsHost: sockets.url,
5635
+ wsPort: sockets.port,
5636
+ enabledTransports: ['wss', 'ws'],
5637
+ forceTLS: false,
5638
+ enableStats: false,
5639
+ };
5640
+ const { apiSecurityUrl } = this.environments;
5641
+ if (apiSecurityUrl) {
5642
+ this.refreshChannelAuthHeader();
5643
+ options.channelAuthorization = {
5644
+ transport: 'ajax',
5645
+ endpoint: `${apiSecurityUrl}${SOCKETS_AUTH_PATH}`,
5646
+ headers: this.channelAuthHeaders,
5647
+ headersProvider: () => {
5648
+ this.refreshChannelAuthHeader();
5649
+ return this.channelAuthHeaders;
5650
+ },
5651
+ };
5652
+ }
5653
+ const pusher = new Pusher(appKey, options);
5654
+ if (sockets.debug) {
5655
+ Pusher.logToConsole = true;
5656
+ }
5594
5657
  this.pusher = pusher;
5658
+ return resolve(pusher);
5595
5659
  });
5596
5660
  }
5597
5661
  /**
@@ -5614,13 +5678,16 @@ class WebSocketsService {
5614
5678
  * @throws {Error} If the subscription fails after the maximum number of retry attempts.
5615
5679
  */
5616
5680
  async channelSub(channelName) {
5681
+ const pusher = await this.ensureConnected();
5682
+ this.refreshChannelAuthHeader();
5617
5683
  const attempts = 3;
5618
5684
  for (let attempt = 0; attempt <= attempts; attempt++) {
5619
- const channel = this.pusher?.channel(channelName);
5685
+ const channel = pusher.channel(channelName);
5620
5686
  if (channel?.subscribed) {
5621
5687
  return channel;
5622
5688
  }
5623
- this.pusher?.subscribe(channelName);
5689
+ this.refreshChannelAuthHeader();
5690
+ pusher.subscribe(channelName);
5624
5691
  if (attempt < attempts) {
5625
5692
  await this.wait();
5626
5693
  }
@@ -5637,13 +5704,22 @@ class WebSocketsService {
5637
5704
  */
5638
5705
  eventBind(channel, event) {
5639
5706
  return new Observable((subscriber) => {
5640
- if (channel?.name) {
5641
- channel.bind(event, (data) => subscriber.next(data));
5642
- }
5643
- else {
5644
- this.pusher?.subscribe(channel)
5645
- .bind(event, (data) => subscriber.next(data));
5646
- }
5707
+ void (async () => {
5708
+ try {
5709
+ const pusher = await this.ensureConnected();
5710
+ this.refreshChannelAuthHeader();
5711
+ if (channel?.name) {
5712
+ channel.bind(event, (data) => subscriber.next(data));
5713
+ }
5714
+ else {
5715
+ pusher.subscribe(channel)
5716
+ .bind(event, (data) => subscriber.next(data));
5717
+ }
5718
+ }
5719
+ catch (error) {
5720
+ subscriber.error(error);
5721
+ }
5722
+ })();
5647
5723
  });
5648
5724
  }
5649
5725
  /**
@@ -5657,23 +5733,33 @@ class WebSocketsService {
5657
5733
  eventUnbind(channel, event) {
5658
5734
  return new Observable((subscriber) => channel.unbind(event, (data) => subscriber.next(data)));
5659
5735
  }
5660
- connect() {
5661
- return new Promise((resolve) => {
5662
- const { sockets } = this.environments;
5663
- if (!sockets)
5664
- return resolve(false);
5665
- const pusher = new Pusher(sockets.app_key, {
5666
- wsHost: sockets.url,
5667
- wsPort: sockets.port,
5668
- enabledTransports: ['wss', 'ws'],
5669
- forceTLS: false,
5670
- enableStats: false,
5671
- });
5672
- if (sockets.debug) {
5673
- Pusher.logToConsole = true;
5736
+ async ensureConnected() {
5737
+ if (this.pusher)
5738
+ return this.pusher;
5739
+ if (!this.appKey) {
5740
+ throw new Error('The sockets connection is not established.');
5741
+ }
5742
+ this.connectPromise ??= this.connect(this.appKey);
5743
+ try {
5744
+ const pusher = await this.connectPromise;
5745
+ if (!pusher) {
5746
+ throw new Error('The sockets connection is not established.');
5674
5747
  }
5675
- return resolve(pusher);
5676
- });
5748
+ return pusher;
5749
+ }
5750
+ catch (error) {
5751
+ this.connectPromise = null;
5752
+ throw error;
5753
+ }
5754
+ }
5755
+ refreshChannelAuthHeader() {
5756
+ const token = this.getAuthToken?.() ?? null;
5757
+ if (token) {
5758
+ this.channelAuthHeaders['Authorization'] = `Bearer ${token}`;
5759
+ }
5760
+ else {
5761
+ delete this.channelAuthHeaders['Authorization'];
5762
+ }
5677
5763
  }
5678
5764
  async wait() {
5679
5765
  const waitTime = 3 * 1000;
@@ -5687,13 +5773,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImpo
5687
5773
  args: [{
5688
5774
  providedIn: 'root',
5689
5775
  }]
5690
- }], ctorParameters: () => [] });
5776
+ }] });
5691
5777
 
5692
5778
  const AES_KEY_USAGES = ['encrypt', 'decrypt'];
5693
5779
  class CryptoService {
5694
- environments = inject(ENVIRONMENT_TOKEN);
5695
- async encryptAES(plaintext) {
5696
- const cryptoKey = await this.loadKey();
5780
+ async encryptAES(plaintext, secretKey) {
5781
+ const cryptoKey = await this.loadKey(secretKey);
5697
5782
  // Convert the plaintext in Uint8Array
5698
5783
  const encoder = new TextEncoder();
5699
5784
  const encoded = encoder.encode(plaintext);
@@ -5706,13 +5791,13 @@ class CryptoService {
5706
5791
  }, cryptoKey, encoded);
5707
5792
  return `${this.uint8ArrayToBase64(iv)}:${this.arrayBufferToBase64(ciphertext)}`;
5708
5793
  }
5709
- async decryptAES(payload) {
5794
+ async decryptAES(payload, secretKey) {
5710
5795
  // Extract the Initialization Vector (IV), and the cypher text
5711
5796
  const [base64IV, base64Ciphertext] = payload.split(':');
5712
5797
  if (!base64IV || !base64Ciphertext) {
5713
5798
  throw new Error('The payload must contain the IV and the cipher text.');
5714
5799
  }
5715
- const cryptoKey = await this.loadKey();
5800
+ const cryptoKey = await this.loadKey(secretKey);
5716
5801
  const iv = this.base64ToUint8Array(base64IV);
5717
5802
  const cipherText = this.base64ToArrayBuffer(base64Ciphertext);
5718
5803
  // Decrypt the cypher text
@@ -5724,14 +5809,12 @@ class CryptoService {
5724
5809
  const decoder = new TextDecoder();
5725
5810
  return decoder.decode(decrypted);
5726
5811
  }
5727
- async loadKey() {
5728
- if (!this.environments.secretKey) {
5729
- throw new Error('The secret key is not defined in the environment configuration.');
5812
+ async loadKey(secretKey) {
5813
+ if (!secretKey) {
5814
+ throw new Error('The secret key is not defined.');
5730
5815
  }
5731
- // Decode the base64 key to Uint8Array
5732
- const rawKey = this.base64ToUint8Array(this.environments.secretKey);
5733
- // Import the key to API Crypto
5734
- return await crypto.subtle.importKey('raw', rawKey, { name: 'AES-GCM' }, true, AES_KEY_USAGES);
5816
+ const rawKey = new TextEncoder().encode(secretKey);
5817
+ return crypto.subtle.importKey('raw', rawKey, { name: 'AES-GCM' }, true, AES_KEY_USAGES);
5735
5818
  }
5736
5819
  uint8ArrayToBase64(data) {
5737
5820
  let binary = '';