package web import ( "net/http" "net/url" "sort" "strings" "git.sr.ht/~sircmpwn/core-go/config" "github.com/vaughan0/go-ini" "go.bigb.es/sourcehut-compare/authz" ) // navCanonical is the SourceHut service-switcher order. Services not listed here // (including our own compare) sort alphabetically after these. var navCanonical = []string{"hub", "git", "hg", "lists", "todo", "builds", "man", "meta"} // navExcluded are service sections that never appear in the switcher: paste and // pages have no top-level UI worth linking, and hub is rendered as the brand. var navExcluded = map[string]bool{"paste": true, "pages": true, "hub": true} // navItem is one entry in the service switcher. type navItem struct { Name string // short service name, e.g. "git" Origin string // external origin URL Active bool // true for compare.sr.ht (this service) } // buildNav derives the service switcher from the shared config: every section // whose name ends in ".sr.ht" (with a configured origin) except paste/pages/hub, // ordered by navCanonical then alphabetically, with compare.sr.ht marked active. func buildNav(conf ini.File) []navItem { var items []navItem for section := range conf { if !strings.HasSuffix(section, ".sr.ht") { continue } short := strings.TrimSuffix(section, ".sr.ht") if navExcluded[short] { continue } origin := config.GetOrigin(conf, section, true) if origin == "" { continue } items = append(items, navItem{ Name: short, Origin: origin, Active: section == "compare.sr.ht", }) } sort.SliceStable(items, func(i, j int) bool { ci, cj := canonIndex(items[i].Name), canonIndex(items[j].Name) if ci != cj { return ci < cj } return items[i].Name < items[j].Name }) return items } // canonIndex returns a service's position in navCanonical, or a sentinel past // the end for services that are not canonically ordered. func canonIndex(name string) int { for i, n := range navCanonical { if n == name { return i } } return len(navCanonical) } // viewData is the root value every template is executed against: the chrome // fields are common to all pages; Data carries the page-specific payload. type viewData struct { Title string SiteName string HubOrigin string // non-empty ⇒ brand links to hub instead of "/" Nav []navItem Username string // "" for an anonymous viewer LoginURL string LogoutURL string RegisterURL string ProfileURL string CSSHref string Environment string ShowBanner bool Data any } // chrome builds the common chrome fields for a request. Login return_to is the // current full URL (so the viewer lands back where they were); logout return_to // is this service's origin. func (s *Server) chrome(r *http.Request) viewData { username := authz.ForContext(r.Context()) current := s.compareOrigin + r.URL.RequestURI() loginURL := s.metaOrigin + "/login?return_to=" + url.QueryEscape(current) logoutURL := s.metaOrigin + "/logout?return_to=" + url.QueryEscape(s.compareOrigin) profileURL := s.metaOrigin + "/profile" if s.hubOrigin != "" && username != "" { profileURL = s.hubOrigin + "/~" + username } return viewData{ SiteName: s.siteName, HubOrigin: s.hubOrigin, Nav: s.nav, Username: username, LoginURL: loginURL, LogoutURL: logoutURL, RegisterURL: s.metaOrigin, ProfileURL: profileURL, CSSHref: s.cssHref, Environment: strings.ToUpper(s.environment), ShowBanner: s.environment != "" && s.environment != "production", } }