From f51291704da47e9d425ba64f530af884708b8795 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Bosi?= <71827178+bosiraphael@users.noreply.github.com> Date: Mon, 9 Feb 2026 19:10:29 +0100 Subject: [PATCH] [FRONT COMPONENTS] Retrieve the front components from the backend (#17813) - Pass the auth token to worker - Fetch the file from the rest API with the auth token - Create the blob url - Update the stories to pass a fake token which will be ignored --------- Co-authored-by: Charles Bochet --- .../components/FrontComponentRenderer.tsx | 14 ++++-- .../utils/mockFrontComponent.ts | 43 ------------------- .../FrontComponentRenderer.stories.tsx | 1 + .../components/FrontComponentRenderer.tsx | 4 ++ .../components/FrontComponentWorkerEffect.tsx | 6 ++- .../remote/worker/remote-worker.ts | 32 +++++++++++--- .../types/HostToWorkerRenderContext.ts | 1 + 7 files changed, 47 insertions(+), 54 deletions(-) delete mode 100644 packages/twenty-front/src/modules/front-components/utils/mockFrontComponent.ts diff --git a/packages/twenty-front/src/modules/front-components/components/FrontComponentRenderer.tsx b/packages/twenty-front/src/modules/front-components/components/FrontComponentRenderer.tsx index 4a56887df9..dbd8819349 100644 --- a/packages/twenty-front/src/modules/front-components/components/FrontComponentRenderer.tsx +++ b/packages/twenty-front/src/modules/front-components/components/FrontComponentRenderer.tsx @@ -1,4 +1,5 @@ -import { getMockFrontComponentUrl } from '@/front-components/utils/mockFrontComponent'; +import { REST_API_BASE_URL } from '@/apollo/constant/rest-api-base-url'; +import { getTokenPair } from '@/apollo/utils/getTokenPair'; import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; import { useTheme } from '@emotion/react'; import { t } from '@lingui/core/macro'; @@ -13,7 +14,7 @@ type FrontComponentRendererProps = { }; export const FrontComponentRenderer = ({ - frontComponentId: _frontComponentId, + frontComponentId, }: FrontComponentRendererProps) => { const theme = useTheme(); const [hasError, setHasError] = useState(false); @@ -22,6 +23,9 @@ export const FrontComponentRenderer = ({ const { executionContext, frontComponentHostCommunicationApi } = useFrontComponentExecutionContext(); + const componentUrl = `${REST_API_BASE_URL}/front-components/${frontComponentId}`; + const authToken = getTokenPair()?.accessOrWorkspaceAgnosticToken?.token; + const handleError = (error?: Error) => { if (isDefined(error)) { const errorMessage = error.message; @@ -33,14 +37,16 @@ export const FrontComponentRenderer = ({ setHasError(true); }; - if (hasError) { + if (hasError || !isDefined(authToken)) { + // TODO: Add an error display component here return null; } return ( globalThis.TwentySdk.navigate)(); - -var AppPath = /* @__PURE__ */ (() => globalThis.TwentyShared["types"].AppPath)(); - -var Button = /* @__PURE__ */ (() => globalThis.RemoteComponents.TwentyUiButton)(); - -var jsx = /* @__PURE__ */ (() => globalThis.jsx)(); - -var TestComponent = () => { - return /* @__PURE__ */ jsx( - Button, - { - title: "Navigate to people index page", - onClick: () => navigate?.(AppPath.RecordIndexPage, { - objectNamePlural: "people" - }) - } - ); -}; - -var test_component_front_component_default = globalThis.jsx(TestComponent, {}); -export { - test_component_front_component_default as default -}; -`; - -let cachedMockBlobUrl: string | null = null; - -export const getMockFrontComponentUrl = (): string => { - if (isDefined(cachedMockBlobUrl)) { - return cachedMockBlobUrl; - } - - const blob = new Blob([mockFrontComponentCode], { - type: 'application/javascript', - }); - cachedMockBlobUrl = URL.createObjectURL(blob); - - return cachedMockBlobUrl; -}; diff --git a/packages/twenty-sdk/src/front-component/__stories__/FrontComponentRenderer.stories.tsx b/packages/twenty-sdk/src/front-component/__stories__/FrontComponentRenderer.stories.tsx index 55d3e601dd..fdb19418fd 100644 --- a/packages/twenty-sdk/src/front-component/__stories__/FrontComponentRenderer.stories.tsx +++ b/packages/twenty-sdk/src/front-component/__stories__/FrontComponentRenderer.stories.tsx @@ -15,6 +15,7 @@ const meta: Meta = { }, args: { onError: errorHandler, + authToken: 'fake-token', }, beforeEach: () => { errorHandler.mockClear(); diff --git a/packages/twenty-sdk/src/front-component/host/components/FrontComponentRenderer.tsx b/packages/twenty-sdk/src/front-component/host/components/FrontComponentRenderer.tsx index f6a35bb1ed..b384a8eec2 100644 --- a/packages/twenty-sdk/src/front-component/host/components/FrontComponentRenderer.tsx +++ b/packages/twenty-sdk/src/front-component/host/components/FrontComponentRenderer.tsx @@ -19,6 +19,7 @@ import { componentRegistry } from '../generated/host-component-registry'; type FrontComponentContentProps = { componentUrl: string; + authToken: string; executionContext: FrontComponentExecutionContext; frontComponentHostCommunicationApi: FrontComponentHostCommunicationApi; onError: (error?: Error) => void; @@ -27,6 +28,7 @@ type FrontComponentContentProps = { export const FrontComponentRenderer = ({ componentUrl, + authToken, executionContext, frontComponentHostCommunicationApi, onError, @@ -43,6 +45,7 @@ export const FrontComponentRenderer = ({ return ( >; setThread: React.Dispatch< @@ -20,6 +21,7 @@ type FrontComponentWorkerEffectProps = { export const FrontComponentWorkerEffect = ({ componentUrl, + authToken, frontComponentHostCommunicationApi, setReceiver, setThread, @@ -56,7 +58,7 @@ export const FrontComponentWorkerEffect = ({ setThread(thread); thread.imports - .render(newReceiver.connection, { componentUrl }) + .render(newReceiver.connection, { componentUrl, authToken }) .catch((error: Error) => { setError(error); }); @@ -67,7 +69,7 @@ export const FrontComponentWorkerEffect = ({ setThread(null); worker.terminate(); }; - }, [componentUrl, setError, setReceiver, setThread]); + }, [componentUrl, authToken, setError, setReceiver, setThread]); return null; }; diff --git a/packages/twenty-sdk/src/front-component/remote/worker/remote-worker.ts b/packages/twenty-sdk/src/front-component/remote/worker/remote-worker.ts index 873bb9d01f..0a301a275d 100644 --- a/packages/twenty-sdk/src/front-component/remote/worker/remote-worker.ts +++ b/packages/twenty-sdk/src/front-component/remote/worker/remote-worker.ts @@ -15,9 +15,9 @@ import { jsx, jsxs } from 'react/jsx-runtime'; import * as TwentySharedTypes from 'twenty-shared/types'; import * as TwentySharedUtils from 'twenty-shared/utils'; +import * as TwentySdk from '@/sdk'; import { setFrontComponentExecutionContext } from '@/sdk/front-component-api/context/frontComponentContext'; import { setNavigate } from '@/sdk/front-component-api/functions/navigate'; -import * as TwentySdk from '@/sdk'; import { type FrontComponentExecutionContext } from '../../types/FrontComponentExecutionContext'; import { type FrontComponentHostCommunicationApi } from '../../types/FrontComponentHostCommunicationApi'; @@ -47,11 +47,33 @@ const render: WorkerExports['render'] = async ( root.connect(batchedConnection); document.body.append(root); - /* @vite-ignore */ - const componentModule = await import(renderContext.componentUrl); + const response = await fetch(renderContext.componentUrl, { + headers: { Authorization: `Bearer ${renderContext.authToken}` }, + }); - const reactRoot = createRoot(root); - reactRoot.render(componentModule.default); + if (!response.ok) { + throw new Error( + `Failed to fetch front component from ${renderContext.componentUrl}: ${response.status} ${response.statusText}`, + ); + } + + const responseText = await response.text(); + + const blob = new Blob([responseText], { + type: 'application/javascript', + }); + + const importUrl = URL.createObjectURL(blob); + + try { + /* @vite-ignore */ + const componentModule = await import(importUrl); + + const reactRoot = createRoot(root); + reactRoot.render(componentModule.default); + } finally { + URL.revokeObjectURL(importUrl); + } }; const initializeHostCommunicationApi: WorkerExports['initializeHostCommunicationApi'] = diff --git a/packages/twenty-sdk/src/front-component/types/HostToWorkerRenderContext.ts b/packages/twenty-sdk/src/front-component/types/HostToWorkerRenderContext.ts index 1e0a5ab8f5..f181a34565 100644 --- a/packages/twenty-sdk/src/front-component/types/HostToWorkerRenderContext.ts +++ b/packages/twenty-sdk/src/front-component/types/HostToWorkerRenderContext.ts @@ -1,3 +1,4 @@ export type HostToWorkerRenderContext = { componentUrl: string; + authToken: string; };