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.
Expand the admin status page into an operational dashboard
4506a4d3d279556435fdd414265e7499c3dada16 · Keenan Nemetz · 14d ago · parent 2e024cc9
server/web/auth.go +32−1
| @@ -101,15 +101,22 @@ func (w *Web) principal(r *http.Request) *principal { | |||
| 101 | 101 | type statusWriter struct { | |
| 102 | 102 | http.ResponseWriter | |
| 103 | 103 | wroteHeader bool | |
| 104 | status int | ||
| 104 | 105 | } | |
| 105 | 106 | ||
| 106 | 107 | func (s *statusWriter) WriteHeader(code int) { | |
| 108 | if !s.wroteHeader { | ||
| 109 | s.status = code | ||
| 110 | } | ||
| 107 | 111 | s.wroteHeader = true | |
| 108 | 112 | s.ResponseWriter.WriteHeader(code) | |
| 109 | 113 | } | |
| 110 | 114 | ||
| 111 | 115 | func (s *statusWriter) Write(b []byte) (int, error) { | |
| 112 | s.wroteHeader = true | ||
| 116 | if !s.wroteHeader { | ||
| 117 | s.wroteHeader = true | ||
| 118 | s.status = http.StatusOK | ||
| 119 | } | ||
| 113 | 120 | return s.ResponseWriter.Write(b) | |
| 114 | 121 | } | |
| 115 | 122 | ||
| @@ -168,9 +175,33 @@ func (w *Web) Middleware(next http.Handler) http.Handler { | |||
| 168 | 175 | w.serverError(sw, r, "") | |
| 169 | 176 | }() | |
| 170 | 177 | next.ServeHTTP(sw, r) | |
| 178 | w.recordPageView(r, sw) | ||
| 171 | 179 | }) | |
| 172 | 180 | } | |
| 173 | 181 | ||
| 182 | // recordPageView counts a successfully rendered HTML page. Static files, Git | ||
| 183 | // traffic, redirects, HTMX fragments and the status page itself are excluded, so | ||
| 184 | // the metric follows reading rather than polling or asset loads. It is | ||
| 185 | // best-effort: analytics must never turn a page into an error. | ||
| 186 | func (w *Web) recordPageView(r *http.Request, sw *statusWriter) { | ||
| 187 | if w.PageViews == nil || r.Method != http.MethodGet || sw.status != http.StatusOK { | ||
| 188 | return | ||
| 189 | } | ||
| 190 | if r.Header.Get("HX-Request") == "true" || r.URL.Path == "/status" { | ||
| 191 | return | ||
| 192 | } | ||
| 193 | if !strings.HasPrefix(sw.Header().Get("Content-Type"), "text/html") { | ||
| 194 | return | ||
| 195 | } | ||
| 196 | account := "" | ||
| 197 | if pr := w.principal(r); pr != nil { | ||
| 198 | account = pr.account.Name | ||
| 199 | } | ||
| 200 | if err := w.PageViews.Record(account, time.Now().UTC()); err != nil && w.Logger != nil { | ||
| 201 | w.Logger("page view: %v", err) | ||
| 202 | } | ||
| 203 | } | ||
| 204 | |||
| 174 | 205 | // sessionFromCookie returns the first valid session cookie and the name it came | |
| 175 | 206 | // from. The dev cookie is only considered when dev login is enabled, so a stray | |
| 176 | 207 | // cookie on a normal instance is never a credential. | |