From f6fb033eb8f7bf64b1c07fddff32b47065e31b49 Mon Sep 17 00:00:00 2001 From: Kosmos Date: Tue, 25 Aug 2026 16:53:58 +0000 Subject: [PATCH] Derive API path from frontend config URL --- Dockerfile | 9 ++++++ README.md | 6 ++-- docker/use-config-url-api-path.mjs | 50 ++++++++++++++++++++++++++++++ 3 files changed, 63 insertions(+), 2 deletions(-) create mode 100644 docker/use-config-url-api-path.mjs diff --git a/Dockerfile b/Dockerfile index 0e1d049..cfdfd8c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -17,6 +17,7 @@ COPY upstream/HubFrontend/ ./ COPY docker/hcaptcha-sitekey.patch /tmp/hcaptcha-sitekey.patch COPY docker/remove-analytics.mjs /tmp/remove-analytics.mjs COPY docker/replace-fallback-avatar.mjs /tmp/replace-fallback-avatar.mjs +COPY docker/use-config-url-api-path.mjs /tmp/use-config-url-api-path.mjs RUN if grep -q 'sitekey="1788882d-3695-4807-abac-7d7166ec6325"' src/routes/auth/login.js; then \ patch -p1 < /tmp/hcaptcha-sitekey.patch; \ @@ -29,6 +30,7 @@ RUN if grep -q 'sitekey="1788882d-3695-4807-abac-7d7166ec6325"' src/routes/auth/ RUN node /tmp/remove-analytics.mjs RUN node /tmp/replace-fallback-avatar.mjs +RUN node /tmp/use-config-url-api-path.mjs ARG ASSETS_BASE="" ARG VITE_USE_MULTIHUB=false @@ -45,6 +47,13 @@ ENV ASSETS_BASE=${ASSETS_BASE} \ RUN test -n "$VITE_HCAPTCHA_SITEKEY" \ || (echo "VITE_HCAPTCHA_SITEKEY must be set." >&2; exit 1) +RUN if [ "$VITE_USE_MULTIHUB" != "true" ]; then \ + case "$VITE_CONFIG_URL" in \ + */client/config/global|*/client/config/global/) ;; \ + *) echo "VITE_CONFIG_URL must end with /client/config/global." >&2; exit 1 ;; \ + esac; \ + fi + RUN npm run build # Electron entry points are not part of the web deployment. diff --git a/README.md b/README.md index e369db5..c97cc6e 100644 --- a/README.md +++ b/README.md @@ -40,8 +40,10 @@ as `VITE_HCAPTCHA_SITEKEY`. Set the related secret in the backend not a secret. Enable the `client-config` external plugin in the backend configuration. The -backend `abbr` value must match the API prefix without its leading slash. For -example, use `"abbr": "api"` with `"prefix": "/api"`. +single-hub build derives the API base URL from `VITE_CONFIG_URL`. The backend +`abbr` can contain the VTC abbreviation and does not have to match the API +prefix. This deployment-specific behavior does not apply when +`VITE_USE_MULTIHUB` is `true`. The frontend uses the Vite values during the image build. Rebuild the image after you change `.env`, including the hCaptcha site key. diff --git a/docker/use-config-url-api-path.mjs b/docker/use-config-url-api-path.mjs new file mode 100644 index 0000000..21e1fb3 --- /dev/null +++ b/docker/use-config-url-api-path.mjs @@ -0,0 +1,50 @@ +import { readFileSync, writeFileSync } from "node:fs"; + +const loaderPath = "src/components/loader.js"; +let source = readFileSync(loaderPath, "utf8"); + +const upstreamApiPath = "`${webConfig.api_host}/${webConfig.abbr}`"; +const deploymentApiPath = "getApiPath(webConfig)"; +const helperMarker = "function getApiPath(webConfig) {"; +const insertBefore = "function sleep(ms) {"; +const occurrences = source.split(upstreamApiPath).length - 1; + +if (occurrences === 3 && !source.includes(helperMarker)) { + const helper = `function getApiPath(webConfig) { + if (import.meta.env.VITE_USE_MULTIHUB == "true") { + return \`${upstreamApiPath.slice(1, -1)}\`; + } + + const configUrl = new URL(import.meta.env.VITE_CONFIG_URL); + const endpoint = "/client/config/global"; + const pathname = configUrl.pathname.replace(/\\/+$/, ""); + if (!pathname.endsWith(endpoint)) { + throw new Error("VITE_CONFIG_URL must end with /client/config/global."); + } + + configUrl.pathname = pathname.slice(0, -endpoint.length) || "/"; + configUrl.search = ""; + configUrl.hash = ""; + return configUrl.toString().replace(/\\/$/, ""); +} + +`; + + if (!source.includes(insertBefore)) { + throw new Error("Cannot locate the API path helper insertion point."); + } + + source = source.replace(insertBefore, helper + insertBefore); + source = source.replaceAll(upstreamApiPath, deploymentApiPath); + console.log("Use VITE_CONFIG_URL as the single-hub API path source."); +} else if (occurrences === 0 && !/api_host[^;\n]{0,80}abbr/.test(source)) { + console.log("Upstream does not contain the known API path dependency; skip the patch."); +} else { + throw new Error(`Unsupported upstream API path implementation (${occurrences} known references).`); +} + +if (source.includes(upstreamApiPath) && !source.includes("VITE_USE_MULTIHUB")) { + throw new Error("The upstream API path dependency remains without a multihub fallback."); +} + +writeFileSync(loaderPath, source);