From 51abe45fba8a6b491477c309b70958d4aa9d8eee Mon Sep 17 00:00:00 2001
From: hsiegeln <37154749+hsiegeln@users.noreply.github.com>
Date: Sun, 5 Apr 2026 21:04:28 +0200
Subject: [PATCH] feat: add BASE_PATH env var for serving UI from a subpath
When BASE_PATH is set (e.g., /server/), the entrypoint script injects
a tag and rewrites asset paths in index.html. React Router reads
the basename from the tag. Vite builds with relative paths.
Default / for standalone mode (no changes).
Co-Authored-By: Claude Opus 4.6 (1M context)
---
ui/Dockerfile | 7 +++++++
ui/docker-entrypoint.sh | 21 +++++++++++++++++++++
ui/src/router.tsx | 4 +++-
ui/vite.config.ts | 1 +
4 files changed, 32 insertions(+), 1 deletion(-)
create mode 100644 ui/docker-entrypoint.sh
diff --git a/ui/Dockerfile b/ui/Dockerfile
index e0a9826a..a89d813a 100644
--- a/ui/Dockerfile
+++ b/ui/Dockerfile
@@ -20,8 +20,15 @@ RUN npm run build
FROM nginx:1.27-alpine
COPY --from=build /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/templates/default.conf.template
+COPY docker-entrypoint.sh /cameleer-entrypoint.sh
+RUN chmod +x /cameleer-entrypoint.sh
# Default API URL — override via K8s env or docker run -e
ENV CAMELEER_API_URL=http://cameleer3-server:8081
+# Base path for serving the SPA from a subpath (e.g., /server/). Default: /
+ENV BASE_PATH=/
+
+ENTRYPOINT ["/cameleer-entrypoint.sh"]
+CMD ["nginx", "-g", "daemon off;"]
EXPOSE 80
diff --git a/ui/docker-entrypoint.sh b/ui/docker-entrypoint.sh
new file mode 100644
index 00000000..382afd65
--- /dev/null
+++ b/ui/docker-entrypoint.sh
@@ -0,0 +1,21 @@
+#!/bin/sh
+# Inject tag into index.html when BASE_PATH is set.
+# This allows the SPA to be served from a subpath (e.g., /server/).
+# Default: / (standalone mode, no tag needed).
+
+BASE_PATH="${BASE_PATH:-/}"
+
+if [ "$BASE_PATH" != "/" ]; then
+ # Ensure BASE_PATH starts and ends with /
+ BASE_PATH=$(echo "$BASE_PATH" | sed 's#/*$#/#; s#^/*#/#')
+
+ INDEX="/usr/share/nginx/html/index.html"
+ # Inject tag after and rewrite absolute asset paths
+ sed -i "s|||" "$INDEX"
+ sed -i "s|href=\"/|href=\"${BASE_PATH}|g; s|src=\"/|src=\"${BASE_PATH}|g" "$INDEX"
+
+ echo "BASE_PATH set to ${BASE_PATH} — rewrote index.html"
+fi
+
+# Delegate to the default nginx entrypoint (handles envsubst for nginx templates)
+exec /docker-entrypoint.sh "$@"
diff --git a/ui/src/router.tsx b/ui/src/router.tsx
index 6bb8a444..36c4bfdf 100644
--- a/ui/src/router.tsx
+++ b/ui/src/router.tsx
@@ -41,6 +41,8 @@ function LegacyAgentRedirect() {
return ;
}
+const basename = document.querySelector('base')?.getAttribute('href')?.replace(/\/$/, '') || '';
+
export const router = createBrowserRouter([
{ path: '/login', element: },
{ path: '/oidc/callback', element: },
@@ -101,4 +103,4 @@ export const router = createBrowserRouter([
},
],
},
-]);
+], { basename: basename || undefined });
diff --git a/ui/vite.config.ts b/ui/vite.config.ts
index db5bbe6a..1f0d0a7d 100644
--- a/ui/vite.config.ts
+++ b/ui/vite.config.ts
@@ -27,6 +27,7 @@ export default defineConfig({
optimizeDeps: {
include: ['swagger-ui-dist/swagger-ui-bundle'],
},
+ base: './',
build: {
outDir: 'dist',
},