Files
llmgw/internal/web/nav.go
T
2026-10-04 16:30:56 +02:00

78 lines
2.4 KiB
Go

package web
import (
"context"
"net/http"
"gitea.mynodes.xyz/ilker/llmgw/internal/store"
)
// Nav is the menu at the top of every signed-in page.
//
// One menu, drawn by one template, because each page used to write its own:
// the same links in a different order, some missing, and the page you were on
// sometimes listed and sometimes not. Moving between pages then read as moving
// between different programs.
type Nav struct {
// Current is the page being shown, so its link is marked rather than
// offered: "cluster", "people", "quotas", "usage", "mirror", "chat", "keys".
Current string
// Admin shows the administration links, and Role which of them: a
// billing role sees usage and quotas only.
Admin bool
Role string
// Mirror and Quotas are whether those pages were mounted at all; usage is
// mounted with quotas.
Mirror, Quotas bool
// Guard is whether the guardrails page was mounted.
Guard bool
// CSRF signs the sign-out form.
CSRF string
// Org offers the organization page: to an administrator, and to anyone
// who manages an organization.
Org bool
}
type navKey struct{}
// navFor builds the menu for a signed-in person.
func (p *AccountPages) navFor(r *http.Request, user store.User, current string) Nav {
n := Nav{
Current: current,
Admin: user.Staff(),
Role: user.StaffRole(),
Mirror: p.hasMirror,
Quotas: p.hasQuotas,
Guard: p.hasGuard,
CSRF: csrfFor(r),
}
// Only when there is an organization to show: a link that leads to "there
// are none" on every page is noise.
if p.orgs != nil {
var orgs []store.Org
var err error
if user.IsAdmin {
orgs, err = p.orgs.AllOrgs(r.Context())
} else {
orgs, err = p.orgs.ManagedOrgs(r.Context(), user.ID)
}
n.Org = err == nil && len(orgs) > 0
}
return n
}
// withNav carries the menu to a page rendered by a handler that has no
// session of its own — the dashboard and the node logs are built without the
// account pages and wrapped by them.
func (p *AccountPages) withNav(r *http.Request, user store.User) *http.Request {
return r.WithContext(context.WithValue(r.Context(), navKey{}, p.navFor(r, user, "")))
}
// navFrom reads the menu withNav stored, marked for the page being drawn.
// Zero when there is none, which draws no admin links.
func navFrom(r *http.Request, current string) Nav {
n, _ := r.Context().Value(navKey{}).(Nav)
n.Current = current
return n
}