vole
Clone
Read only, no credentials:
https://rodent.software/vole.git
Write over SSH with a registered key:
git clone ssh://git@rodent.software:2222/vole.git
A free and open-source forge that lives in your Git repo.
Add one Admin page with status, accounts, and the audit log
43e681d8a5d94567aa9530d32b2c89223c1a17f9 · Keenan Nemetz · 5d ago · parent af018ce8
Add one Admin page with status, accounts, and the audit log Instance administration was split between the Status and Accounts pages, and the audit log had no browser view at all. The new /admin page gathers the status tabs, the account roster, and a read-only audit viewer into one place with a tab for each, and /status, /runners, and /users redirect to the tab they named so old links keep working. The audit reader walks the file backwards a chunk at a time, returns the newest records first, and shows one extra record to know whether an older page exists. It skips a line it cannot parse rather than failing the page and refuses a symlink or a non-regular file, as startup already does. Refs #198 (01M6MK0NNZ2XR1D5TQKNWYT4HN).
server/web/status.go +18−36
| @@ -1,12 +1,13 @@ | |||
| 1 | 1 | package web | |
| 2 | 2 | ||
| 3 | // The status page reports how much disk the instance uses, what the LFS pool | ||
| 4 | // contains, and whether people are reading it. It exposes counts and sizes, never | ||
| 5 | // repository contents; /healthz remains the separate liveness probe. | ||
| 3 | // The status tabs on the Admin page report how much disk the instance uses, what | ||
| 4 | // the LFS pool contains, and whether people are reading it. They expose counts and | ||
| 5 | // sizes, never repository contents; /healthz remains the separate liveness probe. | ||
| 6 | 6 | ||
| 7 | 7 | import ( | |
| 8 | 8 | "fmt" | |
| 9 | 9 | "net/http" | |
| 10 | "net/url" | ||
| 10 | 11 | "strings" | |
| 11 | 12 | "sync" | |
| 12 | 13 | "time" | |
| @@ -78,30 +79,21 @@ type statusCache struct { | |||
| 78 | 79 | lfs statusLFSView | |
| 79 | 80 | } | |
| 80 | 81 | ||
| 81 | func (w *Web) StatusPage(rw http.ResponseWriter, r *http.Request) { | ||
| 82 | if _, ok := w.requireInstanceAdmin(rw, r); !ok { | ||
| 83 | return | ||
| 84 | } | ||
| 85 | tab := statusTab(r.URL.Query().Get("tab")) | ||
| 86 | view := struct { | ||
| 87 | Page | ||
| 88 | Tab string | ||
| 89 | Accounts int | ||
| 90 | Repos int | ||
| 91 | Queued int | ||
| 92 | Running int | ||
| 93 | Runners int | ||
| 94 | Started time.Time | ||
| 95 | Storage statusStorageView | ||
| 96 | LFS statusLFSView | ||
| 97 | Views statusViewsView | ||
| 98 | RunnerErr string | ||
| 99 | RunnerSessions []runnerView | ||
| 100 | RunnerQueue []queueView | ||
| 101 | RunnerWindow string | ||
| 102 | Config *statusConfigView | ||
| 103 | }{Page: w.page(r, "Status"), Tab: tab, Started: w.Started} | ||
| 82 | // StatusRedirect keeps old links working: the status tabs now live on the Admin | ||
| 83 | // page, and the tab name is preserved so a bookmark lands on the same section. | ||
| 84 | func (w *Web) StatusRedirect(rw http.ResponseWriter, r *http.Request) { | ||
| 85 | target := "/admin" | ||
| 86 | if tab := adminTabFrom(r.URL.Query().Get("tab")); tab != "overview" { | ||
| 87 | target += "?tab=" + url.QueryEscape(tab) | ||
| 88 | } | ||
| 89 | http.Redirect(rw, r, target, http.StatusMovedPermanently) | ||
| 90 | } | ||
| 104 | 91 | ||
| 92 | // fillStatusView reads the instance state the status tabs show. It is a method on | ||
| 93 | // the Admin view because those tabs are sections of that page, not a page of | ||
| 94 | // their own; each branch reads only what its tab renders. | ||
| 95 | func (w *Web) fillStatusView(view *adminPageView) { | ||
| 96 | tab := view.Tab | ||
| 105 | 97 | if tab == "overview" { | |
| 106 | 98 | if w.Ident != nil { | |
| 107 | 99 | view.Accounts = w.Ident.Snapshot().Count | |
| @@ -161,16 +153,6 @@ func (w *Web) StatusPage(rw http.ResponseWriter, r *http.Request) { | |||
| 161 | 153 | if tab == "configuration" && w.Cfg != nil { | |
| 162 | 154 | view.Config = w.statusConfig() | |
| 163 | 155 | } | |
| 164 | w.render(rw, "status", view, 0) | ||
| 165 | } | ||
| 166 | |||
| 167 | func statusTab(raw string) string { | ||
| 168 | switch raw { | ||
| 169 | case "storage", "runners", "configuration": | ||
| 170 | return raw | ||
| 171 | default: | ||
| 172 | return "overview" | ||
| 173 | } | ||
| 174 | 156 | } | |
| 175 | 157 | ||
| 176 | 158 | func (w *Web) instanceStats() (storage.Stats, error, statusLFSView) { | |