v0.4.1
This commit is contained in:
1 parent
439cc20dcf
commit
722a49f6bc
14 files changed
+564
-283
No files matched your search
+12
-31
@@ -5,13 +5,13 @@ import (
|
||||
"crypto/sha256"
|
||||
"embed"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strconv"
|
||||
@@ -111,7 +111,7 @@ func (s *Server) Handler() http.Handler {
|
||||
mux.HandleFunc("GET /{$}", func(w http.ResponseWriter, r *http.Request) { http.Redirect(w, r, "/stats", http.StatusFound) })
|
||||
mux.HandleFunc("GET /stats", s.stats)
|
||||
mux.HandleFunc("GET /stats/live", s.statsLive)
|
||||
mux.HandleFunc("GET /graph/{file}", s.graph)
|
||||
mux.HandleFunc("GET /stats/graph/{key}", s.graphData)
|
||||
mux.HandleFunc("GET /channel", s.channel)
|
||||
mux.HandleFunc("GET /embed/channel", s.channel)
|
||||
mux.HandleFunc("POST /channel/verify/start", s.verifyStart)
|
||||
@@ -250,43 +250,26 @@ func (s *Server) asset(w http.ResponseWriter, r *http.Request) {
|
||||
_, _ = w.Write(data)
|
||||
}
|
||||
|
||||
func (s *Server) graph(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *Server) graphData(w http.ResponseWriter, r *http.Request) {
|
||||
ip := s.clientIP(r).String()
|
||||
// Graph PNGs are cheap to serve: rrd.Manager.Graph caches each
|
||||
// key+theme render on disk for ~55s, so most hits here are a plain
|
||||
// file read, not an rrdtool invocation. One page view alone can
|
||||
// legitimately touch this 6 ranges x 2 themes (lightbox open, theme
|
||||
// toggle) times, before counting multiple tabs or several visitors
|
||||
// sharing one IP (same household/NAT) -- see README "Rate limiting"
|
||||
// for tuning RATE_LIMIT_GRAPH if this still trips in practice.
|
||||
// Each hit here is one rrdtool fetch returning a few hundred small
|
||||
// JSON numbers read straight from the local RRD file -- cheap, so
|
||||
// the limit mainly exists to stop outright abuse. See README "Rate
|
||||
// limiting" for tuning RATE_LIMIT_GRAPH.
|
||||
if s.cfg.RateLimitEnabled && !s.limit.allow("graph|"+ip, s.cfg.RateLimitGraph, time.Minute) {
|
||||
s.tooMany(w)
|
||||
return
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 8*time.Second)
|
||||
defer cancel()
|
||||
file := r.PathValue("file")
|
||||
if !strings.HasSuffix(file, ".png") {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
key := strings.TrimSuffix(file, ".png")
|
||||
theme := "dark"
|
||||
if strings.HasSuffix(key, "-light") {
|
||||
theme = "light"
|
||||
key = strings.TrimSuffix(key, "-light")
|
||||
}
|
||||
if key == "" || strings.Contains(key, "/") {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
p, err := s.rrd.Graph(ctx, key, theme)
|
||||
data, err := s.rrd.Fetch(ctx, r.PathValue("key"))
|
||||
if err != nil {
|
||||
http.Error(w, "graph unavailable", http.StatusServiceUnavailable)
|
||||
http.Error(w, "graph data unavailable", http.StatusServiceUnavailable)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Cache-Control", "public, max-age=60")
|
||||
http.ServeFile(w, r, p)
|
||||
w.Header().Set("Cache-Control", "no-store")
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
_ = json.NewEncoder(w).Encode(data)
|
||||
}
|
||||
|
||||
func (s *Server) channel(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -579,5 +562,3 @@ func (s *Server) render(w http.ResponseWriter, name string, data any) {
|
||||
log.Printf("template error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func EnsureGraphDir(path string) error { return os.MkdirAll(filepath.Clean(path), 0750) }
|
||||
Reference in new issue
Block a user