package server import ( "context" "errors" "log/slog" "net/http" "time" "github.com/go-chi/chi/v5" "github.com/VictorVargas/rony-chat-bot/internal/config" ) type Server struct { httpSrv *http.Server } func New(cfg *config.Config, h *Handlers) *Server { r := chi.NewRouter() r.Use(RequestID) r.Use(Logging) r.Use(CORS(cfg.Server.CORSOrigins)) if cfg.Server.RateLimit.RequestsPerMinute > 0 { r.Use(RateLimit(cfg.Server.RateLimit)) } r.Get("/", func(w http.ResponseWriter, _ *http.Request) { w.Header().Set("Content-Type", "text/plain; charset=utf-8") _, _ = w.Write([]byte("Rony Chat Bot — see /api/health, /api/info, POST /api/chat\n")) }) r.Route("/api", func(r chi.Router) { r.Post("/chat", h.Chat) r.Post("/reindex", h.Reindex) r.Get("/health", h.Health) r.Get("/info", h.Info) }) srv := &http.Server{ Addr: cfg.Addr(), Handler: r, ReadTimeout: time.Duration(cfg.Server.ReadTimeoutMS) * time.Millisecond, WriteTimeout: 0, // SSE streams must not be cut off by WriteTimeout IdleTimeout: 120 * time.Second, } return &Server{httpSrv: srv} } func (s *Server) Start() error { slog.Info("http server starting", "addr", s.httpSrv.Addr) if err := s.httpSrv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) { return err } return nil } func (s *Server) Shutdown(ctx context.Context) error { return s.httpSrv.Shutdown(ctx) }