From 9862f751a2f2684252aeff85c7082dc27cb3c4d8 Mon Sep 17 00:00:00 2001 From: "ARCG Admin (Claude)" Date: Thu, 22 Oct 2026 21:21:27 +0200 Subject: [PATCH] feat(wavelog): link to the Wavelog instance next to the API key field Show an "Open Wavelog" link beside the Wavelog API Key heading in the OpenWebRX panel. The URL comes from the runtime WAVELOG_BASE_URL (exposed via GET /v1/me and the OpenWebRX session response), so the private hostname is never hardcoded in this public repo. The link is hidden when no base URL is configured. Co-Authored-By: Claude Opus 4.8 --- public/app.js | 18 ++++++++++++++++++ public/i18n/de.json | 1 + public/i18n/en.json | 1 + public/index.html | 5 ++++- server/index.js | 11 +++++++++-- 5 files changed, 33 insertions(+), 3 deletions(-) diff --git a/public/app.js b/public/app.js index bd0f3ea..897b033 100644 --- a/public/app.js +++ b/public/app.js @@ -138,6 +138,7 @@ const els = { wavelogKeyInput: document.getElementById("wavelogKeyInput"), wavelogKeySaveBtn: document.getElementById("wavelogKeySaveBtn"), wavelogKeyMessage: document.getElementById("wavelogKeyMessage"), + wavelogInstanceLink: document.getElementById("wavelogInstanceLink"), openwebrxTxStatePill: document.getElementById("openwebrxTxStatePill"), rotorCurrent: document.getElementById("rotorCurrent"), rotorCompass: document.getElementById("rotorCompass"), @@ -1505,6 +1506,7 @@ async function refreshCurrentUser() { const result = await api("/v1/me", { authRequired: true }); state.user = result.user; state.wavelogApiKey = typeof result.wavelogApiKey === "string" ? result.wavelogApiKey : ""; + state.wavelogBaseUrl = typeof result.wavelogBaseUrl === "string" ? result.wavelogBaseUrl : ""; applyWavelogKeyToInput(); renderUserSettingsAuthMethods(); await ensureLanguageFromUserPreference(); @@ -1698,6 +1700,16 @@ function applyWavelogKeyToInput() { if (els.wavelogKeyInput) { els.wavelogKeyInput.value = String(state.wavelogApiKey || ""); } + if (els.wavelogInstanceLink) { + const url = String(state.wavelogBaseUrl || "").trim(); + if (url) { + els.wavelogInstanceLink.href = url; + els.wavelogInstanceLink.hidden = false; + } else { + els.wavelogInstanceLink.removeAttribute("href"); + els.wavelogInstanceLink.hidden = true; + } + } } async function saveWavelogKey() { @@ -1723,6 +1735,9 @@ async function openOpenWebRxSession() { const session = result && result.session ? result.session : null; if (result && typeof result.wavelogApiKey === "string") { state.wavelogApiKey = result.wavelogApiKey; + if (typeof result.wavelogBaseUrl === "string") { + state.wavelogBaseUrl = result.wavelogBaseUrl; + } applyWavelogKeyToInput(); } const sessionTicket = session && session.ticket ? String(session.ticket) : ""; @@ -1752,6 +1767,9 @@ async function openOpenWebRxExternal() { const session = result && result.session ? result.session : null; if (result && typeof result.wavelogApiKey === "string") { state.wavelogApiKey = result.wavelogApiKey; + if (typeof result.wavelogBaseUrl === "string") { + state.wavelogBaseUrl = result.wavelogBaseUrl; + } applyWavelogKeyToInput(); } const sessionTicket = session && session.ticket ? String(session.ticket) : ""; diff --git a/public/i18n/de.json b/public/i18n/de.json index 30fd217..5da7f5a 100644 --- a/public/i18n/de.json +++ b/public/i18n/de.json @@ -56,6 +56,7 @@ "Key speichern": "Key speichern", "Frequenz und Mode der Station werden beim Bandwechsel an dein Wavelog-Logbuch gesendet.": "Frequenz und Mode der Station werden beim Bandwechsel an dein Wavelog-Logbuch gesendet.", "Wavelog API Key gespeichert.": "Wavelog API Key gespeichert.", + "Wavelog öffnen": "Wavelog öffnen", "Reservierungen": "Reservierungen", "Naechsten Slot reservieren": "Naechsten Slot reservieren", "SWR Uebersicht": "SWR Uebersicht", diff --git a/public/i18n/en.json b/public/i18n/en.json index 6f5e4e7..9d98d70 100644 --- a/public/i18n/en.json +++ b/public/i18n/en.json @@ -56,6 +56,7 @@ "Key speichern": "Save key", "Frequenz und Mode der Station werden beim Bandwechsel an dein Wavelog-Logbuch gesendet.": "The station's frequency and mode are sent to your Wavelog logbook on band change.", "Wavelog API Key gespeichert.": "Wavelog API key saved.", + "Wavelog öffnen": "Open Wavelog", "Reservierungen": "Reservations", "Naechsten Slot reservieren": "Reserve next slot", "SWR Uebersicht": "SWR Overview", diff --git a/public/index.html b/public/index.html index a26bf3a..df58f0e 100644 --- a/public/index.html +++ b/public/index.html @@ -167,7 +167,10 @@

Session Key (Ticket): -

- +
+ + +
diff --git a/server/index.js b/server/index.js index 86e1fe0..11df935 100644 --- a/server/index.js +++ b/server/index.js @@ -360,7 +360,7 @@ async function routeRequest(req, res) { if (method === "GET" && url.pathname === "/v1/me") { const auth = requireAuth(req, res); if (!auth) return; - return sendJson(res, 200, { user: sanitizeUser(auth.user), wavelogApiKey: auth.user.wavelogApiKey || "", capabilities: userCapabilities(auth.user) }); + return sendJson(res, 200, { user: sanitizeUser(auth.user), wavelogApiKey: auth.user.wavelogApiKey || "", wavelogBaseUrl: wavelogBaseUrlPublic(), capabilities: userCapabilities(auth.user) }); } if (method === "GET" && url.pathname === "/v1/auth/session/diag") { @@ -3641,7 +3641,8 @@ async function handleOpenWebRxSessionIssue(res, user) { return sendJson(res, 200, { ok: true, session: result, - wavelogApiKey: user.wavelogApiKey || "" + wavelogApiKey: user.wavelogApiKey || "", + wavelogBaseUrl: wavelogBaseUrlPublic() }); } @@ -3719,6 +3720,12 @@ async function handleOpenWebRxBandSelect(res, user, body) { } } +// Public Wavelog base URL (for the UI link). Comes from runtime env only, so the +// private hostname is never baked into this (public) repo. +function wavelogBaseUrlPublic() { + return String(process.env.WAVELOG_BASE_URL || "").trim().replace(/\/+$/, ""); +} + // Push a frequency/mode to the active operator's Wavelog logbook (CAT API). // Best-effort: never throws into the caller's flow. async function sendWavelogCat(user, frequencyHz, mode) {