2e491e20c1
Reader (internal/nostr): - Connects to all configured relays via WebSocket - Subscribes to kind 2003 (NIP-35) events since (now - backfill_days) - Parses x/title/file/tracker/i/t tags into DB rows - Reconnects with exponential backoff (5s → 5min) on disconnect Torznab API (internal/torznab): - GET /api?t=caps — XML capabilities doc (exact shape Sonarr expects) - GET /api?t=search — FTS5 full-text search over title+description - All other t= types (tvsearch, movie, etc.) route to the same search handler - API key auth via ?apikey= query param; 401 XML error on missing/bad key - Magnet links built from info_hash + event tracker tags DB (internal/db/queries.go): - InsertTorrent with upsert + related rows (trackers, tags, files) - Search with FTS5 + optional category filter (parent cat expands to range) - GetAPIKey, CreateAPIKey, UpsertRelay, UpdateRelaySync/LastEvent CLI (cmd/kindexr-cli): - apikey create --label <name> [--visibility all|wot|curated] Health endpoint now reports live relays_connected count.
94 lines
2.4 KiB
Go
94 lines
2.4 KiB
Go
// Package server provides the HTTP server for kindexr, including the /health endpoint.
|
|
package server
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
|
|
"github.com/go-chi/chi/v5"
|
|
"github.com/go-chi/chi/v5/middleware"
|
|
"git.utn.lol/enki/kindexr/internal/config"
|
|
"git.utn.lol/enki/kindexr/internal/db"
|
|
"git.utn.lol/enki/kindexr/internal/torznab"
|
|
)
|
|
|
|
// Server holds the HTTP server state.
|
|
type Server struct {
|
|
cfg *config.Config
|
|
db *db.DB
|
|
router chi.Router
|
|
version string
|
|
connectedRelays func() int // returns live relay count; nil = always 0
|
|
}
|
|
|
|
// New creates a new Server with the given config, database, and version string.
|
|
func New(cfg *config.Config, database *db.DB, version string) *Server {
|
|
s := &Server{
|
|
cfg: cfg,
|
|
db: database,
|
|
version: version,
|
|
}
|
|
s.router = s.buildRouter()
|
|
return s
|
|
}
|
|
|
|
// SetConnectedRelays wires in a function that returns the current connected
|
|
// relay count. Call this after the reader is started.
|
|
func (s *Server) SetConnectedRelays(fn func() int) {
|
|
s.connectedRelays = fn
|
|
}
|
|
|
|
// Handler returns the root http.Handler for the server.
|
|
func (s *Server) Handler() http.Handler {
|
|
return s.router
|
|
}
|
|
|
|
// buildRouter wires up all routes and middleware.
|
|
func (s *Server) buildRouter() chi.Router {
|
|
r := chi.NewRouter()
|
|
r.Use(middleware.Logger)
|
|
r.Use(middleware.Recoverer)
|
|
|
|
r.Get("/health", s.healthHandler)
|
|
|
|
tz := torznab.New(s.cfg, s.db, s.version)
|
|
tz.Mount(r)
|
|
|
|
return r
|
|
}
|
|
|
|
// healthResponse is the JSON body returned by GET /health.
|
|
type healthResponse struct {
|
|
Status string `json:"status"`
|
|
Version string `json:"version"`
|
|
RelaysConnected int `json:"relays_connected"`
|
|
EventsTotal int64 `json:"events_total"`
|
|
LastEventAt *int64 `json:"last_event_at"`
|
|
}
|
|
|
|
// healthHandler handles GET /health.
|
|
func (s *Server) healthHandler(w http.ResponseWriter, r *http.Request) {
|
|
stats, err := s.db.GetStats(r.Context())
|
|
if err != nil {
|
|
http.Error(w, `{"error":"internal server error"}`, http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
relaysConnected := 0
|
|
if s.connectedRelays != nil {
|
|
relaysConnected = s.connectedRelays()
|
|
}
|
|
|
|
resp := healthResponse{
|
|
Status: "ok",
|
|
Version: s.version,
|
|
RelaysConnected: relaysConnected,
|
|
EventsTotal: stats.EventsTotal,
|
|
LastEventAt: stats.LastEventAt,
|
|
}
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.WriteHeader(http.StatusOK)
|
|
_ = json.NewEncoder(w).Encode(resp)
|
|
}
|