4 Commits

Author SHA1 Message Date
ARCG Admin (Claude)
487c801bab feat(ui): add Impressum/Datenschutz footer links
remote.arcg.at has the same Medieninhaber (ARCG) as the homepage, so link the
legal pages there: Impressum -> amateurradioclubgraz.at/imprint/, Datenschutz ->
amateurradioclubgraz.at/datenschutz/ (the homepage DSE now covers the remote
station processing).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-10-23 11:19:52 +02:00
dxd
1acbb569dd Merge pull request 'feat(wavelog): link to the Wavelog instance next to the API key field' (#3) from feat/wavelog-instance-link into main
Reviewed-on: #3
2026-10-22 21:46:20 +02:00
ARCG Admin (Claude)
9862f751a2 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 <noreply@anthropic.com>
2026-10-22 21:21:27 +02:00
dxd
3387ded386 Merge pull request 'fix(wavelog): push live tuned frequency, not just band center' (#2) from fix/wavelog-cat-live-frequency into main
Reviewed-on: #2
2026-10-22 21:17:36 +02:00
6 changed files with 60 additions and 3 deletions

View File

@@ -138,6 +138,7 @@ const els = {
wavelogKeyInput: document.getElementById("wavelogKeyInput"), wavelogKeyInput: document.getElementById("wavelogKeyInput"),
wavelogKeySaveBtn: document.getElementById("wavelogKeySaveBtn"), wavelogKeySaveBtn: document.getElementById("wavelogKeySaveBtn"),
wavelogKeyMessage: document.getElementById("wavelogKeyMessage"), wavelogKeyMessage: document.getElementById("wavelogKeyMessage"),
wavelogInstanceLink: document.getElementById("wavelogInstanceLink"),
openwebrxTxStatePill: document.getElementById("openwebrxTxStatePill"), openwebrxTxStatePill: document.getElementById("openwebrxTxStatePill"),
rotorCurrent: document.getElementById("rotorCurrent"), rotorCurrent: document.getElementById("rotorCurrent"),
rotorCompass: document.getElementById("rotorCompass"), rotorCompass: document.getElementById("rotorCompass"),
@@ -1505,6 +1506,7 @@ async function refreshCurrentUser() {
const result = await api("/v1/me", { authRequired: true }); const result = await api("/v1/me", { authRequired: true });
state.user = result.user; state.user = result.user;
state.wavelogApiKey = typeof result.wavelogApiKey === "string" ? result.wavelogApiKey : ""; state.wavelogApiKey = typeof result.wavelogApiKey === "string" ? result.wavelogApiKey : "";
state.wavelogBaseUrl = typeof result.wavelogBaseUrl === "string" ? result.wavelogBaseUrl : "";
applyWavelogKeyToInput(); applyWavelogKeyToInput();
renderUserSettingsAuthMethods(); renderUserSettingsAuthMethods();
await ensureLanguageFromUserPreference(); await ensureLanguageFromUserPreference();
@@ -1698,6 +1700,16 @@ function applyWavelogKeyToInput() {
if (els.wavelogKeyInput) { if (els.wavelogKeyInput) {
els.wavelogKeyInput.value = String(state.wavelogApiKey || ""); 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() { async function saveWavelogKey() {
@@ -1723,6 +1735,9 @@ async function openOpenWebRxSession() {
const session = result && result.session ? result.session : null; const session = result && result.session ? result.session : null;
if (result && typeof result.wavelogApiKey === "string") { if (result && typeof result.wavelogApiKey === "string") {
state.wavelogApiKey = result.wavelogApiKey; state.wavelogApiKey = result.wavelogApiKey;
if (typeof result.wavelogBaseUrl === "string") {
state.wavelogBaseUrl = result.wavelogBaseUrl;
}
applyWavelogKeyToInput(); applyWavelogKeyToInput();
} }
const sessionTicket = session && session.ticket ? String(session.ticket) : ""; const sessionTicket = session && session.ticket ? String(session.ticket) : "";
@@ -1752,6 +1767,9 @@ async function openOpenWebRxExternal() {
const session = result && result.session ? result.session : null; const session = result && result.session ? result.session : null;
if (result && typeof result.wavelogApiKey === "string") { if (result && typeof result.wavelogApiKey === "string") {
state.wavelogApiKey = result.wavelogApiKey; state.wavelogApiKey = result.wavelogApiKey;
if (typeof result.wavelogBaseUrl === "string") {
state.wavelogBaseUrl = result.wavelogBaseUrl;
}
applyWavelogKeyToInput(); applyWavelogKeyToInput();
} }
const sessionTicket = session && session.ticket ? String(session.ticket) : ""; const sessionTicket = session && session.ticket ? String(session.ticket) : "";

View File

@@ -56,6 +56,7 @@
"Key speichern": "Key speichern", "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.", "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 API Key gespeichert.": "Wavelog API Key gespeichert.",
"Wavelog öffnen": "Wavelog öffnen",
"Reservierungen": "Reservierungen", "Reservierungen": "Reservierungen",
"Naechsten Slot reservieren": "Naechsten Slot reservieren", "Naechsten Slot reservieren": "Naechsten Slot reservieren",
"SWR Uebersicht": "SWR Uebersicht", "SWR Uebersicht": "SWR Uebersicht",

View File

@@ -56,6 +56,7 @@
"Key speichern": "Save key", "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.", "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 API Key gespeichert.": "Wavelog API key saved.",
"Wavelog öffnen": "Open Wavelog",
"Reservierungen": "Reservations", "Reservierungen": "Reservations",
"Naechsten Slot reservieren": "Reserve next slot", "Naechsten Slot reservieren": "Reserve next slot",
"SWR Uebersicht": "SWR Overview", "SWR Uebersicht": "SWR Overview",

View File

@@ -167,7 +167,10 @@
<p class="muted">Session Key (Ticket): <code id="openwebrxSessionTicket">-</code></p> <p class="muted">Session Key (Ticket): <code id="openwebrxSessionTicket">-</code></p>
</div> </div>
<div id="wavelogKeyBox" class="stack"> <div id="wavelogKeyBox" class="stack">
<div class="section-head">
<label for="wavelogKeyInput">Wavelog API Key (Rig-Key)</label> <label for="wavelogKeyInput">Wavelog API Key (Rig-Key)</label>
<a id="wavelogInstanceLink" target="_blank" rel="noopener noreferrer" hidden>Wavelog öffnen</a>
</div>
<input id="wavelogKeyInput" type="text" autocomplete="off" spellcheck="false" placeholder="Wavelog API Key" /> <input id="wavelogKeyInput" type="text" autocomplete="off" spellcheck="false" placeholder="Wavelog API Key" />
<div class="actions"> <div class="actions">
<button id="wavelogKeySaveBtn" type="button" class="ghost-btn">Key speichern</button> <button id="wavelogKeySaveBtn" type="button" class="ghost-btn">Key speichern</button>
@@ -548,6 +551,12 @@
</nav> </nav>
</main> </main>
<footer class="site-footer">
<a href="https://amateurradioclubgraz.at/imprint/" target="_blank" rel="noopener noreferrer">Impressum</a>
<span class="site-footer-sep" aria-hidden="true">·</span>
<a href="https://amateurradioclubgraz.at/datenschutz/" target="_blank" rel="noopener noreferrer">Datenschutz</a>
</footer>
<script src="/app.js" defer></script> <script src="/app.js" defer></script>
</body> </body>
</html> </html>

View File

@@ -885,3 +885,24 @@ html[data-theme="light"] .message.domain-notice {
display: grid; display: grid;
} }
} }
/* Legal footer links (Impressum / Datenschutz -> homepage) */
.site-footer {
margin: 2rem auto 1.25rem;
padding: 1rem;
text-align: center;
font-size: 0.85rem;
opacity: 0.8;
}
.site-footer a {
color: inherit;
text-decoration: underline;
}
.site-footer a:hover,
.site-footer a:focus-visible {
opacity: 1;
}
.site-footer-sep {
margin: 0 0.5rem;
opacity: 0.6;
}

View File

@@ -360,7 +360,7 @@ async function routeRequest(req, res) {
if (method === "GET" && url.pathname === "/v1/me") { if (method === "GET" && url.pathname === "/v1/me") {
const auth = requireAuth(req, res); const auth = requireAuth(req, res);
if (!auth) return; 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") { if (method === "GET" && url.pathname === "/v1/auth/session/diag") {
@@ -3641,7 +3641,8 @@ async function handleOpenWebRxSessionIssue(res, user) {
return sendJson(res, 200, { return sendJson(res, 200, {
ok: true, ok: true,
session: result, 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). // Push a frequency/mode to the active operator's Wavelog logbook (CAT API).
// Best-effort: never throws into the caller's flow. // Best-effort: never throws into the caller's flow.
async function sendWavelogCat(user, frequencyHz, mode) { async function sendWavelogCat(user, frequencyHz, mode) {