package server import ( "io/fs" "net/http" "time" "git.eeqj.de/sneak/chat/web" sentryhttp "github.com/getsentry/sentry-go/http" "github.com/go-chi/chi" "github.com/go-chi/chi/middleware" "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/spf13/viper" ) const routeTimeout = 60 * time.Second // SetupRoutes configures the HTTP routes and middleware chain. func (s *Server) SetupRoutes() { s.router = chi.NewRouter() s.setupMiddleware() s.setupHealthAndMetrics() s.setupAPIRoutes() s.setupSPA() } func (s *Server) setupMiddleware() { s.router.Use(middleware.Recoverer) s.router.Use(middleware.RequestID) s.router.Use(s.mw.Logging()) if viper.GetString("METRICS_USERNAME") != "" { s.router.Use(s.mw.Metrics()) } s.router.Use(s.mw.CORS()) s.router.Use(middleware.Timeout(routeTimeout)) if s.sentryEnabled { sentryHandler := sentryhttp.New(sentryhttp.Options{ Repanic: true, }) s.router.Use(sentryHandler.Handle) } } func (s *Server) setupHealthAndMetrics() { s.router.Get("/.well-known/healthcheck.json", s.h.HandleHealthCheck()) if viper.GetString("METRICS_USERNAME") != "" { s.router.Group(func(r chi.Router) { r.Use(s.mw.MetricsAuth()) r.Get("/metrics", http.HandlerFunc(promhttp.Handler().ServeHTTP)) }) } } func (s *Server) setupAPIRoutes() { s.router.Route("/api/v1", func(r chi.Router) { r.Get("/server", s.h.HandleServerInfo()) r.Post("/session", s.h.HandleCreateSession()) r.Get("/state", s.h.HandleState()) r.Get("/messages", s.h.HandleGetMessages()) r.Post("/messages", s.h.HandleSendCommand()) r.Get("/history", s.h.HandleGetHistory()) r.Get("/channels", s.h.HandleListAllChannels()) r.Get("/channels/{channel}/members", s.h.HandleChannelMembers()) }) } func (s *Server) setupSPA() { distFS, err := fs.Sub(web.Dist, "dist") if err != nil { s.log.Error("failed to get web dist filesystem", "error", err) return } fileServer := http.FileServer(http.FS(distFS)) s.router.Get("/*", func(w http.ResponseWriter, r *http.Request) { readFS, ok := distFS.(fs.ReadFileFS) if !ok { http.NotFound(w, r) return } f, readErr := readFS.ReadFile(r.URL.Path[1:]) if readErr != nil || len(f) == 0 { indexHTML, _ := readFS.ReadFile("index.html") w.Header().Set("Content-Type", "text/html; charset=utf-8") w.WriteHeader(http.StatusOK) _, _ = w.Write(indexHTML) return } fileServer.ServeHTTP(w, r) }) }