diff --git a/core/src/main/kotlin/me/eternal/purrfectsnap/core/features/impl/global/CompanionServer.kt b/core/src/main/kotlin/me/eternal/purrfectsnap/core/features/impl/global/CompanionServer.kt index 6bc00256..f8ebd906 100644 --- a/core/src/main/kotlin/me/eternal/purrfectsnap/core/features/impl/global/CompanionServer.kt +++ b/core/src/main/kotlin/me/eternal/purrfectsnap/core/features/impl/global/CompanionServer.kt @@ -170,7 +170,13 @@ class CompanionServer : Feature("CompanionServer") { } val query = parseQuery(rawQuery) - // Constant-time auth check + // Serve the dashboard without auth so the user can see the login form + if (rawPath == "/" || rawPath == "/index.html") { + serveDashboard(socket) + return + } + + // Constant-time auth check for all other endpoints val providedToken = headers["authorization"]?.removePrefix("Bearer ")?.trim() ?: query["token"] ?: "" @@ -180,11 +186,11 @@ class CompanionServer : Feature("CompanionServer") { } when (rawPath) { - "/events" -> handleSSE(socket) - "/logs" -> serveJson(socket, synchronized(bufferLock) { recentLogs.toList() }) - "/network" -> serveJson(socket, synchronized(bufferLock) { recentNetworkCalls.toList() }) - "/", "/index.html" -> serveDashboard(socket) - else -> respond404(socket) + "/events" -> handleSSE(socket) + "/logs" -> serveJson(socket, synchronized(bufferLock) { recentLogs.toList() }) + "/network" -> serveJson(socket, synchronized(bufferLock) { recentNetworkCalls.toList() }) + "/ping" -> serveJson(socket, mapOf("ok" to true)) + else -> respond404(socket) } } } @@ -384,7 +390,7 @@ function q(id){return document.getElementById(id)} function connect(){ tok=q('tok').value.trim(); if(!tok)return; - fetch('/?token='+encodeURIComponent(tok)) + fetch('/ping?token='+encodeURIComponent(tok)) .then(function(r){ if(r.status===401){q('login-err').style.display='block';return;} q('login').style.display='none';