mirror of
https://github.com/vee1e/subidx.git
synced 2026-09-02 02:07:13 +00:00
- Embedded Svelte dashboard: search, virtualized results, filter, sort, dates, export, rate budget meter, streaming NDJSON search - Live feed: SSE /v1/feed, delta polling /v1/watch, x-max-seq cursor, ingest hook and subscriber hub, store mutex fix so reads never starve behind ingest backlog - gzip middleware, /v1/stats with cache and bounded top-k - CORS allow-list flag for split frontend deployments - Dockerfile + render.yaml + vercel.json, serve gains -no-drain
52 lines
1.2 KiB
Go
52 lines
1.2 KiB
Go
package server
|
|
|
|
import (
|
|
"compress/gzip"
|
|
"net/http"
|
|
"strings"
|
|
)
|
|
|
|
type gzipResponseWriter struct {
|
|
http.ResponseWriter
|
|
gz *gzip.Writer
|
|
}
|
|
|
|
func (g *gzipResponseWriter) Write(b []byte) (int, error) {
|
|
return g.gz.Write(b)
|
|
}
|
|
|
|
func (g *gzipResponseWriter) Flush() {
|
|
g.gz.Flush()
|
|
if f, ok := g.ResponseWriter.(http.Flusher); ok {
|
|
f.Flush()
|
|
}
|
|
}
|
|
|
|
// gzipMiddleware compresses search and stats responses when the client
|
|
// accepts it. Hostname lists are highly repetitive text (5-6x ratios),
|
|
// so BestSpeed keeps the CPU cost trivial even while tailers ingest.
|
|
func gzipMiddleware(next http.Handler) http.Handler {
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodGet ||
|
|
!strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") {
|
|
next.ServeHTTP(w, r)
|
|
return
|
|
}
|
|
switch r.URL.Path {
|
|
case "/v1/search", "/v1/stats":
|
|
h := w.Header()
|
|
h.Set("Content-Encoding", "gzip")
|
|
h.Add("Vary", "Accept-Encoding")
|
|
h.Del("Content-Length")
|
|
gz, err := gzip.NewWriterLevel(w, gzip.BestSpeed)
|
|
if err != nil {
|
|
next.ServeHTTP(w, r)
|
|
return
|
|
}
|
|
next.ServeHTTP(&gzipResponseWriter{ResponseWriter: w, gz: gz}, r)
|
|
gz.Close()
|
|
default:
|
|
next.ServeHTTP(w, r)
|
|
}
|
|
})
|
|
}
|