diff --git a/cogstack-cohorter/.env.example b/cogstack-cohorter/.env.example index 444eb52..9191324 100644 --- a/cogstack-cohorter/.env.example +++ b/cogstack-cohorter/.env.example @@ -30,7 +30,7 @@ NPM_TOKEN=ghp_your_token_here # DEFAULT_USER_NAME=Local User # DEFAULT_USER_EMAIL= # DEFAULT_USER_ID=local -# DEFAULT_USER_GROUPS= # comma-separated, e.g. "cohorter-users,role:admin" +# DEFAULT_USER_GROUPS=cohorter-users # ── Other runtime options ───────────────────────────────────────────────────── diff --git a/cogstack-cohorter/WebAPP/Dockerfile b/cogstack-cohorter/WebAPP/Dockerfile index e1bb153..c73e269 100644 --- a/cogstack-cohorter/WebAPP/Dockerfile +++ b/cogstack-cohorter/WebAPP/Dockerfile @@ -1,14 +1,4 @@ FROM node:20 - -ARG VITE_OAUTH2_USERINFO_PATH -ARG VITE_OAUTH2_LOGIN_PATH -ARG VITE_OAUTH2_LOGOUT_PATH - -# VITE_* vars must be ENV (not just ARG) for Vite to pick them up at build time. -ENV VITE_OAUTH2_USERINFO_PATH=${VITE_OAUTH2_USERINFO_PATH} -ENV VITE_OAUTH2_LOGIN_PATH=${VITE_OAUTH2_LOGIN_PATH} -ENV VITE_OAUTH2_LOGOUT_PATH=${VITE_OAUTH2_LOGOUT_PATH} - WORKDIR /usr/src/app/client-react COPY client-react/package*.json ./ RUN --mount=type=secret,id=npm_token,required=true \ diff --git a/cogstack-cohorter/WebAPP/README.md b/cogstack-cohorter/WebAPP/README.md index 4766e4c..069f98f 100644 --- a/cogstack-cohorter/WebAPP/README.md +++ b/cogstack-cohorter/WebAPP/README.md @@ -46,6 +46,39 @@ Please make sure to have the six data files ready in the `server/data/` folder b The recommended way to run the full stack (webapp + NL2DSL + MedCAT + Ollama) is via `docker-compose` from the `cogstack-cohorter/` root — see the root-level README and `.env.example` for details. The `NPM_TOKEN` is passed as a BuildKit secret; set it in the `.env` file before building. +### Environment variables + +The webapp is configured entirely through environment variables — no rebuild is required to change settings. + +#### Server (Express backend) + +| Variable | Default | Description | +|---|-------------------------------------|---| +| `PORT` | `3000` | Port the Express server listens on | +| `NL2DSL_SERVER` | `http://localhost:3002/api/compile` | URL of the NL2DSL service | +| `RANDOM_DATA` | `true` | `true` → generate and serve synthetic demo data; `false` → load real data from `server/data/` | +| `PASSWORD` | `admin_pass` | Password for the admin export panel | +| `DEFAULT_USER_NAME` | `Local User` | Display name shown in the header when no oauth2-proxy is deployed | +| `DEFAULT_USER_EMAIL` | _(empty)_ | Email shown in the user panel when no oauth2-proxy is deployed | +| `DEFAULT_USER_ID` | `local` | Opaque user ID returned by `/oauth2/userinfo` when no oauth2-proxy is deployed | +| `DEFAULT_USER_GROUPS` | `cohorter-users` | Comma-separated groups returned by `/oauth2/userinfo` when no oauth2-proxy is deployed | + +#### Frontend runtime config (written to `config.js` at container startup) + +At startup `entrypoint.sh` writes `/config.js` into the pre-built static assets folder. `index.html` loads this file before the React bundle, so the values are available as `window.__RUNTIME_CONFIG__` without a rebuild. + +These variables are only needed when **oauth2-proxy is deployed in front of the app**. The defaults work correctly for standalone deployments — the Express server handles `/oauth2/userinfo` itself and returns the `DEFAULT_USER_*` values above. + +| Variable | Default | Description | +|---|---|---| +| `OAUTH2_USERINFO_PATH` | `/oauth2/userinfo` | Path the `UserSection` component fetches to get the logged-in user's info. With oauth2-proxy this is intercepted by oauth2-proxy before reaching Express. Without oauth2-proxy it hits the Express fallback endpoint. | +| `OAUTH2_LOGIN_PATH` | `/oauth2/sign_in` | Path the header's "Sign in" button links to | +| `OAUTH2_LOGOUT_PATH` | `/oauth2/sign_out?rd=/` | Path the header's "Sign out" button links to | + +When deploying with oauth2-proxy, the defaults are correct and no override is needed unless the oauth2-proxy is mounted at a non-standard prefix. + +In Kubernetes these are set as env vars on the webapp container (via helm `cogstack-cohorter.webapp.env`) and are picked up automatically on the next pod restart. + ### Run using Docker with random data Set `RANDOM_DATA=true` in your `.env` file (or leave it unset — it defaults to `true`), then from the `cogstack-cohorter/` root: diff --git a/cogstack-cohorter/WebAPP/client-react/index.html b/cogstack-cohorter/WebAPP/client-react/index.html index 64e916a..cf53d2a 100644 --- a/cogstack-cohorter/WebAPP/client-react/index.html +++ b/cogstack-cohorter/WebAPP/client-react/index.html @@ -36,6 +36,7 @@
+ diff --git a/cogstack-cohorter/WebAPP/client-react/src/App.jsx b/cogstack-cohorter/WebAPP/client-react/src/App.jsx index d6622ae..21f2160 100644 --- a/cogstack-cohorter/WebAPP/client-react/src/App.jsx +++ b/cogstack-cohorter/WebAPP/client-react/src/App.jsx @@ -10,10 +10,10 @@ import { LoadQueryDialog } from './LoadQueryDialog'; // Fixed header height const HEADER_HEIGHT = 80; -// OAuth2 proxy paths — configurable at build time via .env -const USERINFO_PATH = import.meta.env.VITE_OAUTH2_USERINFO_PATH ?? '/oauth2/userinfo'; -const LOGIN_PATH = import.meta.env.VITE_OAUTH2_LOGIN_PATH ?? '/oauth2/sign_in'; -const LOGOUT_PATH = import.meta.env.VITE_OAUTH2_LOGOUT_PATH ?? '/oauth2/sign_out?rd=/'; +const runtimeConfig = (typeof window !== 'undefined' && window.__RUNTIME_CONFIG__) || {}; +const USERINFO_PATH = runtimeConfig.OAUTH2_USERINFO_PATH || '/oauth2/userinfo'; +const LOGIN_PATH = runtimeConfig.OAUTH2_LOGIN_PATH || '/oauth2/sign_in'; +const LOGOUT_PATH = runtimeConfig.OAUTH2_LOGOUT_PATH || '/oauth2/sign_out?rd=/'; function App() { const rootRef = useRef(null); diff --git a/cogstack-cohorter/WebAPP/entrypoint.sh b/cogstack-cohorter/WebAPP/entrypoint.sh index 29b3064..f29b5a4 100644 --- a/cogstack-cohorter/WebAPP/entrypoint.sh +++ b/cogstack-cohorter/WebAPP/entrypoint.sh @@ -18,5 +18,24 @@ if [ "${RANDOM_DATA}" = "true" ] && \ node --max-old-space-size=32768 /usr/src/app/server/gen_random_data.js fi +# ── Inject runtime config for the React app ────────────────────────────────── +# Writes config.js into the pre-built dist folder so Express serves it +# as /config.js. +# Override any of these via OAUTH2_* env vars. +# +# Values are sanitised before being embedded in the JS string literal: +# backslashes are escaped first, then double-quotes, preventing a +# malformed env var from injecting arbitrary JavaScript. +_js_escape() { printf '%s' "$1" | sed 's/\\/\\\\/g; s/"/\\"/g'; } + +CONFIG_JS=/usr/src/app/client-react/dist/config.js +{ + printf 'window.__RUNTIME_CONFIG__ = {\n' + printf ' OAUTH2_USERINFO_PATH: "%s",\n' "$(_js_escape "${OAUTH2_USERINFO_PATH:-/oauth2/userinfo}")" + printf ' OAUTH2_LOGIN_PATH: "%s",\n' "$(_js_escape "${OAUTH2_LOGIN_PATH:-/oauth2/sign_in}")" + printf ' OAUTH2_LOGOUT_PATH: "%s",\n' "$(_js_escape "${OAUTH2_LOGOUT_PATH:-/oauth2/sign_out?rd=/}")" + printf '};\n' +} > "$CONFIG_JS" + # ── Start the server ────────────────────────────────────────────────────────── exec node --max-old-space-size=32768 server.js diff --git a/cogstack-cohorter/WebAPP/server/server.js b/cogstack-cohorter/WebAPP/server/server.js index 7eb06a5..62a2738 100644 --- a/cogstack-cohorter/WebAPP/server/server.js +++ b/cogstack-cohorter/WebAPP/server/server.js @@ -675,13 +675,9 @@ app.post('/compare_query', (req, res) => { // // During development (or when OAuth2 is not yet deployed) this returns a // configurable default user so the Header/UserSection renders correctly -// without any real authentication. Override the defaults via env vars: -// DEFAULT_USER_NAME — displayed username (default: "Local User") -// DEFAULT_USER_EMAIL — user email (default: "") -// DEFAULT_USER_ID — opaque user id (default: "local") -// DEFAULT_USER_GROUPS — comma-separated groups (default: "") +// without any real authentication. app.get("/oauth2/userinfo", (req, res) => { - const groups = process.env.DEFAULT_USER_GROUPS + const groups = process.env.DEFAULT_USER_GROUPS || 'cohorter-users' ? process.env.DEFAULT_USER_GROUPS.split(',').map(g => g.trim()).filter(Boolean) : []; res.status(200).json({ diff --git a/cogstack-cohorter/docker-compose.yml b/cogstack-cohorter/docker-compose.yml index cc73957..f93dc50 100644 --- a/cogstack-cohorter/docker-compose.yml +++ b/cogstack-cohorter/docker-compose.yml @@ -79,7 +79,7 @@ services: DEFAULT_USER_EMAIL: "${DEFAULT_USER_EMAIL:-}" DEFAULT_USER_ID: "${DEFAULT_USER_ID:-local}" # Comma-separated list, e.g. "cohorter-users,role:admin" - DEFAULT_USER_GROUPS: "${DEFAULT_USER_GROUPS:-}" + DEFAULT_USER_GROUPS: "${DEFAULT_USER_GROUPS:-cohorter-users}" volumes: # Mount your data directory here. It should contain either: # - snomed_terms_data.tar.gz (will be auto-extracted on startup), or