From 90b69f65d1ba019805122ec8c0916213f7f3fd88 Mon Sep 17 00:00:00 2001 From: creativeindustriesgroup Date: Sat, 24 May 2025 20:28:18 +0100 Subject: [PATCH 1/8] fix: use relative paths for assets --- frontend/vite.config.ts | 75 +++++++++++++++++++++-------------------- 1 file changed, 38 insertions(+), 37 deletions(-) diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index 722202add..8394d2006 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -1,41 +1,42 @@ -import {defineConfig} from "vite"; -import {lingui} from "@lingui/vite-plugin"; +import { lingui } from "@lingui/vite-plugin"; import react from "@vitejs/plugin-react"; -import {copy} from "vite-plugin-copy"; +import { defineConfig } from "vite"; +import { copy } from "vite-plugin-copy"; export default defineConfig({ - optimizeDeps: { - include: ["react-router"] - }, - server: { - hmr: { - port: 24678, - protocol: "ws", - }, - }, - plugins: [ - react({ - babel: { - plugins: ["macros"], - }, - }), - lingui(), - copy({ - targets: [{src: "src/embed/widget.js", dest: "public"}], - hook: "writeBundle", - }), - ], - define: { - "process.env": process.env, - }, - ssr: { - noExternal: ["react-helmet-async"], - }, - css: { - preprocessorOptions: { - scss: { - api: "modern-compiler", - } - } - } + base: "./", // Use relative paths for assets + optimizeDeps: { + include: ["react-router"], + }, + server: { + hmr: { + port: 24678, + protocol: "ws", + }, + }, + plugins: [ + react({ + babel: { + plugins: ["macros"], + }, + }), + lingui(), + copy({ + targets: [{ src: "src/embed/widget.js", dest: "public" }], + hook: "writeBundle", + }), + ], + define: { + "process.env": process.env, + }, + ssr: { + noExternal: ["react-helmet-async"], + }, + css: { + preprocessorOptions: { + scss: { + api: "modern-compiler", + }, + }, + }, }); From d63e199a619d157048dd0a686faaa74f269f8646 Mon Sep 17 00:00:00 2001 From: creativeindustriesgroup Date: Sat, 24 May 2025 20:59:49 +0100 Subject: [PATCH 2/8] feat: utility to get the base path from the env variables --- frontend/src/utilites/basePath.ts | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 frontend/src/utilites/basePath.ts diff --git a/frontend/src/utilites/basePath.ts b/frontend/src/utilites/basePath.ts new file mode 100644 index 000000000..6f46f9a16 --- /dev/null +++ b/frontend/src/utilites/basePath.ts @@ -0,0 +1,22 @@ +export function getBasePath() { + const frontendUrl: string = import.meta.env.VITE_FRONTEND_URL || "/"; // The env should be set but if not, fallback to root + + try { + const url = new URL(frontendUrl); + let basePath: string = url.pathname; + + // Make sure it always ends without trailing slash (except root) + if (basePath !== "/" && basePath.endsWith("/")) { + basePath = basePath.slice(0, -1); + } + + return basePath || "/"; + } catch (e) { + // If URL parsing fails, fallback to root + console.warn( + `Invalid frontend URL: ${frontendUrl}. This might be due to an incorrect environment variable 'VITE_FRONTEND_URL'.` + ); + + return "/"; + } +} From 00b995a1813459e799a776c70d912883e5e4e9e9 Mon Sep 17 00:00:00 2001 From: creativeindustriesgroup Date: Sat, 24 May 2025 21:01:46 +0100 Subject: [PATCH 3/8] refactor: rename router to routes so that we can add options as a separate argument later --- frontend/src/entry.client.tsx | 6 +++--- frontend/src/entry.server.tsx | 4 ++-- frontend/src/router.tsx | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/frontend/src/entry.client.tsx b/frontend/src/entry.client.tsx index 911560db9..2031d6a32 100644 --- a/frontend/src/entry.client.tsx +++ b/frontend/src/entry.client.tsx @@ -2,7 +2,7 @@ import {hydrateRoot} from "react-dom/client"; import {createBrowserRouter, matchRoutes, RouterProvider} from "react-router-dom"; import {hydrate} from "@tanstack/react-query"; -import {router} from "./router"; +import {routes} from "./router"; import {App} from "./App"; import {queryClient} from "./utilites/queryClient"; import {dynamicActivateLocale, getClientLocale, getSupportedLocale,} from "./locales.ts"; @@ -23,7 +23,7 @@ async function initClientApp() { await dynamicActivateLocale(locale); // Resolve lazy-loaded routes before hydration - const matches = matchRoutes(router, window.location)?.filter((m) => m.route.lazy); + const matches = matchRoutes(routes, window.location)?.filter((m) => m.route.lazy); if (matches && matches.length > 0) { await Promise.all( matches.map(async (m) => { @@ -33,7 +33,7 @@ async function initClientApp() { ); } - const browserRouter = createBrowserRouter(router); + const browserRouter = createBrowserRouter(routes); hydrateRoot( document.getElementById("app") as HTMLElement, diff --git a/frontend/src/entry.server.tsx b/frontend/src/entry.server.tsx index 0cc94cfa3..654d6c57d 100644 --- a/frontend/src/entry.server.tsx +++ b/frontend/src/entry.server.tsx @@ -2,7 +2,7 @@ import type * as express from "express"; import ReactDOMServer from "react-dom/server"; import {dehydrate} from "@tanstack/react-query"; -import {router} from "./router"; +import {routes} from "./router"; import {App} from "./App"; import {queryClient} from "./utilites/queryClient"; import {setAuthToken} from "./utilites/apiClient.ts"; @@ -26,7 +26,7 @@ export async function render(params: { }) { setAuthToken(params.req.cookies.token); - const {query, dataRoutes} = createStaticHandler(router); + const {query, dataRoutes} = createStaticHandler(routes); const remixRequest = createFetchRequest(params.req, params.res); const context = await query(remixRequest); diff --git a/frontend/src/router.tsx b/frontend/src/router.tsx index f13f9064c..ec787be62 100644 --- a/frontend/src/router.tsx +++ b/frontend/src/router.tsx @@ -1,4 +1,4 @@ -import {Navigate, RouteObject} from "react-router"; +import { Navigate, RouteObject} from "react-router"; import ErrorPage from "./error-page.tsx"; import {eventsClientPublic} from "./api/event.client.ts"; import {promoCodeClientPublic} from "./api/promo-code.client.ts"; @@ -20,7 +20,7 @@ const Root = () => { } }; -export const router: RouteObject[] = [ +export const routes: RouteObject[] = [ { path: "", element: , From 7d4e609626ef78c12aa8487c7cafb0c95df37421 Mon Sep 17 00:00:00 2001 From: creativeindustriesgroup Date: Sat, 24 May 2025 21:02:50 +0100 Subject: [PATCH 4/8] feat: dynamic base path in the browser router configuration --- frontend/src/entry.client.tsx | 4 ++-- frontend/src/router.tsx | 7 ++++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/frontend/src/entry.client.tsx b/frontend/src/entry.client.tsx index 2031d6a32..bfeb6e692 100644 --- a/frontend/src/entry.client.tsx +++ b/frontend/src/entry.client.tsx @@ -2,7 +2,7 @@ import {hydrateRoot} from "react-dom/client"; import {createBrowserRouter, matchRoutes, RouterProvider} from "react-router-dom"; import {hydrate} from "@tanstack/react-query"; -import {routes} from "./router"; +import {options, routes} from "./router"; import {App} from "./App"; import {queryClient} from "./utilites/queryClient"; import {dynamicActivateLocale, getClientLocale, getSupportedLocale,} from "./locales.ts"; @@ -33,7 +33,7 @@ async function initClientApp() { ); } - const browserRouter = createBrowserRouter(routes); + const browserRouter = createBrowserRouter(routes, options); hydrateRoot( document.getElementById("app") as HTMLElement, diff --git a/frontend/src/router.tsx b/frontend/src/router.tsx index ec787be62..4c74b1ea9 100644 --- a/frontend/src/router.tsx +++ b/frontend/src/router.tsx @@ -1,9 +1,10 @@ -import { Navigate, RouteObject} from "react-router"; +import { createBrowserRouter, Navigate, RouteObject} from "react-router"; import ErrorPage from "./error-page.tsx"; import {eventsClientPublic} from "./api/event.client.ts"; import {promoCodeClientPublic} from "./api/promo-code.client.ts"; import {useEffect, useState} from "react"; import {useGetMe} from "./queries/useGetMe.ts"; +import { getBasePath } from "./utilites/basePath.ts"; const Root = () => { const [redirectPath, setRedirectPath] = useState(null); @@ -20,6 +21,10 @@ const Root = () => { } }; +export const options: Parameters[1] = { + basename: getBasePath(), +}; + export const routes: RouteObject[] = [ { path: "", From d0a9c2dbce74f148baf0a086e0d661da41f539f0 Mon Sep 17 00:00:00 2001 From: creativeindustriesgroup Date: Sat, 24 May 2025 21:50:27 +0100 Subject: [PATCH 5/8] fix: react-router v6 does not automatically redirect users to the base path, so we should ensure the client is always at the base path --- frontend/src/App.tsx | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index fa93d7b61..e449dc917 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -17,6 +17,7 @@ import "./styles/global.scss"; import {isSsr} from "./utilites/helpers.ts"; import {StartupChecks} from "./StartupChecks.tsx"; import {ThirdPartyScripts} from "./components/common/ThirdPartyScripts"; +import { getBasePath } from "./utilites/basePath.ts"; declare global { interface Window { @@ -32,10 +33,20 @@ export const App: FC< }> > = (props) => { const [isLoadedOnBrowser, setIsLoadedOnBrowser] = React.useState(false); + const basePath = getBasePath(); useEffect(() => { setIsLoadedOnBrowser(!isSsr()); - }, []); + + // Ensure that the client is always accessing via the base path + // This is to ensure that the app is always served from the correct base path + if (!window.location.pathname.startsWith(basePath)) { + window.location.replace(basePath); + } + }, [] ); + + + return ( From 37e076f5f4138c0b23caee6a7f07e8b76a2eb9c2 Mon Sep 17 00:00:00 2001 From: creativeindustriesgroup Date: Sat, 24 May 2025 22:58:57 +0100 Subject: [PATCH 6/8] fix: ssr/client env variable --- frontend/src/utilites/basePath.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/frontend/src/utilites/basePath.ts b/frontend/src/utilites/basePath.ts index 6f46f9a16..562062522 100644 --- a/frontend/src/utilites/basePath.ts +++ b/frontend/src/utilites/basePath.ts @@ -1,5 +1,7 @@ +import { getConfig } from "./config"; + export function getBasePath() { - const frontendUrl: string = import.meta.env.VITE_FRONTEND_URL || "/"; // The env should be set but if not, fallback to root + const frontendUrl: string = getConfig("VITE_FRONTEND_URL") as string; try { const url = new URL(frontendUrl); @@ -11,10 +13,10 @@ export function getBasePath() { } return basePath || "/"; - } catch (e) { + } catch ( e ) { // If URL parsing fails, fallback to root console.warn( - `Invalid frontend URL: ${frontendUrl}. This might be due to an incorrect environment variable 'VITE_FRONTEND_URL'.` + `Invalid frontend URL: ${frontendUrl}. This might be due to an incorrect environment variable 'VITE_FRONTEND_URL'.`, e ); return "/"; From b53853ca0a5e2e9c575425012ebda90576ab582f Mon Sep 17 00:00:00 2001 From: creativeindustriesgroup Date: Sun, 25 May 2025 14:11:59 +0100 Subject: [PATCH 7/8] feat: set vite base url dynamically --- frontend/vite.config.ts | 71 +++++++++++++++++++++-------------------- 1 file changed, 37 insertions(+), 34 deletions(-) diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index 8394d2006..9c0ecd24d 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -1,42 +1,45 @@ import { lingui } from "@lingui/vite-plugin"; import react from "@vitejs/plugin-react"; -import { defineConfig } from "vite"; +import { defineConfig, loadEnv } from "vite"; import { copy } from "vite-plugin-copy"; -export default defineConfig({ - base: "./", // Use relative paths for assets - optimizeDeps: { - include: ["react-router"], - }, - server: { - hmr: { - port: 24678, - protocol: "ws", +export default defineConfig( ( { mode } ) => { + const env = loadEnv(mode, process.cwd(), ""); + return { + base: new URL(env.VITE_FRONTEND_URL).pathname || "/", + optimizeDeps: { + include: ["react-router"], }, - }, - plugins: [ - react({ - babel: { - plugins: ["macros"], + server: { + hmr: { + port: 24678, + protocol: "ws", }, - }), - lingui(), - copy({ - targets: [{ src: "src/embed/widget.js", dest: "public" }], - hook: "writeBundle", - }), - ], - define: { - "process.env": process.env, - }, - ssr: { - noExternal: ["react-helmet-async"], - }, - css: { - preprocessorOptions: { - scss: { - api: "modern-compiler", + }, + plugins: [ + react({ + babel: { + plugins: ["macros"], + }, + }), + lingui(), + copy({ + targets: [{ src: "src/embed/widget.js", dest: "public" }], + hook: "writeBundle", + }), + ], + define: { + "process.env": process.env, + }, + ssr: { + noExternal: ["react-helmet-async"], + }, + css: { + preprocessorOptions: { + scss: { + api: "modern-compiler", + }, }, }, - }, -}); + }; +}) From 9bf51371992716677bc783e013f6c16f049a5222 Mon Sep 17 00:00:00 2001 From: creativeindustriesgroup Date: Sun, 25 May 2025 14:14:38 +0100 Subject: [PATCH 8/8] fix: use import.meta for CSR edge cases --- frontend/src/utilites/basePath.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/utilites/basePath.ts b/frontend/src/utilites/basePath.ts index 562062522..f5846e33f 100644 --- a/frontend/src/utilites/basePath.ts +++ b/frontend/src/utilites/basePath.ts @@ -1,8 +1,8 @@ import { getConfig } from "./config"; export function getBasePath() { - const frontendUrl: string = getConfig("VITE_FRONTEND_URL") as string; - + const frontendUrl: string = getConfig( "VITE_FRONTEND_URL" ) as string || import.meta.env.VITE_FRONTEND_URL as string; + try { const url = new URL(frontendUrl); let basePath: string = url.pathname;