From 5dedb7c4c10d8f68ce8b6b756617b0940c1e5afe Mon Sep 17 00:00:00 2001 From: Onur Date: Wed, 4 Mar 2026 00:18:23 +0100 Subject: [PATCH] feat(ui): add configurable launch URL query params --- helm/spritz/templates/ui-deployment.yaml | 2 + helm/spritz/values.yaml | 2 + ui/entrypoint.sh | 3 + ui/public/app.js | 77 +++++++++++++++++++++++- ui/public/config.js | 3 + 5 files changed, 86 insertions(+), 1 deletion(-) diff --git a/helm/spritz/templates/ui-deployment.yaml b/helm/spritz/templates/ui-deployment.yaml index 800daa2..8e97240 100644 --- a/helm/spritz/templates/ui-deployment.yaml +++ b/helm/spritz/templates/ui-deployment.yaml @@ -74,6 +74,8 @@ spec: value: {{ .Values.ui.repoDefaults.branch | quote }} - name: SPRITZ_UI_HIDE_REPO_INPUTS value: {{ .Values.ui.repoDefaults.hideInputs | quote }} + - name: SPRITZ_UI_LAUNCH_QUERY_PARAMS + value: {{ .Values.ui.launch.queryParams | toJson | quote }} {{- if .Values.ui.assetVersion }} - name: SPRITZ_UI_ASSET_VERSION value: {{ .Values.ui.assetVersion | quote }} diff --git a/helm/spritz/values.yaml b/helm/spritz/values.yaml index 2e62f26..a6b9aca 100644 --- a/helm/spritz/values.yaml +++ b/helm/spritz/values.yaml @@ -241,6 +241,8 @@ ui: dir: "" branch: "" hideInputs: false + launch: + queryParams: {} auth: mode: none tokenStorage: localStorage diff --git a/ui/entrypoint.sh b/ui/entrypoint.sh index 2585a98..c3d42a4 100755 --- a/ui/entrypoint.sh +++ b/ui/entrypoint.sh @@ -24,6 +24,7 @@ DEFAULT_REPO_URL="${SPRITZ_UI_DEFAULT_REPO_URL:-}" DEFAULT_REPO_DIR="${SPRITZ_UI_DEFAULT_REPO_DIR:-}" DEFAULT_REPO_BRANCH="${SPRITZ_UI_DEFAULT_REPO_BRANCH:-}" HIDE_REPO_INPUTS="${SPRITZ_UI_HIDE_REPO_INPUTS:-}" +LAUNCH_QUERY_PARAMS="${SPRITZ_UI_LAUNCH_QUERY_PARAMS:-}" ASSET_VERSION="${SPRITZ_UI_ASSET_VERSION:-}" if [ -z "$API_BASE_URL" ]; then @@ -60,6 +61,7 @@ DEFAULT_REPO_URL_ESCAPED="$(escape_sed "$DEFAULT_REPO_URL")" DEFAULT_REPO_DIR_ESCAPED="$(escape_sed "$DEFAULT_REPO_DIR")" DEFAULT_REPO_BRANCH_ESCAPED="$(escape_sed "$DEFAULT_REPO_BRANCH")" HIDE_REPO_INPUTS_ESCAPED="$(escape_sed "$HIDE_REPO_INPUTS")" +LAUNCH_QUERY_PARAMS_ESCAPED="$(escape_sed "$LAUNCH_QUERY_PARAMS")" ASSET_VERSION_ESCAPED="$(escape_sed "$ASSET_VERSION")" sed "s|__SPRITZ_API_BASE_URL__|${API_BASE_URL_ESCAPED}|g" /usr/share/nginx/html/config.js \ @@ -73,6 +75,7 @@ sed "s|__SPRITZ_API_BASE_URL__|${API_BASE_URL_ESCAPED}|g" /usr/share/nginx/html/ | sed "s|__SPRITZ_UI_DEFAULT_REPO_DIR__|${DEFAULT_REPO_DIR_ESCAPED}|g" \ | sed "s|__SPRITZ_UI_DEFAULT_REPO_BRANCH__|${DEFAULT_REPO_BRANCH_ESCAPED}|g" \ | sed "s|__SPRITZ_UI_HIDE_REPO_INPUTS__|${HIDE_REPO_INPUTS_ESCAPED}|g" \ + | sed "s|__SPRITZ_UI_LAUNCH_QUERY_PARAMS__|${LAUNCH_QUERY_PARAMS_ESCAPED}|g" \ | sed "s|__SPRITZ_UI_AUTH_LOGIN_URL__|${AUTH_LOGIN_URL_ESCAPED}|g" \ | sed "s|__SPRITZ_UI_AUTH_RETURN_TO_MODE__|${AUTH_RETURN_TO_MODE_ESCAPED}|g" \ | sed "s|__SPRITZ_UI_AUTH_RETURN_TO_PARAM__|${AUTH_RETURN_TO_PARAM_ESCAPED}|g" \ diff --git a/ui/public/app.js b/ui/public/app.js index 44f46e8..99daf26 100644 --- a/ui/public/app.js +++ b/ui/public/app.js @@ -23,6 +23,9 @@ const defaultRepoUrl = String(repoDefaults.url || '').trim(); const defaultRepoDir = String(repoDefaults.dir || '').trim(); const defaultRepoBranch = String(repoDefaults.branch || '').trim(); const hideRepoInputs = parseBoolean(repoDefaults.hideInputs, false); +const launchQueryParamsPlaceholder = '__SPRITZ_UI_LAUNCH_QUERY_PARAMS__'; +const launchConfig = config.launch || {}; +const launchQueryParams = parseTemplateMap(launchConfig.queryParams); const authReturnToPlaceholder = '__SPRITZ_RETURN_TO__'; const noticeEl = document.getElementById('notice'); const listEl = document.getElementById('list'); @@ -421,6 +424,33 @@ function parseStorageKeys(value) { .filter(Boolean); } +function normalizeTemplateMap(value) { + if (!value || typeof value !== 'object' || Array.isArray(value)) return {}; + const normalized = {}; + for (const [key, raw] of Object.entries(value)) { + const name = String(key || '').trim(); + if (!name) continue; + if (raw === undefined || raw === null) continue; + normalized[name] = String(raw); + } + return normalized; +} + +function parseTemplateMap(value) { + if (!value) return {}; + if (typeof value === 'object') { + return normalizeTemplateMap(value); + } + const trimmed = String(value).trim(); + if (!trimmed || trimmed === launchQueryParamsPlaceholder) return {}; + try { + const parsed = JSON.parse(trimmed); + return normalizeTemplateMap(parsed); + } catch { + return {}; + } +} + function parsePresets(raw) { if (Array.isArray(raw)) return raw; if (typeof raw === 'string') { @@ -863,6 +893,51 @@ async function fetchSpritzes() { } } +function applyTemplatePlaceholders(template, context) { + if (!template) return ''; + return String(template).replace(/\{([a-zA-Z0-9_]+)\}/g, (_, key) => { + const value = context[key]; + if (value === undefined || value === null) return ''; + return String(value); + }); +} + +function buildOpenUrl(rawUrl, spritz) { + const input = String(rawUrl || '').trim(); + if (!input) return ''; + let url; + try { + url = new URL(input, window.location.href); + } catch { + return input; + } + const queryEntries = Object.entries(launchQueryParams); + if (!queryEntries.length) { + return url.href; + } + const name = String(spritz?.metadata?.name || '').trim(); + const namespace = String(spritz?.metadata?.namespace || '').trim(); + const context = { + origin: url.origin, + host: url.host, + hostname: url.hostname, + path: url.pathname, + query: url.search, + name, + namespace, + name_encoded: encodeURIComponent(name), + namespace_encoded: encodeURIComponent(namespace), + path_encoded: encodeURIComponent(url.pathname), + ui_origin: window.location.origin, + ws_origin: url.origin.replace(/^http/i, 'ws'), + }; + for (const [param, template] of queryEntries) { + const value = applyTemplatePlaceholders(template, context); + url.searchParams.set(param, value); + } + return url.href; +} + function renderList(items) { updateKnownSpritzNames(items); if (!items.length) { @@ -891,7 +966,7 @@ function renderList(items) { const openBtn = document.createElement('button'); openBtn.textContent = 'Open'; openBtn.onclick = () => { - const url = spritz.status?.url; + const url = buildOpenUrl(spritz.status?.url, spritz); if (url) window.open(url, '_blank'); }; diff --git a/ui/public/config.js b/ui/public/config.js index 5262139..9d9d793 100644 --- a/ui/public/config.js +++ b/ui/public/config.js @@ -8,6 +8,9 @@ window.SPRITZ_CONFIG = { branch: '__SPRITZ_UI_DEFAULT_REPO_BRANCH__', hideInputs: '__SPRITZ_UI_HIDE_REPO_INPUTS__', }, + launch: { + queryParams: '__SPRITZ_UI_LAUNCH_QUERY_PARAMS__', + }, auth: { mode: '__SPRITZ_UI_AUTH_MODE__', tokenStorage: '__SPRITZ_UI_AUTH_TOKEN_STORAGE__',