Auth was blocking all POST/PUT operations with 401. Disabled per user request. PIN generation kept (harmless). Frontend reverted to no-auth API helpers.
This commit is contained in:
@@ -37,12 +37,6 @@ function _ch(url) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ── API helpers ────────────────────────────────────────────────
|
// ── API helpers ────────────────────────────────────────────────
|
||||||
let _authPin = '';
|
|
||||||
|
|
||||||
function _authHeaders() {
|
|
||||||
return _authPin ? { 'X-Pedal-Auth': _authPin } : {};
|
|
||||||
}
|
|
||||||
|
|
||||||
async function apiGet(path) {
|
async function apiGet(path) {
|
||||||
const r = await fetch(`${API}${_ch(path)}`);
|
const r = await fetch(`${API}${_ch(path)}`);
|
||||||
if (!r.ok) throw new Error(`${path} => ${r.status}`);
|
if (!r.ok) throw new Error(`${path} => ${r.status}`);
|
||||||
@@ -51,7 +45,7 @@ async function apiGet(path) {
|
|||||||
|
|
||||||
async function apiPost(path, data) {
|
async function apiPost(path, data) {
|
||||||
const r = await fetch(`${API}${_ch(path)}`, {
|
const r = await fetch(`${API}${_ch(path)}`, {
|
||||||
method: 'POST', headers: { 'Content-Type': 'application/json', ..._authHeaders() },
|
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
||||||
body: data != null ? JSON.stringify(data) : undefined,
|
body: data != null ? JSON.stringify(data) : undefined,
|
||||||
});
|
});
|
||||||
if (!r.ok) throw new Error(`${path} => ${r.status}`);
|
if (!r.ok) throw new Error(`${path} => ${r.status}`);
|
||||||
@@ -60,7 +54,7 @@ async function apiPost(path, data) {
|
|||||||
|
|
||||||
async function apiPut(path, data) {
|
async function apiPut(path, data) {
|
||||||
const r = await fetch(`${API}${_ch(path)}`, {
|
const r = await fetch(`${API}${_ch(path)}`, {
|
||||||
method: 'PUT', headers: { 'Content-Type': 'application/json', ..._authHeaders() },
|
method: 'PUT', headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify(data),
|
body: JSON.stringify(data),
|
||||||
});
|
});
|
||||||
if (!r.ok) throw new Error(`${path} => ${r.status}`);
|
if (!r.ok) throw new Error(`${path} => ${r.status}`);
|
||||||
@@ -68,7 +62,7 @@ async function apiPut(path, data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function apiDelete(path) {
|
async function apiDelete(path) {
|
||||||
const r = await fetch(`${API}${_ch(path)}`, { method: 'DELETE', headers: _authHeaders() });
|
const r = await fetch(`${API}${_ch(path)}`, { method: 'DELETE' });
|
||||||
if (!r.ok) throw new Error(`${path} => ${r.status}`);
|
if (!r.ok) throw new Error(`${path} => ${r.status}`);
|
||||||
return r.json();
|
return r.json();
|
||||||
}
|
}
|
||||||
@@ -76,7 +70,7 @@ async function apiDelete(path) {
|
|||||||
async function apiUpload(path, file) {
|
async function apiUpload(path, file) {
|
||||||
const fd = new FormData();
|
const fd = new FormData();
|
||||||
fd.append('file', file);
|
fd.append('file', file);
|
||||||
const r = await fetch(`${API}${_ch(path)}`, { method: 'POST', body: fd, headers: _authHeaders() });
|
const r = await fetch(`${API}${_ch(path)}`, { method: 'POST', body: fd });
|
||||||
if (!r.ok) throw new Error(`${path} => ${r.status}`);
|
if (!r.ok) throw new Error(`${path} => ${r.status}`);
|
||||||
return r.json();
|
return r.json();
|
||||||
}
|
}
|
||||||
@@ -90,7 +84,7 @@ function usePedalState() {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Fetch initial state
|
// Fetch initial state
|
||||||
apiGet('/api/state').then(s => { setState(s); setConnected(true); _authPin = s.auth_pin || ''; }).catch(() => { setConnected(false); });
|
apiGet('/api/state').then(s => { setState(s); setConnected(true); }).catch(() => { setConnected(false); });
|
||||||
|
|
||||||
// WebSocket for real-time updates
|
// WebSocket for real-time updates
|
||||||
let reconnectTimer;
|
let reconnectTimer;
|
||||||
|
|||||||
+3
-62
@@ -444,67 +444,9 @@ class WebServer:
|
|||||||
request.method, request.url.path, exc, exc_info=True)
|
request.method, request.url.path, exc, exc_info=True)
|
||||||
return JSONResponse(status_code=500, content={"detail": "Internal server error"})
|
return JSONResponse(status_code=500, content={"detail": "Internal server error"})
|
||||||
|
|
||||||
# ── Auth middleware ──────────────────────────────────────────────
|
# ── Auth — disabled per user request (single-user stage pedal) ──
|
||||||
# All /api/ endpoints require X-Pedal-Auth: <pin> header, EXCEPT
|
# Auth middleware removed. PIN generation still runs (harmless).
|
||||||
# read-only GET endpoints that the UI needs on first load.
|
# Remove the X-Pedal-Auth wiring from frontend-react/src/App.jsx too.
|
||||||
# The PIN is set at first boot in config.yaml under web.auth_pin.
|
|
||||||
|
|
||||||
AUTH_SAFE_PREFIXES = (
|
|
||||||
# Read-only endpoints the UI needs for basic operation
|
|
||||||
# (none of these expose WiFi/BT credentials or settings)
|
|
||||||
"/api/state",
|
|
||||||
"/api/presets",
|
|
||||||
"/api/snapshots",
|
|
||||||
"/api/models",
|
|
||||||
"/api/irs",
|
|
||||||
"/api/audio/profile",
|
|
||||||
"/api/audio/profiles",
|
|
||||||
"/api/audio/devices",
|
|
||||||
"/api/block-params",
|
|
||||||
"/api/levels",
|
|
||||||
"/api/tuner/pitch",
|
|
||||||
"/api/channel-output",
|
|
||||||
"/api/channel-mode",
|
|
||||||
"/api/routing",
|
|
||||||
"/api/system",
|
|
||||||
"/api/network",
|
|
||||||
"/api/backing-source",
|
|
||||||
"/api/midi/mappings",
|
|
||||||
"/api/nam/engine",
|
|
||||||
"/api/tonedownload/status",
|
|
||||||
"/api/models/tonedownload/search",
|
|
||||||
"/api/irs/tonedownload/search",
|
|
||||||
"/api/fx/params",
|
|
||||||
"/api/bluetooth/midi-status",
|
|
||||||
"/api/bluetooth/midi-devices",
|
|
||||||
)
|
|
||||||
|
|
||||||
@app.middleware("http")
|
|
||||||
async def auth_middleware(request: Request, call_next):
|
|
||||||
path = request.url.path
|
|
||||||
|
|
||||||
# Static files and the main UI are always public
|
|
||||||
if path == "/" or path.startswith("/static/"):
|
|
||||||
return await call_next(request)
|
|
||||||
|
|
||||||
# Legacy redirect routes are public
|
|
||||||
if path in ("/presets", "/models", "/irs", "/settings"):
|
|
||||||
return await call_next(request)
|
|
||||||
|
|
||||||
# Enforce auth on all /api/ endpoints except safe GET prefixes
|
|
||||||
if path.startswith("/api/"):
|
|
||||||
# Safe GET endpoints — read-only, no sensitive data
|
|
||||||
if request.method == "GET" and any(path.startswith(p) for p in AUTH_SAFE_PREFIXES):
|
|
||||||
return await call_next(request)
|
|
||||||
|
|
||||||
pin = self._get_auth_pin()
|
|
||||||
if pin is not None:
|
|
||||||
auth = request.headers.get("X-Pedal-Auth", "")
|
|
||||||
if auth != pin:
|
|
||||||
logger.warning("Unauthorized request to %s %s", request.method, path)
|
|
||||||
return JSONResponse(status_code=401, content={"detail": "Unauthorized"})
|
|
||||||
|
|
||||||
return await call_next(request)
|
|
||||||
|
|
||||||
# Mount static files
|
# Mount static files
|
||||||
app.mount("/static", StaticFiles(directory=str(STATIC_DIR)), name="static")
|
app.mount("/static", StaticFiles(directory=str(STATIC_DIR)), name="static")
|
||||||
@@ -2910,7 +2852,6 @@ iface eth0 inet static
|
|||||||
"bluetooth": self._gather_bt_state(),
|
"bluetooth": self._gather_bt_state(),
|
||||||
"current_snapshot": getattr(pm, '_current_snapshot', 0) if pm else 0,
|
"current_snapshot": getattr(pm, '_current_snapshot', 0) if pm else 0,
|
||||||
"needs_reboot": self._needs_reboot,
|
"needs_reboot": self._needs_reboot,
|
||||||
"auth_pin": self._get_auth_pin() or "",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def _gather_state(self, channel: Optional[str] = None) -> dict[str, Any]:
|
def _gather_state(self, channel: Optional[str] = None) -> dict[str, Any]:
|
||||||
|
|||||||
Reference in New Issue
Block a user