Each page wrote its own nav: the same links in a different order, some missing, the current page sometimes listed and sometimes not. A shared "nav" template now draws it everywhere — Cluster, People, Quotas, Usage, Mirror for an administrator, then Chat, Your keys and Sign out — with the current page marked by aria-current. Pages rendered outside the account pages (the dashboard, node logs) get it through the request context their admin wrapper sets. The admin listener's own page keeps its metrics/pprof/api links. The chat and key pages said "project proj-3kq7…"; they now show the project's path, and the key page no longer repeats the email. A test renders several admin pages and requires the same menu on each. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
31 lines
907 B
Go
31 lines
907 B
Go
package web
|
|
|
|
import (
|
|
"net/http"
|
|
)
|
|
|
|
// AdminPage wraps the dashboard in a session check for the public port.
|
|
//
|
|
// A visitor who is not an admin gets 404 rather than 403. 403 would confirm the
|
|
// page exists and that admin accounts are a thing here, which is free
|
|
// reconnaissance for anyone probing; 404 is what every other unknown path
|
|
// returns.
|
|
func (p *AccountPages) AdminPage(dashboard http.HandlerFunc) http.HandlerFunc {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
noStore(w)
|
|
_, user, ok := p.session(r)
|
|
if !ok {
|
|
// Not signed in at all: send them to sign in, which is useful and
|
|
// reveals nothing — /login is public anyway.
|
|
http.Redirect(w, r, "/login", http.StatusSeeOther)
|
|
return
|
|
}
|
|
if !user.IsAdmin {
|
|
http.NotFound(w, r)
|
|
return
|
|
}
|
|
p.log.Info("admin viewed the cluster", "user", user.ID, "email", user.Email)
|
|
dashboard(w, p.withNav(r, user))
|
|
}
|
|
}
|