extract counter as middleware
4 files changed, 64 insertions(+), 8 deletions(-)
M domain/analytics/counter.go → domain/analytics/counter.go
@@ -1,4 +1,4 @@ -package stats +package analytics import "net/http"
A domain/analytics/middleware.go
@@ -0,0 +1,55 @@ +package analytics + +import ( + "context" + "net/http" +) + +type contextKey struct{} + +var countKeyContextKey contextKey + +func WithCountKey(r *http.Request, key string) *http.Request { + return r.WithContext(context.WithValue(r.Context(), countKeyContextKey, key)) +} + +func GetCountKey(r *http.Request) (string, bool) { + key, ok := r.Context().Value(countKeyContextKey).(string) + + return key, ok +} + +func CounterMiddleware(counter Counter) func(http.Handler) http.Handler { + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + rw := newStatusCapturingResponseWriter(w) + + next.ServeHTTP(rw, r) + + countKey, ok := GetCountKey(r) + if !ok { + if rw.status >= 201 { + countKey = http.StatusText(rw.status) + } else { + countKey = r.URL.Path + } + } + + counter.Count(r, countKey) + }) + } +} + +type statusCapturingResponseWriter struct { + http.ResponseWriter + status int +} + +func newStatusCapturingResponseWriter(w http.ResponseWriter) *statusCapturingResponseWriter { + return &statusCapturingResponseWriter{w, http.StatusOK} +} + +func (w *statusCapturingResponseWriter) WriteHeader(code int) { + w.status = code + w.ResponseWriter.WriteHeader(code) +}
M domain/web/mux.go → domain/web/mux.go
@@ -7,6 +7,7 @@ "regexp" "slices" "strings" + "alin.ovh/homestead/domain/analytics" calendar "alin.ovh/homestead/domain/calendar/templates" "alin.ovh/homestead/domain/web/server" "alin.ovh/homestead/domain/web/templates"@@ -34,7 +35,7 @@ urlPath = "/go" + r.URL.Path } urlPath, shouldRedirect := website.reader.CanonicalisePath(urlPath) if shouldRedirect { - website.counter.Count(r, "302") + analytics.WithCountKey(r, "302") http.Redirect(w, r, urlPath, http.StatusFound) return nil@@ -46,11 +47,11 @@ return ihttp.InternalServerError("Error reading file", err) } if file == nil { - website.counter.Count(r, "404") + analytics.WithCountKey(r, "404") return ihttp.NotFound("File not found") } - website.counter.Count(r, file.Title) + analytics.WithCountKey(r, file.Title) w.Header().Add("ETag", file.Etag) w.Header().Add("Vary", "Accept-Encoding") if file.StyleHash != "" {@@ -76,7 +77,7 @@ return nil } func (website *Website) Calendar(w http.ResponseWriter, r *http.Request) ihttp.Error { - website.counter.Count(r, "Calendar") + analytics.WithCountKey(r, "Calendar") err := calendar.CalendarPage(*website.siteSettings, templates.PageSettings{ Title: "Calendar", TitleAttrs: templates.Attrs{},
M domain/web/website.go → domain/web/website.go
@@ -9,7 +9,7 @@ "slices" "sync" "time" - stats "alin.ovh/homestead/domain/analytics" + "alin.ovh/homestead/domain/analytics" "alin.ovh/homestead/domain/analytics/goatcounter" "alin.ovh/homestead/domain/analytics/nullcounter" "alin.ovh/homestead/domain/calendar"@@ -47,7 +47,7 @@ type Website struct { config *config.Config siteSettings *templates.SiteSettings - counter stats.Counter + counter analytics.Counter log *log.Logger reader storage.Reader calendar *calendar.Calendar@@ -217,7 +217,7 @@ mux.HandleFunc("/style.css", staticHandler) } - website.Handler = mux + website.Handler = analytics.CounterMiddleware(website.counter)(mux) return website, nil }