diff --git a/src/locales/en/services/translation.json b/src/locales/en/services/translation.json index df385ba417..bf56608117 100644 --- a/src/locales/en/services/translation.json +++ b/src/locales/en/services/translation.json @@ -139,7 +139,8 @@ "sendingCodeSuggestionAccepted": "Sending code suggestion accepted for file {{fileName}}, operation: {{operation}}", "sendingCodeSuggestionRejected": "Sending code suggestion rejected for file {{fileName}}, operation: {{operation}}, reason: {{reason}}", "sendingDatadogSessionId": "Sending Datadog session ID: {{sessionId}}", - "sendingDatadogViewId": "Sending Datadog view ID: {{viewId}}" + "sendingDatadogViewId": "Sending Datadog view ID: {{viewId}}", + "errorParsingIframeOrigin": "Error parsing iframe origin URL: {{error}}" } }, "errors": { @@ -159,7 +160,8 @@ "errorImportingStoreForDiagramDisplayHandling": "Error importing store for diagram display handling: {{error}}", "serviceDestroyedDuringNavigation": "Service destroyed during navigation", "serviceResetDuringNavigation": "Service reset during navigation", - "errorImportingDatadogUtils": "Error importing Datadog utils: {{error}}" + "errorImportingDatadogUtils": "Error importing Datadog utils: {{error}}", + "errorNavigatingToBilling": "Error navigating to billing: {{error}}" } } } diff --git a/src/services/iframeComm.service.ts b/src/services/iframeComm.service.ts index f19af942d7..de2904d6e9 100644 --- a/src/services/iframeComm.service.ts +++ b/src/services/iframeComm.service.ts @@ -41,19 +41,17 @@ export const CONFIG = { class IframeCommService { private readonly expectedOrigin: string = ((): string => { try { - if (aiChatbotOrigin && aiChatbotOrigin.startsWith("http")) { + if (aiChatbotOrigin && (aiChatbotOrigin.startsWith("http") || aiChatbotOrigin.startsWith("https"))) { return new URL(aiChatbotOrigin).origin; } - if (aiChatbotUrl && aiChatbotUrl.startsWith("http")) { - return new URL(aiChatbotUrl).origin; - } - return aiChatbotOrigin?.replace(/\/$/, "") || ""; } catch (error) { - // eslint-disable-next-line no-console - console.error("Failed to parse aiChatbotOrigin or aiChatbotUrl:", error); - return aiChatbotOrigin || ""; + LoggerService.debug( + namespaces.iframeCommService, + `Failed to parse aiChatbotOrigin or aiChatbotUrl: ${aiChatbotOrigin} ${aiChatbotUrl}: ${error}` + ); + return ""; } })(); private listeners: MessageListener[] = []; @@ -121,14 +119,6 @@ class IframeCommService { } this.iframeRef = iframe; - - if (this.messageQueue.length > 0) { - LoggerService.debug( - namespaces.iframeCommService, - `Iframe ref set - processing ${this.messageQueue.length} queued messages` - ); - void this.processMessageQueue(); - } } public destroy(): void { @@ -194,7 +184,7 @@ class IframeCommService { t("debug.iframeComm.handshakeMessageSent", { ns: "services" }) ); } catch (error) { - LoggerService.error( + LoggerService.debug( namespaces.iframeCommService, t("errors.iframeComm.failedToSendHandshakeMessage", { ns: "services", error }) ); @@ -279,15 +269,16 @@ class IframeCommService { } if (this.iframeRef.contentWindow) { + const targetOrigin = this.getTargetOrigin(); LoggerService.debug( namespaces.iframeCommService, t("debug.iframeComm.postingMessageToChatbot", { ns: "services", - origin: this.expectedOrigin || aiChatbotOrigin, + origin: targetOrigin, message: JSON.stringify(messageToSend), }) ); - this.iframeRef.contentWindow.postMessage(messageToSend, this.expectedOrigin || aiChatbotOrigin); + this.iframeRef.contentWindow.postMessage(messageToSend, targetOrigin); } else { throw new Error(t("errors.iframeComm.iframeContentWindowNotAvailable", { ns: "services" })); } @@ -300,7 +291,7 @@ class IframeCommService { this.queueProcessCount++; if (this.queueProcessCount > this.maxQueueProcessAttempts) { - LoggerService.error( + LoggerService.debug( namespaces.iframeCommService, t("errors.iframeComm.queueProcessingExceeded", { ns: "services", @@ -316,6 +307,7 @@ class IframeCommService { try { const batchSize = 5; let processed = 0; + const targetOrigin = this.getTargetOrigin(); while (this.messageQueue.length > 0 && processed < batchSize) { if (!this.isConnected) { @@ -325,7 +317,7 @@ class IframeCommService { const message = this.messageQueue.shift(); if (message) { if (this.iframeRef?.contentWindow) { - this.iframeRef.contentWindow.postMessage(message, this.expectedOrigin || aiChatbotOrigin); + this.iframeRef.contentWindow.postMessage(message, targetOrigin); } processed++; } @@ -491,7 +483,7 @@ class IframeCommService { return undefined; }) .catch((error) => { - LoggerService.error( + LoggerService.debug( namespaces.iframeCommService, t("errors.iframeComm.errorImportingDatadogUtils", { ns: "services", @@ -608,30 +600,41 @@ class IframeCommService { } } - private isValidOrigin(origin: string): boolean { - const expectedOrigin = this.expectedOrigin || aiChatbotOrigin; - - if (origin === expectedOrigin) { - return true; - } - + private getTargetOrigin(): string { if (this.iframeRef && this.iframeRef.src) { try { const iframeOrigin = new URL(this.iframeRef.src).origin; - if (origin === iframeOrigin) { - return true; - } - // eslint-disable-next-line @typescript-eslint/no-unused-vars + return iframeOrigin; } catch (error) { - // Ignore errors when extracting iframe origin + LoggerService.debug( + namespaces.iframeCommService, + t("debug.iframeComm.errorParsingIframeOrigin", { + ns: "services", + error: (error as Error).message, + iframeRefSrc: this.iframeRef.src, + }) + ); } } - if (origin.includes("localhost") || origin.includes("127.0.0.1")) { - return true; - } + return this.expectedOrigin; + } - return false; + private isValidOrigin(origin: string): boolean { + try { + const url = new URL(origin); + const hostname = url.hostname; + const validHostnames = ["localhost", "127.0.0.1"]; + + return ( + validHostnames.includes(hostname) || + hostname === "autokitteh.cloud" || + hostname.endsWith(".autokitteh.cloud") + ); + } catch (error) { + LoggerService.debug(namespaces.iframeCommService, `Failed to parse origin URL: ${origin}: ${error}`); + return false; + } } private isValidMessage(message: unknown): message is AkbotMessage { @@ -658,6 +661,19 @@ class IframeCommService { private async handleIncomingMessages(event: MessageEvent): Promise { try { + if (!this.iframeRef || !document.contains(this.iframeRef)) { + return; + } + const origin = this.getTargetOrigin(); + if (event.origin === origin && !this.isConnected) { + this.isConnected = true; + if (this.connectionResolve) { + this.connectionResolve(); + this.connectionResolve = null; + } + this.sendDatadogContext(); + } + if (!this.isValidOrigin(event.origin)) { LoggerService.debug( namespaces.iframeCommService, @@ -676,21 +692,12 @@ class IframeCommService { } catch (error) { LoggerService.debug( namespaces.iframeCommService, - `ered message due to origin mismatch: error extracting type/source ${error}` + `Filtered message due to origin mismatch: error extracting type/source ${error}` ); } return; } - if (event.origin === (this.expectedOrigin || aiChatbotOrigin) && !this.isConnected) { - this.isConnected = true; - if (this.connectionResolve) { - this.connectionResolve(); - this.connectionResolve = null; - } - this.sendDatadogContext(); - } - const message = event.data as AkbotMessage; if (!Object.values(MessageTypes).includes(message?.type)) { @@ -720,19 +727,6 @@ class IframeCommService { await this.processMessageQueue(); } - if (!this.iframeRef || !document.contains(this.iframeRef)) { - if (message.type !== MessageTypes.HANDSHAKE && message.type !== MessageTypes.HANDSHAKE_ACK) { - LoggerService.debug( - namespaces.iframeCommService, - `Received ${message.type} but iframe ref not ready - adding to queue` - ); - if (this.messageQueue.length < this.maxQueueSize) { - this.messageQueue.push(message); - } - return; - } - } - switch (message.type) { case MessageTypes.HANDSHAKE: { const handshakeAckMessage: HandshakeAckMessage = { @@ -742,14 +736,7 @@ class IframeCommService { version: CONFIG.APP_VERSION, }, }; - if (this.iframeRef && document.contains(this.iframeRef)) { - this.sendMessage(handshakeAckMessage); - } else { - LoggerService.debug( - namespaces.iframeCommService, - "Received HANDSHAKE but iframe ref not ready yet - waiting for iframe to be set" - ); - } + this.sendMessage(handshakeAckMessage); break; } case MessageTypes.HANDSHAKE_ACK: @@ -792,7 +779,7 @@ class IframeCommService { listener.callback(message); }); } catch (error) { - LoggerService.error( + LoggerService.debug( namespaces.iframeCommService, t("errors.iframeComm.errorProcessingIncomingMessage", { ns: "services", @@ -830,7 +817,7 @@ class IframeCommService { return true; }) .catch((error) => { - LoggerService.error( + LoggerService.debug( namespaces.iframeCommService, t("errors.iframeComm.errorImportingStoreForDiagramDisplayHandling", { ns: "services", @@ -852,7 +839,7 @@ class IframeCommService { return true; }) .catch((error) => { - LoggerService.error( + LoggerService.debug( namespaces.iframeCommService, t("errors.iframeComm.errorImportingStoreForVarUpdatedHandling", { ns: "services", @@ -874,7 +861,7 @@ class IframeCommService { try { window.location.href = "/organization-settings/billing"; } catch (error) { - LoggerService.error( + LoggerService.debug( namespaces.iframeCommService, t("errors.iframeComm.errorNavigatingToBilling", { ns: "services", @@ -969,7 +956,7 @@ class IframeCommService { } catch (error) { operationResult.success = false; failureCount++; - LoggerService.error( + LoggerService.debug( namespaces.iframeCommService, `Failed to apply ${operation} operation for file ${fileName}: ${(error as Error).message}` ); @@ -1145,7 +1132,7 @@ class IframeCommService { } catch (error) { const errorMessage = error instanceof Error ? error.message : "Unknown error"; - LoggerService.error( + LoggerService.debug( namespaces.iframeCommService, t("errors.iframeComm.failedToDownloadFile", { ns: "services", @@ -1185,7 +1172,7 @@ class IframeCommService { URL.revokeObjectURL(url); } catch (error) { const errorMessage = error instanceof Error ? error.message : "Unknown error"; - LoggerService.error( + LoggerService.debug( namespaces.iframeCommService, t("errors.iframeComm.failedToDownloadChatFile", { ns: "services",