fix: wire auth PIN to frontend — 401 on NAM select and all POST/PUT calls
CI / test (push) Has been cancelled

Root cause: auth middleware (1603bfe) blocked all non-GET /api/ endpoints
requiring X-Pedal-Auth header, but the PIN was never served to the frontend.

Fix:
- Added auth_pin to /api/state response (safe GET endpoint, no auth required)
- Frontend stores pin from state on initial load
- apiPost/apiPut/apiDelete/apiUpload now include X-Pedal-Auth header
This commit is contained in:
2026-06-17 22:51:23 -04:00
parent 2558306e78
commit ad5b369a10
5 changed files with 695 additions and 5 deletions
+11 -5
View File
@@ -37,6 +37,12 @@ function _ch(url) {
}
// ── API helpers ────────────────────────────────────────────────
let _authPin = '';
function _authHeaders() {
return _authPin ? { 'X-Pedal-Auth': _authPin } : {};
}
async function apiGet(path) {
const r = await fetch(`${API}${_ch(path)}`);
if (!r.ok) throw new Error(`${path} => ${r.status}`);
@@ -45,7 +51,7 @@ async function apiGet(path) {
async function apiPost(path, data) {
const r = await fetch(`${API}${_ch(path)}`, {
method: 'POST', headers: { 'Content-Type': 'application/json' },
method: 'POST', headers: { 'Content-Type': 'application/json', ..._authHeaders() },
body: data != null ? JSON.stringify(data) : undefined,
});
if (!r.ok) throw new Error(`${path} => ${r.status}`);
@@ -54,7 +60,7 @@ async function apiPost(path, data) {
async function apiPut(path, data) {
const r = await fetch(`${API}${_ch(path)}`, {
method: 'PUT', headers: { 'Content-Type': 'application/json' },
method: 'PUT', headers: { 'Content-Type': 'application/json', ..._authHeaders() },
body: JSON.stringify(data),
});
if (!r.ok) throw new Error(`${path} => ${r.status}`);
@@ -62,7 +68,7 @@ async function apiPut(path, data) {
}
async function apiDelete(path) {
const r = await fetch(`${API}${_ch(path)}`, { method: 'DELETE' });
const r = await fetch(`${API}${_ch(path)}`, { method: 'DELETE', headers: _authHeaders() });
if (!r.ok) throw new Error(`${path} => ${r.status}`);
return r.json();
}
@@ -70,7 +76,7 @@ async function apiDelete(path) {
async function apiUpload(path, file) {
const fd = new FormData();
fd.append('file', file);
const r = await fetch(`${API}${_ch(path)}`, { method: 'POST', body: fd });
const r = await fetch(`${API}${_ch(path)}`, { method: 'POST', body: fd, headers: _authHeaders() });
if (!r.ok) throw new Error(`${path} => ${r.status}`);
return r.json();
}
@@ -84,7 +90,7 @@ function usePedalState() {
useEffect(() => {
// Fetch initial state
apiGet('/api/state').then(s => { setState(s); setConnected(true); }).catch(() => { setConnected(false); });
apiGet('/api/state').then(s => { setState(s); setConnected(true); _authPin = s.auth_pin || ''; }).catch(() => { setConnected(false); });
// WebSocket for real-time updates
let reconnectTimer;