~bigbes/sr-ht-dolt

ref: ba34443357cb2471337ae8faa7dee369bdbcae69 sr-ht-dolt/web/chrome.go -rw-r--r-- 4.4 KiB
ba344433 — Eugene Blikh feat(remoteapi): auto-create databases on first push to own namespace 30 days ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
package web

import (
	"net/http"
	"net/url"
	"sort"
	"strings"

	"sourcecraft.dev/bigbes/sr-ht-core/auth"
	"sourcecraft.dev/bigbes/sr-ht-core/config"
	"github.com/vaughan0/go-ini"

	"sourcecraft.dev/bigbes/sr-ht-dolt/authn"
)

// serviceName is our own service key in the shared config and nav.
const serviceName = "dolt.sr.ht"

// networkOrder is upstream core.sr.ht's fixed nav ordering (flask.py
// _network_order). Services present in the config but not listed here sort
// after these, alphabetically. paste.sr.ht and pages.sr.ht are excluded from
// the network entirely (they have no user-facing nav), mirroring _network.
var networkOrder = []string{
	"hub.sr.ht",
	"git.sr.ht",
	"hg.sr.ht",
	"lists.sr.ht",
	"todo.sr.ht",
	"builds.sr.ht",
	"man.sr.ht",
	"meta.sr.ht",
}

var networkExcluded = map[string]bool{
	"paste.sr.ht": true,
	"pages.sr.ht": true,
}

// navEntry is one service link in the shared nav.
type navEntry struct {
	// Name is the leading segment of the service (e.g. "git" for git.sr.ht),
	// rendered as the link text exactly as upstream nav.html does.
	Name string
	// Site is the full service key (e.g. "git.sr.ht").
	Site string
	// Origin is the external URL for the service.
	Origin string
	// Active is true for our own service, which highlights it in the nav.
	Active bool
}

// basePage is the chrome model shared by every rendered page. Handler view
// structs embed it so templates reference its fields directly (e.g. .SiteName).
type basePage struct {
	Title         string
	Site          string
	SiteLabel     string
	SiteName      string
	Environment   string
	ShowEnvBanner bool
	Network       []navEntry
	MetaOrigin    string
	SelfOrigin    string
	LoginURL      string
	LogoutURL     string
	StyleHref     string
	// CurrentUser is the authenticated caller, or nil for an anonymous request.
	CurrentUser *auth.AuthContext
}

// buildNetwork returns the ordered nav entries for conf: every section ending
// ".sr.ht" that is not excluded, ordered by networkOrder then alphabetically
// for unknown services. Origins are resolved externally via config.GetOrigin.
func buildNetwork(conf ini.File) []navEntry {
	var sites []string
	for section := range conf {
		if !strings.HasSuffix(section, ".sr.ht") || networkExcluded[section] {
			continue
		}
		sites = append(sites, section)
	}

	orderIndex := func(s string) int {
		for i, n := range networkOrder {
			if n == s {
				return i
			}
		}
		return len(networkOrder) // unknown services sort after the fixed set
	}
	sort.Slice(sites, func(i, j int) bool {
		oi, oj := orderIndex(sites[i]), orderIndex(sites[j])
		if oi != oj {
			return oi < oj
		}
		return sites[i] < sites[j] // stable, alphabetical among unknowns
	})

	entries := make([]navEntry, 0, len(sites))
	for _, s := range sites {
		entries = append(entries, navEntry{
			Name:   strings.SplitN(s, ".", 2)[0],
			Site:   s,
			Origin: config.GetOrigin(conf, s, true),
			Active: s == serviceName,
		})
	}
	return entries
}

// newBasePage builds the chrome model for a request. Title is the per-page
// <title>; the caller sets any page-specific fields on its own view struct.
func (a *app) newBasePage(r *http.Request, title string) basePage {
	conf := a.cfg.Conf
	env := config.GetString(conf, "sr.ht", "environment", "development")
	self := config.GetOrigin(conf, serviceName, true)
	meta := config.GetOrigin(conf, "meta.sr.ht", true)

	return basePage{
		Title:         title,
		Site:          serviceName,
		SiteLabel:     strings.SplitN(serviceName, ".", 2)[0],
		SiteName:      config.GetString(conf, "sr.ht", "site-name", "sr.ht"),
		Environment:   env,
		ShowEnvBanner: env != "production",
		Network:       buildNetwork(conf),
		MetaOrigin:    meta,
		SelfOrigin:    self,
		LoginURL:      loginURL(self, meta, r),
		LogoutURL:     logoutURL(self, meta),
		StyleHref:     a.styleHref,
		CurrentUser:   authn.CallerFromContext(r.Context()),
	}
}

// loginURL mirrors core.sr.ht flask.py: {meta}/login?return_to={self+full_path}.
// full_path is the request path with its query string, so login round-trips the
// user back to exactly where they were.
func loginURL(self, meta string, r *http.Request) string {
	returnTo := self + r.URL.EscapedPath()
	if r.URL.RawQuery != "" {
		returnTo += "?" + r.URL.RawQuery
	}
	return meta + "/login?return_to=" + url.QueryEscape(returnTo)
}

// logoutURL mirrors core.sr.ht flask.py: {meta}/logout?return_to={self origin}.
func logoutURL(self, meta string) string {
	return meta + "/logout?return_to=" + url.QueryEscape(self)
}