feat(frontend): auto-enter guest mode with inline admin login - #7
Merged
Conversation
Remove the full-screen login page gate so the dashboard loads immediately in guest (read-only) mode. Add a 'Login as Admin' button in the header that opens a compact login modal to upgrade to ROLE_ADMIN. Logout now returns to guest mode instead of a blank login screen. No backend changes required — the existing /api/auth/guest endpoint is already permitAll and stateless.
The auto-guest feature could lock every visitor out of the dashboard, including administrators. - A failed api.guestLogin() only logged to the console, leaving token empty, so the app rendered a bare spinner forever. The "Login as Admin" button lives in the header, which is not rendered without a token, so there was no way back in. - handleLogout() is called from eight fetch error handlers on 401, and each fired its own api.guestLogin(). The dashboard runs five fetches in parallel on load and again every 30s. - /api/auth/guest is rate limited to 5 requests per minute per IP, so the above trips the limit, which fails acquisition, which produces the permanent spinner. Routes every guest acquisition through one guarded entry point: concurrent callers share the in-flight request (which also covers React StrictMode's double invoke), and a five second cooldown stops a rejected token driving an acquire/reject loop. Failure now renders an explanatory card with Retry and Sign in as admin, and the login modal renders in that branch so admin access survives a guest-service outage. Also removes components/auth/Login.jsx, which AdminLoginModal replaced and nothing imports, makes the Vite dev proxy target configurable via API_PROXY_TARGET, and gives ESLint Node globals for config files.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Rebased onto current
mainand fixed before shipping. Verified locally against the production API.The feature
Visitors land on the dashboard directly as a guest instead of hitting a login wall. Admin sign-in moves into a modal behind a "Login as Admin" button in the header; logging out returns to guest rather than the login screen.
What needed fixing first
The original commit could lock every visitor out of the dashboard — administrators included.
Unrecoverable spinner. A failed
api.guestLogin()only didconsole.error, leavingtokenempty, so the app rendered a bare spinner indefinitely. The "Login as Admin" button lives in the header, which isn't rendered without a token — so there was no way back in at all.Logout storm.
handleLogout()is called from eight fetch error handlers on 401, and each fired its ownapi.guestLogin(). The dashboard runs five fetches in parallel on load and again every 30 seconds.Rate limit collision.
/api/auth/guestis capped at 5 requests/minute per IP byRateLimitFilter. The storm trips the limit, which fails acquisition, which produces the permanent spinner. The three bugs compound into a self-inflicted lockout.The fix
All guest acquisition goes through one guarded entry point:
Also in here
components/auth/Login.jsx— superseded byAdminLoginModal, nothing imports it.API_PROXY_TARGET, and presents an allowedOriginso the dev server can point at a deployed environment. The orchestrator's CORS allowlist doesn't include the Vite dev origin, which returns 403 →UNAUTHORIZEDin the browser but passes forcurl(noOriginheader).vite.config.jswas failingno-undefonprocess.Verified
Exercised in a dev server proxied to production: guest auto-entry, admin login via modal, logout back to guest, and the failure card.
Worth a deliberate decision
The dashboard is now readable with no login at all — health, deployments, Docker container names and images. That's the intent of the feature, but it is a real change in exposure. Guests still can't act (C-1) and logs remain admin-only, so I think it's defensible for a portfolio piece — flagging it as a choice rather than a side effect.
Separately: every first-time visitor now consumes one of the 5/min guest tokens for their IP. Fine for normal traffic, but that limit is what will bite if the dashboard is ever linked somewhere busy.