From ee6e581b9d7249a260681469889d20bcb552f1b0 Mon Sep 17 00:00:00 2001 From: Ben Vincent Date: Fri, 26 Jun 2026 23:50:17 +1000 Subject: [PATCH] feat: configurable UI base path via BASE_PATH build arg (#58) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Serves the UI under /ui instead of /. This pairs with the argocd route simplification (argocd-apps#201) where /ui → UI service and everything else → API. - Vite: `base` set from `BASE_PATH` env var at build time - React Router: `basename` set from injected `__BASE_PATH__` - Nginx: location block uses `${BASE_PATH}`, substituted by sed at build - Dockerfile: `ARG BASE_PATH=/` (default preserves existing behavior) - Woodpecker: passes `BASE_PATH=/ui` to docker-web build Tested: assets serve at `/ui/assets/...`, SPA routing works at `/ui/remotes`, etc. Reviewed-on: https://git.unkin.net/unkin/artifactapi/pulls/58 Co-authored-by: Ben Vincent Co-committed-by: Ben Vincent --- .woodpecker/docker.yaml | 2 ++ ui/Dockerfile.ui | 7 +++++++ ui/nginx.conf | 28 +--------------------------- ui/src/main.tsx | 6 +++++- ui/vite.config.ts | 6 ++++++ 5 files changed, 21 insertions(+), 28 deletions(-) diff --git a/.woodpecker/docker.yaml b/.woodpecker/docker.yaml index 3addb4e..629cb8d 100644 --- a/.woodpecker/docker.yaml +++ b/.woodpecker/docker.yaml @@ -22,6 +22,8 @@ steps: repo: git.unkin.net/unkin/artifactapi-ui dockerfile: ui/Dockerfile.ui context: ui + build_args: + - BASE_PATH=/ui username: droneci password: from_secret: DRONECI_PASSWORD diff --git a/ui/Dockerfile.ui b/ui/Dockerfile.ui index 372b0ea..5cb5c57 100644 --- a/ui/Dockerfile.ui +++ b/ui/Dockerfile.ui @@ -6,13 +6,20 @@ COPY package.json package-lock.json* ./ RUN npm ci COPY . . + +ARG BASE_PATH=/ +ENV BASE_PATH=${BASE_PATH} RUN npm run build FROM nginx:alpine +ARG BASE_PATH=/ + COPY --from=builder /app/dist /usr/share/nginx/html COPY nginx.conf /etc/nginx/conf.d/default.conf +RUN sed -i "s|\${BASE_PATH}|${BASE_PATH}|g" /etc/nginx/conf.d/default.conf + EXPOSE 80 CMD ["nginx", "-g", "daemon off;"] diff --git a/ui/nginx.conf b/ui/nginx.conf index 4a0acd5..7a49804 100644 --- a/ui/nginx.conf +++ b/ui/nginx.conf @@ -5,33 +5,7 @@ server { root /usr/share/nginx/html; index index.html; - location /api/ { - proxy_pass http://artifactapi:8000; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_buffering off; - } - - location /v2/ { - proxy_pass http://artifactapi:8000; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_buffering off; - } - - location /health { - proxy_pass http://artifactapi:8000; - } - - location /metrics { - proxy_pass http://artifactapi:8000; - } - - location / { + location ${BASE_PATH} { try_files $uri $uri/ /index.html; } } diff --git a/ui/src/main.tsx b/ui/src/main.tsx index 8f6b53a..d53dc32 100644 --- a/ui/src/main.tsx +++ b/ui/src/main.tsx @@ -4,9 +4,13 @@ import { BrowserRouter } from 'react-router-dom'; import { App } from './App'; import './index.css'; +declare const __BASE_PATH__: string; + +const basename = __BASE_PATH__.replace(/\/+$/, '') || '/'; + createRoot(document.getElementById('root')!).render( - + , diff --git a/ui/vite.config.ts b/ui/vite.config.ts index 172618d..6f3f7cc 100644 --- a/ui/vite.config.ts +++ b/ui/vite.config.ts @@ -1,7 +1,10 @@ import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' +const basePath = process.env.BASE_PATH || '/' + export default defineConfig({ + base: basePath, plugins: [react()], server: { proxy: { @@ -11,4 +14,7 @@ export default defineConfig({ '/metrics': 'http://localhost:8000', }, }, + define: { + '__BASE_PATH__': JSON.stringify(basePath), + }, })