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/server.go +29−20
| @@ -18,6 +18,7 @@ import ( | |||
| 18 | 18 | "github.com/rodent-software/vole/forge/config" | |
| 19 | 19 | "github.com/rodent-software/vole/forge/identity" | |
| 20 | 20 | "github.com/rodent-software/vole/forge/index" | |
| 21 | "github.com/rodent-software/vole/forge/pageview" | ||
| 21 | 22 | "github.com/rodent-software/vole/forge/repo" | |
| 22 | 23 | "github.com/rodent-software/vole/forge/session" | |
| 23 | 24 | "github.com/rodent-software/vole/server/githttp" | |
| @@ -27,23 +28,24 @@ import ( | |||
| 27 | 28 | ) | |
| 28 | 29 | ||
| 29 | 30 | type Server struct { | |
| 30 | ctx context.Context | ||
| 31 | cancel context.CancelFunc | ||
| 32 | Cfg *config.Config | ||
| 33 | Repos *repo.Store | ||
| 34 | Identity *identity.Store | ||
| 35 | Sessions *session.Store | ||
| 36 | Runs *runs.Store | ||
| 37 | Index *index.Store | ||
| 38 | LFS *lfs.Store | ||
| 39 | Locks *lfs.LockStore | ||
| 40 | Pages *pages.Server | ||
| 41 | Proxy *githttp.Proxy | ||
| 42 | SSH *ssh.Server | ||
| 43 | Web *web.Web | ||
| 44 | Log *slog.Logger | ||
| 45 | mux *http.ServeMux | ||
| 46 | acme *acmeSetup | ||
| 31 | ctx context.Context | ||
| 32 | cancel context.CancelFunc | ||
| 33 | Cfg *config.Config | ||
| 34 | Repos *repo.Store | ||
| 35 | Identity *identity.Store | ||
| 36 | Sessions *session.Store | ||
| 37 | Runs *runs.Store | ||
| 38 | Index *index.Store | ||
| 39 | LFS *lfs.Store | ||
| 40 | Locks *lfs.LockStore | ||
| 41 | PageViews *pageview.Store | ||
| 42 | Pages *pages.Server | ||
| 43 | Proxy *githttp.Proxy | ||
| 44 | SSH *ssh.Server | ||
| 45 | Web *web.Web | ||
| 46 | Log *slog.Logger | ||
| 47 | mux *http.ServeMux | ||
| 48 | acme *acmeSetup | ||
| 47 | 49 | } | |
| 48 | 50 | ||
| 49 | 51 | func New(cfg *config.Config, binPath string, log *slog.Logger) (*Server, error) { | |
| @@ -109,6 +111,7 @@ func New(cfg *config.Config, binPath string, log *slog.Logger) (*Server, error) | |||
| 109 | 111 | wp.LFS = inst.LFS | |
| 110 | 112 | wp.Locks = inst.Locks | |
| 111 | 113 | wp.Sessions = inst.Sessions | |
| 114 | wp.PageViews = inst.PageViews | ||
| 112 | 115 | wp.Logger = func(format string, args ...any) { log.Warn(fmt.Sprintf(format, args...)) } | |
| 113 | 116 | if cfg.DevLogin != "" { | |
| 114 | 117 | log.Warn("development login enabled", "account", cfg.DevLogin, "http_addr", cfg.HTTPAddr) | |
| @@ -122,7 +125,8 @@ func New(cfg *config.Config, binPath string, log *slog.Logger) (*Server, error) | |||
| 122 | 125 | s := &Server{ | |
| 123 | 126 | Cfg: cfg, Repos: inst.Repos, Identity: inst.Identity, Sessions: inst.Sessions, | |
| 124 | 127 | Runs: inst.Runs, Index: inst.Index, acme: acme, | |
| 125 | LFS: inst.LFS, Locks: inst.Locks, Proxy: proxy, SSH: ssh, Web: wp, Log: log, | ||
| 128 | LFS: inst.LFS, Locks: inst.Locks, PageViews: inst.PageViews, | ||
| 129 | Proxy: proxy, SSH: ssh, Web: wp, Log: log, | ||
| 126 | 130 | } | |
| 127 | 131 | wp.AllowCI = s.allowContributorCI | |
| 128 | 132 | wp.RenameRepo = inst.RenameRepo | |
| @@ -177,7 +181,9 @@ func (s *Server) routes() *http.ServeMux { | |||
| 177 | 181 | mux.HandleFunc("POST /account/passkeys/begin", w.AddPasskeyBegin) | |
| 178 | 182 | mux.HandleFunc("POST /account/passkeys/finish", w.AddPasskeyFinish) | |
| 179 | 183 | mux.HandleFunc("GET /auth.js", w.AuthJS) | |
| 180 | mux.HandleFunc("GET /runners", w.RunnersPage) | ||
| 184 | mux.HandleFunc("GET /runners", func(rw http.ResponseWriter, r *http.Request) { | ||
| 185 | http.Redirect(rw, r, "/status?tab=runners", http.StatusMovedPermanently) | ||
| 186 | }) | ||
| 181 | 187 | mux.HandleFunc("GET /repos/new", w.AdminNewRepoPage) | |
| 182 | 188 | mux.HandleFunc("POST /repos", w.CreateAdminRepo) | |
| 183 | 189 | mux.HandleFunc("GET /users", w.AdminUsersPage) | |
| @@ -259,7 +265,7 @@ func (s *Server) routes() *http.ServeMux { | |||
| 259 | 265 | mux.HandleFunc("GET /status", w.StatusPage) | |
| 260 | 266 | mux.HandleFunc("GET /healthz", func(rw http.ResponseWriter, r *http.Request) { | |
| 261 | 267 | rw.Header().Set("Content-Type", "text/plain") | |
| 262 | fmt.Fprintf(rw, "ok\nusers=%d\n", s.Identity.Snapshot().Count) | ||
| 268 | fmt.Fprint(rw, "ok\n") | ||
| 263 | 269 | }) | |
| 264 | 270 | ||
| 265 | 271 | // Anonymous, read-only git. Registered without a method so git's POSTs reach it. | |
| @@ -405,6 +411,9 @@ func (s *Server) Close() error { | |||
| 405 | 411 | if s.Locks != nil { | |
| 406 | 412 | _ = s.Locks.Close() | |
| 407 | 413 | } | |
| 414 | if s.PageViews != nil { | ||
| 415 | _ = s.PageViews.Close() | ||
| 416 | } | ||
| 408 | 417 | if s.Index != nil { | |
| 409 | 418 | if err := s.Index.Close(); err != nil { | |
| 410 | 419 | return err | |