From a02d084b54ec27a24fb0db6edada2c07b65b3a6a Mon Sep 17 00:00:00 2001 From: Robert Date: Tue, 28 Jul 2026 14:56:21 -0700 Subject: [PATCH] fix: public API toggle silently fails to save on auth/CSRF error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit togglePublicApi() updated the switch UI optimistically before the save request resolved, and only checked JSON body `ok` for a success toast — a non-2xx response (expired session -> 401, stale CSRF -> 403) left the toggle showing "on" with no error, while nothing was actually persisted. Confirmed via direct API testing that persistence itself was already correct; this was purely a client-side silent-failure bug. Now the UI only updates after a confirmed 2xx + {ok:true} response, and a failed save surfaces a visible error toast instead of failing silently. Particularly relevant here since this setting controls whether /api/state is exposed without authentication. --- templates/index.html | 42 ++++++++++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/templates/index.html b/templates/index.html index 30a12c9..69b8ec5 100644 --- a/templates/index.html +++ b/templates/index.html @@ -4591,21 +4591,35 @@

Warum Mediastarr anders ist

let _publicApiState = false; function togglePublicApi() { - _publicApiState = !_publicApiState; - const tog = document.getElementById('tog-public-api'); - const lab = document.getElementById('lab-public-api'); - if (tog) tog.className = 'toggle' + (_publicApiState ? ' on' : ''); - if (lab) lab.textContent = t(_publicApiState ? 'on' : 'off'); - MSLog.info('togglePublicApi →', _publicApiState); + const next = !_publicApiState; + MSLog.info('togglePublicApi →', next); fetch(BASE + '/api/config', {method:'POST', headers:{'Content-Type':'application/json'}, - body: JSON.stringify({public_api_state: _publicApiState}) - }).then(r => r.json()).then(d => { - if (d.ok) showActionMessage( - currentLang === 'de' - ? (_publicApiState ? '✓ /api/state ist jetzt öffentlich zugänglich (kein Login)' : '✓ /api/state erfordert jetzt Login') - : (_publicApiState ? '✓ /api/state is now publicly accessible (no login)' : '✓ /api/state now requires login'), - _publicApiState ? 'warning' : 'success', 5000); - }).catch(e => MSLog.error('togglePublicApi save failed', e)); + body: JSON.stringify({public_api_state: next}) + }).then(async r => { + const d = await r.json().catch(() => ({})); + if (r.ok && d.ok) { + _publicApiState = next; + const tog = document.getElementById('tog-public-api'); + const lab = document.getElementById('lab-public-api'); + if (tog) tog.className = 'toggle' + (_publicApiState ? ' on' : ''); + if (lab) lab.textContent = t(_publicApiState ? 'on' : 'off'); + showActionMessage( + currentLang === 'de' + ? (_publicApiState ? '✓ /api/state ist jetzt öffentlich zugänglich (kein Login)' : '✓ /api/state erfordert jetzt Login') + : (_publicApiState ? '✓ /api/state is now publicly accessible (no login)' : '✓ /api/state now requires login'), + _publicApiState ? 'warning' : 'success', 5000); + } else { + MSLog.error('togglePublicApi save failed', d.error || r.status); + showActionMessage( + currentLang === 'de' + ? 'Speichern fehlgeschlagen — bitte neu einloggen und erneut versuchen' + : 'Save failed — please log in again and retry', + 'error', 5000); + } + }).catch(e => { + MSLog.error('togglePublicApi save failed', e); + showActionMessage(currentLang === 'de' ? 'Speichern fehlgeschlagen' : 'Save failed', 'error', 5000); + }); } async function exportConfig() {