tinyrelaycommit

Commit 7fccd10734ba

browse files at this commit | history

1diff --git a/README.md b/README.md
2index 7b3b2ee..7a1e6b7 100644
3--- a/README.md
4+++ b/README.md
5@@ -51,7 +51,7 @@ See [Git collaboration](docs/git-collaboration.md) for issues, pull requests, co
6
7 See [Installed app](docs/app.md) for installing the relay as an app, sharing files to it, opening Nostr links and device notifications.
8
9-The `/tools` page reports whether the browser supports native WebMCP controls. See [WebMCP tools](docs/webmcp.md) for browser-agent integration and authorization behavior.
10+**Manage > Health** shows the relay and script versions and whether the browser's WebMCP tools are registered. See [WebMCP tools](docs/webmcp.md) for browser-agent integration and authorization behavior.
11
12 ## Deployment
13
14diff --git a/docs/webmcp.md b/docs/webmcp.md
15index f20b3f9..5b5fe0d 100644
16--- a/docs/webmcp.md
17+++ b/docs/webmcp.md
18@@ -1,9 +1,9 @@
19 # Browser tools
20
21-Open **Manage > Tools** to see the tools available to your browser agent. The agent can browse repositories, files, issues and pull requests, inspect service status, manage background jobs, create backups, update relay settings, open Nostr links and the Files page, read this device's notification state and, as the owner, send a test notification. Turning notifications on stays a manual step, since the browser asks the person for permission.
22+Every page registers tools for your browser agent; **Manage > Health** reports whether they are ready, and Chrome's Application panel lists them with their calls. The agent can browse repositories, files, issues and pull requests, inspect service status, manage background jobs, create backups, update relay settings, open Nostr links and the Files page, read this device's notification state and, as the owner, send a test notification. Turning notifications on stays a manual step, since the browser asks the person for permission.
23
24 Choose **Sign in** in the main navigation and connect your Nostr signer. Reading protected information uses your browser session. Management actions request a signature and follow your account permissions. A queued backup or job has not finished until its status says so.
25
26 In Chrome, enable **WebMCP for testing** at `chrome://flags/#enable-webmcp-testing` and relaunch. Other browsers can use the same pages and forms. See [Chrome's WebMCP guide](https://developer.chrome.com/docs/ai/webmcp) for availability.
27
28-The Tools page lists registered tools and the most recent operation. Read the current policy or connection list before changing settings. Job intervals are measured in hours; zero runs once.
29+Read the current policy or connection list before changing settings. Job intervals are measured in hours; zero runs once.
30diff --git a/internal/daemon/services.go b/internal/daemon/services.go
31index dd44e54..c47c10f 100644
32--- a/internal/daemon/services.go
33+++ b/internal/daemon/services.go
34@@ -123,7 +123,7 @@ func (t *Tenant) initServices(ctx context.Context) error {
35 if err != nil {
36 return err
37 }
38- t.ui, err = webui.New(backend{tenant: t}, webui.Options{Actor: t.resolveUIActor})
39+ t.ui, err = webui.New(backend{tenant: t}, webui.Options{Actor: t.resolveUIActor, Version: t.app.cfg.Version, Revision: t.app.cfg.Revision})
40 if err != nil {
41 return err
42 }
43diff --git a/internal/webui/browse_test.go b/internal/webui/browse_test.go
44index 379e64a..f45446e 100644
45--- a/internal/webui/browse_test.go
46+++ b/internal/webui/browse_test.go
47@@ -49,7 +49,7 @@ func TestBrowseRoutesUseObjectContractsAndRenderData(t *testing.T) {
48 {"/repo?owner=alice&repo=notes&view=file", "browserepo", "one"},
49 {"/files", "browsefiles", "deadbeef"},
50 {"/file?hash=deadbeef", "browsefile", "blob"},
51- {"/manage/status", "browsestatus", "manage this relay"},
52+ {"/manage/health", "browsestatus", "manage this relay"},
53 }
54 for _, test := range tests {
55 recorder := httptest.NewRecorder()
56diff --git a/internal/webui/manage.html b/internal/webui/manage.html
57index d9ddef3..80ba1d8 100644
58--- a/internal/webui/manage.html
59+++ b/internal/webui/manage.html
60@@ -193,20 +193,35 @@
61
62 {{define "health"}}<section>
63 <h1>Health</h1>
64+<table id="facts">
65+<tr><th>relay</th><td><code>{{or .Version "dev"}}</code>{{with .Revision}} <small>{{.}}</small>{{end}}</td></tr>
66+<tr><th>scripts</th><td><code>{{scriptsVersion}}</code></td></tr>
67+<tr><th>browser tools</th><td><span id="webmcp-status" role="status">Needs JavaScript to report.</span></td></tr>
68+</table>
69+<details><summary>About browser tools</summary><p>Browser agents use WebMCP tools registered on every page; Chrome's Application panel lists them and their calls. In Chrome, open <code>chrome://flags/#enable-webmcp-testing</code>, enable WebMCP for testing and relaunch. Your usual pages and signed forms work in other browsers.</p></details>
70+<script>(() => {
71+ const update = () => {
72+ const state = window.tinyWebMCP;
73+ const node = document.getElementById("webmcp-status");
74+ if (!node) return;
75+ node.textContent = !state?.supported ? "WebMCP is unavailable in this browser."
76+ : state.errors.length ? "Some tools could not register: " + state.errors.join("; ")
77+ : "ready, " + state.registered.length + " tools registered" + (state.lastOperation ? ", last: " + state.lastOperation : "");
78+ };
79+ window.addEventListener("tiny:webmcp", update);
80+ update();
81+ window.tinyWebMCP?.ready.then(update);
82+})();</script>
83+<h3>Service</h3>
84+{{if .Error}}{{template "alert" .Error}}{{else}}{{template "jsonView" .Event}}{{end}}
85+<p><a href="/manage/sync">Jobs and delivery</a> | <a href="/manage/data">Storage and backups</a></p>
86+<h3>Reports</h3>
87 <rpc-form method="stats"><button>Relay stats</button></rpc-form>
88 <rpc-form method="listaudit"><label>Before sequence <input name="param" type="number" value="0"></label><button>Audit log</button></rpc-form>
89 <rpc-form method="supportedmethods"><button>Supported methods</button></rpc-form>
90 </section>
91 {{end}}
92
93-{{define "status"}}<section>
94-<h1>Service status</h1>
95-{{if .Error}}{{template "alert" .Error}}
96-{{else}}{{template "jsonView" .Event}}
97-<p><a href="/manage/sync">Jobs and delivery</a> | <a href="/manage/data">Storage and backups</a> | <a href="/manage/health">Relay health</a></p>
98-{{end}}</section>
99-{{end}}
100-
101 {{define "owner"}}<section>
102 <h1>Owner operations</h1>
103 <p>Destructive actions ask the signer. The browser never receives or submits a private key.</p>
104diff --git a/internal/webui/page.html b/internal/webui/page.html
105index 4788e82..6945843 100644
106--- a/internal/webui/page.html
107+++ b/internal/webui/page.html
108@@ -47,13 +47,11 @@
109 {{else if eq .Tab "sync"}}{{template "sync" .}}
110 {{else if eq .Tab "views"}}{{template "views" .}}
111 {{else if eq .Tab "health"}}{{template "health" .}}
112-{{else if eq .Tab "status"}}{{template "status" .}}
113 {{else if eq .Tab "owner"}}{{template "owner" .}}
114 {{else if eq .Tab "repos"}}{{template "repos" .}}
115 {{else if eq .Tab "repo"}}{{template "repo" .}}
116 {{else if eq .Tab "files"}}{{template "files" .}}
117 {{else if eq .Tab "file"}}{{template "file" .}}
118-{{else if eq .Tab "tools"}}{{template "tools" .}}
119 {{else if eq .Tab "event"}}{{template "event" .}}
120 {{else if eq .Tab "view"}}{{template "view" .}}
121 {{else if eq .Tab "invite"}}{{template "invite" .}}
122@@ -81,13 +79,11 @@
123 <p>Private relay. Sign in with a Nostr signer to continue.</p>
124 {{else if eq $kind "repo"}}<header><span><a href="/">{{.Slug}}</a>/<a href="/repos" title="{{.Query.Get "owner"}}">{{shortID (npub (.Query.Get "owner"))}}</a>/<b>{{.Query.Get "repo"}}</b></span></header>
125 <nav>{{$v := repoView .Query}}<a href="{{repoURL .Query "home" ""}}"{{if eq $v "home"}} aria-current="page"{{end}}>/home</a><a href="{{repoURL .Query "tree" ""}}"{{if or (eq $v "tree") (eq $v "file")}} aria-current="page"{{end}}>/code</a><a href="{{repoURL .Query "history" ""}}"{{if or (eq $v "history") (eq $v "commit")}} aria-current="page"{{end}}>/history</a><a href="{{repoURL .Query "issues" ""}}"{{if or (eq $v "issues") (eq $v "issue")}} aria-current="page"{{end}}>/issues</a><a href="{{repoURL .Query "prs" ""}}"{{if or (eq $v "prs") (eq $v "pr")}} aria-current="page"{{end}}>/prs</a><a href="{{repoURL .Query "activity" ""}}"{{if eq $v "activity"}} aria-current="page"{{end}}>/activity</a></nav>
126-<p>&larr; <a href="/repos">all repositories</a></p>
127 {{else if eq $kind "manage"}}<header><span><a href="/">{{.Slug}}</a>/<b>manage</b></span></header>
128-<nav>{{range $item := manageItems}}<a href="{{$item.Href}}"{{if eq $item.Tab $.Tab}} aria-current="page"{{end}}>{{$item.Label}}</a>{{end}}</nav>
129-<p>&larr; <a href="/">relay</a></p>
130+{{range $group := navGroups manageItems}}<nav>{{range $item := $group}}<a href="{{$item.Href}}"{{if eq $item.Tab $.Tab}} aria-current="page"{{end}}>{{$item.Label}}</a>{{end}}</nav>{{end}}
131 {{else}}<header><span><a id="mark" href="/" title="{{.Slug}}"><i>t</i><i>i</i><i>n</i><i>y</i></a></span></header>
132-<nav>{{range $item := relayItems}}<a href="{{$item.Href}}"{{if eq $item.Tab $.Tab}} aria-current="page"{{end}}>{{$item.Label}}</a>{{end}}</nav>
133-{{end}}<p id="rail-tools"><button id="theme" type="button" aria-label="Switch theme">theme</button></p>{{end}}
134+{{range $group := navGroups relayItems}}<nav>{{range $item := $group}}<a href="{{$item.Href}}"{{if eq $item.Tab $.Tab}} aria-current="page"{{end}}>{{$item.Label}}</a>{{end}}</nav>{{end}}
135+{{end}}<p id="rail-tools">{{if eq $kind "repo"}}<a href="/repos">&larr; all repositories</a>{{else if eq $kind "manage"}}<a href="/">&larr; relay</a>{{end}}<button id="theme" type="button" aria-label="Switch theme">theme</button></p>{{end}}
136
137 {{/* The panel holds facts and filters for the current page. */}}
138 {{define "panel"}}{{$kind := railKind .Tab}}{{if .Private}}{{template "panel-signin" .}}
139diff --git a/internal/webui/public.html b/internal/webui/public.html
140index 225ae4a..9809789 100644
141--- a/internal/webui/public.html
142+++ b/internal/webui/public.html
143@@ -131,36 +131,6 @@
144 </section>
145 {{end}}
146
147-{{define "tools"}}<section>
148-<h1>Browser tools</h1>
149-<p>Use a browser agent to browse repositories and files, check relay health, manage jobs and create backups. Connect your signer to manage the relay.</p>
150-<p id="webmcp-status" role="status">Checking browser support...</p>
151-<ul id="webmcp-tools"></ul>
152-<p id="webmcp-operation" role="status"></p>
153-<details>
154-<summary>Enable WebMCP</summary>
155-<p>In Chrome, open <code>chrome://flags/#enable-webmcp-testing</code>, enable WebMCP for testing and relaunch. Your usual pages and signed forms work in other browsers.</p>
156-</details>
157-<script>(() => {
158- const update = () => {
159- const state = window.tinyWebMCP;
160- document.getElementById("webmcp-status").textContent = !state?.supported
161- ? "WebMCP is unavailable in this browser."
162- : state.errors.length ? "Some tools could not register: " + state.errors.join("; ") : state.registered.length + " tools available.";
163- document.getElementById("webmcp-tools").replaceChildren(...(state?.registered || []).map(name => {
164- const item = document.createElement("li");
165- item.textContent = name;
166- return item;
167- }));
168- document.getElementById("webmcp-operation").textContent = state?.lastOperation || "";
169- };
170- window.addEventListener("tiny:webmcp", update);
171- update();
172- window.tinyWebMCP?.ready.then(update);
173-})();</script>
174-</section>
175-{{end}}
176-
177 {{define "private"}}<section>
178 <h1>Private messages</h1>
179 <p>Sign in with a Nostr client to view addressed private events.</p>
180diff --git a/internal/webui/shell_test.go b/internal/webui/shell_test.go
181index eaf2116..f2ce18a 100644
182--- a/internal/webui/shell_test.go
183+++ b/internal/webui/shell_test.go
184@@ -299,7 +299,7 @@ func TestGuestsSeeSignInInsteadOfManagementPages(t *testing.T) {
185 if err != nil {
186 t.Fatal(err)
187 }
188- for _, path := range []string{"/manage/people", "/manage/owner", "/manage/status", "/manage/data"} {
189+ for _, path := range []string{"/manage/people", "/manage/owner", "/manage/health", "/manage/data"} {
190 recorder := httptest.NewRecorder()
191 app.ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, path, nil))
192 body := recorder.Body.String()
193diff --git a/internal/webui/style.css b/internal/webui/style.css
194index 45a481b..f6ef063 100644
195--- a/internal/webui/style.css
196+++ b/internal/webui/style.css
197@@ -37,11 +37,12 @@ img{max-width:100%}
198 #who{color:var(--muted)}#who b,#who nostr-key{color:var(--ink)}
199 #who button{border:0;background:none;padding:0;color:var(--accent);font:inherit}#who button:hover{text-decoration:underline}
200 #railbox{box-sizing:border-box;padding-bottom:44px}
201-#rail-tools{position:absolute;right:0;bottom:0;margin:0;padding:10px 2ch 10px 2.5ch}
202+#rail nav+nav{margin-top:14px}
203+#rail-tools{position:absolute;left:0;right:0;bottom:0;margin:0;padding:10px 2ch 10px 2.5ch;font:12px var(--mono)}#rail-tools a{float:left;color:var(--muted)}#rail-tools a:hover{color:var(--ink)}#rail-tools button{float:right}
204 /* The install hint sits on the footer's centre line so it never moves the prompt or the session. */
205 #install{position:absolute;left:50%;top:0;transform:translateX(-50%);border:0;background:none;padding:0;color:var(--muted);font:12px var(--mono);line-height:27px;cursor:pointer}#install:hover{color:var(--ink);text-decoration:underline}#install[hidden]{display:none}
206 share-link{display:inline-block}share-link:empty{display:none}
207-#theme{border:0;background:none;padding:0;color:var(--muted);font:12px var(--mono)}#theme:hover{color:var(--ink);text-decoration:underline}
208+#theme{border:0;background:none;padding:0;color:var(--muted);font:inherit}#theme:hover{color:var(--ink);text-decoration:underline}
209
210 /* Rail */
211 #rail header{height:60px;box-sizing:border-box;border-bottom:1px solid var(--line);font:13px var(--mono);display:table;width:100%}
212diff --git a/internal/webui/templates.go b/internal/webui/templates.go
213index e07e6cb..fd6dee0 100644
214--- a/internal/webui/templates.go
215+++ b/internal/webui/templates.go
216@@ -93,11 +93,32 @@ var iconSVG []byte
217 var iconMonoSVG []byte
218
219 // navItem is one rail entry. Tab matches PageData.Tab for the active state.
220-type navItem struct{ Label, Href, Tab string }
221+// navItem is one rail entry. Group orders entries by what they are for; the
222+// rail leaves a small gap between groups.
223+type navItem struct {
224+ Label, Href, Tab string
225+ Group int
226+}
227+
228+// relayNav: entry points, then conversation, then collaborative artifacts,
229+// then what the relay publishes and stores, then what is yours.
230+var relayNav = []navItem{{"/home", "/", "home", 0}, {"/search", "/search", "search", 0}, {"/inbox", "/inbox", "inbox", 1}, {"/repos", "/repos", "repos", 2}, {"/files", "/files", "files", 3}, {"/articles", "/articles", "articles", 3}, {"/sites", "/sites", "sites", 3}, {"/outbox", "/outbox", "outbox", 4}, {"/manage", "/manage/people", "manage", 4}}
231
232-var relayNav = []navItem{{"/home", "/", "home"}, {"/search", "/search", "search"}, {"/repos", "/repos", "repos"}, {"/files", "/files", "files"}, {"/sites", "/sites", "sites"}, {"/articles", "/articles", "articles"}, {"/inbox", "/inbox", "inbox"}, {"/outbox", "/outbox", "outbox"}, {"/manage", "/manage/people", "manage"}}
233+// manageNav: who is here, what they may do, what the relay is, what it does
234+// over time, and how it is doing.
235+var manageNav = []navItem{{"/people", "/manage/people", "people", 0}, {"/moderation", "/manage/moderation", "moderation", 1}, {"/rules", "/manage/rules", "rules", 1}, {"/identity", "/manage/identity", "identity", 2}, {"/connect", "/manage/connect", "connect", 2}, {"/owner", "/manage/owner", "owner", 2}, {"/sync", "/manage/sync", "sync", 3}, {"/data", "/manage/data", "data", 3}, {"/views", "/manage/views", "views", 3}, {"/health", "/manage/health", "health", 4}}
236
237-var manageNav = []navItem{{"/people", "/manage/people", "people"}, {"/moderation", "/manage/moderation", "moderation"}, {"/rules", "/manage/rules", "rules"}, {"/identity", "/manage/identity", "identity"}, {"/connect", "/manage/connect", "connect"}, {"/data", "/manage/data", "data"}, {"/sync", "/manage/sync", "sync"}, {"/views", "/manage/views", "views"}, {"/health", "/manage/health", "health"}, {"/owner", "/manage/owner", "owner"}, {"/status", "/manage/status", "status"}, {"/tools", "/tools", "tools"}}
238+// navGroups splits a rail list into its groups, in order.
239+func navGroups(items []navItem) [][]navItem {
240+ var groups [][]navItem
241+ for _, item := range items {
242+ if len(groups) == 0 || groups[len(groups)-1][0].Group != item.Group {
243+ groups = append(groups, nil)
244+ }
245+ groups[len(groups)-1] = append(groups[len(groups)-1], item)
246+ }
247+ return groups
248+}
249
250 // railKind picks the sitemap for a tab: one repository, management, or the relay.
251 func railKind(tab string) string {
252@@ -207,6 +228,7 @@ func parseTemplates() (*template.Template, error) {
253 "repoView": repoView,
254 "relayItems": func() []navItem { return relayNav },
255 "manageItems": func() []navItem { return manageNav },
256+ "navGroups": navGroups,
257 "add": func(a, b int) int { return a + b },
258 "strs": func(values ...string) []string { return values },
259 "repoCommitURL": repoCommitURL,
260diff --git a/internal/webui/webmcp.js b/internal/webui/webmcp.js
261index e524569..1f3da64 100644
262--- a/internal/webui/webmcp.js
263+++ b/internal/webui/webmcp.js
264@@ -128,7 +128,7 @@
265 object({...repository, id: hash, view: {type: "string", enum: ["tree", "file", "history", "commit", "activity", "issues", "prs", "issue", "pr"]}}, ["owner", "repo"]), {}, input => open(repoURL(input)));
266 register("tiny.open_file", "Open a stored file by its SHA-256 hash in this tab. For repository source, use tiny.open_repository with view=file.",
267 object({hash}, ["hash"]), {}, input => open("/file?hash=" + encodeURIComponent(input.hash)));
268- register("tiny.open_status", "Open the relay status page in this tab.", object(), {}, () => open("/manage/status"));
269+ register("tiny.open_status", "Open the relay health page in this tab: build versions, service status and browser tool readiness.", object(), {}, () => open("/manage/health"));
270 register("tiny.open_files", "Open the Files page in this tab, where files and folders upload and shared items wait.", object(), {}, () => open("/files"));
271 register("tiny.open_link", "Open a nostr link in this tab: an npub, nprofile, note, nevent, naddr or 64-character event id, with or without a nostr: or web+nostr: prefix.",
272 object({target: {type: "string", minLength: 1, maxLength: 512}}, ["target"]), {}, input => open("/open?target=" + encodeURIComponent(input.target)));
273diff --git a/internal/webui/webmcp_test.go b/internal/webui/webmcp_test.go
274index ad2c5f1..5220f10 100644
275--- a/internal/webui/webmcp_test.go
276+++ b/internal/webui/webmcp_test.go
277@@ -3,6 +3,7 @@ package webui
278 import (
279 "context"
280 "encoding/json"
281+ "github.com/FelineStateMachine/tinyrelay/internal/policy"
282 "net/http"
283 "net/http/httptest"
284 "net/url"
285@@ -98,14 +99,25 @@ func TestPagesAdvertiseWebMCPScript(t *testing.T) {
286 }
287 }
288
289-func TestToolsPageExplainsNativeFallback(t *testing.T) {
290- app, err := New(&fakeBackend{}, Options{})
291+func TestHealthPageReportsVersionsAndBrowserTools(t *testing.T) {
292+ backend := &fakeBackend{policy: policy.Defaults(strings.Repeat("a", 64))}
293+ app, err := New(backend, Options{Actor: func(*http.Request) (string, error) { return backend.policy.Owner, nil }, Version: "1.4.0", Revision: "abc1234"})
294 if err != nil {
295 t.Fatal(err)
296 }
297 recorder := httptest.NewRecorder()
298- app.ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/tools", nil))
299- if recorder.Code != http.StatusOK || !strings.Contains(recorder.Body.String(), "WebMCP is unavailable") || !strings.Contains(recorder.Body.String(), "signed forms") {
300- t.Fatalf("status=%d body=%s", recorder.Code, recorder.Body.String())
301+ app.ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/manage/health", nil))
302+ body := recorder.Body.String()
303+ for _, want := range []string{"<code>1.4.0</code>", "abc1234", "<code>" + scriptsVersion + "</code>", `id="webmcp-status"`, "WebMCP is unavailable", "signed forms"} {
304+ if recorder.Code != http.StatusOK || !strings.Contains(body, want) {
305+ t.Fatalf("health page missing %q: status=%d", want, recorder.Code)
306+ }
307+ }
308+ for _, old := range []string{"/tools", "/manage/status"} {
309+ moved := httptest.NewRecorder()
310+ app.ServeHTTP(moved, httptest.NewRequest(http.MethodGet, old, nil))
311+ if moved.Code != http.StatusMovedPermanently || moved.Header().Get("Location") != "/manage/health" {
312+ t.Fatalf("%s: status=%d location=%q", old, moved.Code, moved.Header().Get("Location"))
313+ }
314 }
315 }
316diff --git a/internal/webui/webui.go b/internal/webui/webui.go
317index ac7cc56..458120d 100644
318--- a/internal/webui/webui.go
319+++ b/internal/webui/webui.go
320@@ -46,18 +46,24 @@ type ActorResolver func(*http.Request) (string, error)
321 type QRProvider func(string) ([]byte, error)
322
323 type App struct {
324- backend Backend
325- actor ActorResolver
326- qr QRProvider
327- tmpl *template.Template
328+ backend Backend
329+ actor ActorResolver
330+ qr QRProvider
331+ tmpl *template.Template
332+ version string
333+ revision string
334 }
335
336 type Options struct {
337 Actor ActorResolver
338 QR QRProvider
339+ // Version and Revision identify the relay build on the health page.
340+ Version, Revision string
341 }
342
343 type PageData struct {
344+ Version string
345+ Revision string
346 Title string
347 URL string
348 Slug string
349@@ -96,7 +102,7 @@ func New(backend Backend, options Options) (*App, error) {
350 if qr == nil {
351 qr = defaultQR
352 }
353- return &App{backend: backend, actor: options.Actor, qr: qr, tmpl: tmpl}, nil
354+ return &App{backend: backend, actor: options.Actor, qr: qr, tmpl: tmpl, version: options.Version, revision: options.Revision}, nil
355 }
356
357 func defaultQR(text string) ([]byte, error) {
358@@ -147,7 +153,12 @@ func (a *App) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
359 a.webMCPQuery(writer, request)
360 return
361 }
362- if request.Method == http.MethodGet && (request.URL.Path == "/repos" || request.URL.Path == "/repo" || request.URL.Path == "/files" || request.URL.Path == "/file" || request.URL.Path == "/manage/status") {
363+ if request.URL.Path == "/manage/status" || request.URL.Path == "/tools" {
364+ // Service status and the browser tools report both live on Health now.
365+ http.Redirect(writer, request, requestPrefix(request)+"/manage/health", http.StatusMovedPermanently)
366+ return
367+ }
368+ if request.Method == http.MethodGet && (request.URL.Path == "/repos" || request.URL.Path == "/repo" || request.URL.Path == "/files" || request.URL.Path == "/file" || request.URL.Path == "/manage/health") {
369 a.browse(writer, request)
370 return
371 }
372@@ -511,7 +522,7 @@ func (a *App) browse(writer http.ResponseWriter, request *http.Request) {
373 method = "browsefiles"
374 case "/file":
375 method = "browsefile"
376- case "/manage/status":
377+ case "/manage/health":
378 method = "browsestatus"
379 }
380 params := []json.RawMessage{}
381@@ -648,8 +659,6 @@ func browseTab(path string) string {
382 return "files"
383 case "/file":
384 return "file"
385- case "/manage/status":
386- return "status"
387 default:
388 return "health"
389 }
390@@ -686,13 +695,14 @@ func (a *App) render(writer http.ResponseWriter, request *http.Request, data Pag
391 }
392 data.URL, data.Slug, data.Identity, data.Policy, data.Actor, data.Base = a.backend.URL(), a.backend.Slug(), a.backend.Identity(), a.backend.Policy(), actor, requestPrefix(request)
393 data.Owner = actor != "" && actor == data.Policy.Owner
394+ data.Version, data.Revision = a.version, a.revision
395 data.Methods = supportedMethods
396 if data.Query == nil {
397 data.Query = request.URL.Query()
398 }
399 data.Path = strings.TrimPrefix(request.URL.Path, data.Base)
400 a.privatePageData(&data, request, actor)
401- if !data.Private && data.Actor == "" && railKind(data.Tab) == "manage" && data.Tab != "tools" {
402+ if !data.Private && data.Actor == "" && railKind(data.Tab) == "manage" {
403 // Management pages are for signed-in people only. Guests see the
404 // sign-in page at the same address and come back after signing in.
405 data.Tab, data.Notice = "signin", "Sign in to manage this relay."
406@@ -924,7 +934,7 @@ func tabForPath(path string) string {
407 return "files"
408 case "file":
409 return "file"
410- case "tools", "signin", "sites":
411+ case "signin", "sites":
412 return path
413 case "people", "moderation", "rules", "identity", "connect", "data", "sync", "views", "health", "owner":
414 return path

About

ownerc01503e8035f
visibilitypublic
head7fccd10734ba

Maintainers

  • c01503e8035f owner
  • ba1b5beed5b5 agent hermes

Clone

  • https://012.run/npub1cq2s86qrtadf36eqq3epm38wmhdynrrcwxf84eldk3f94dkv7ttsrulrz5/tinyrelay.git

Refs