package http import ( "context" "database/sql" "github.com/99designs/gqlgen/graphql/handler" "github.com/99designs/gqlgen/graphql/handler/transport" "github.com/go-chi/chi/v5" "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/vektah/gqlparser/v2/ast" "log" "net/http" "os" "tailly_back_v2/internal/config" "tailly_back_v2/internal/http/graph" "tailly_back_v2/internal/http/middleware" "tailly_back_v2/internal/service" "tailly_back_v2/pkg/auth" ) type Server struct { router *chi.Mux cfg *config.Config services *service.Services tokenAuth *auth.TokenAuth db *sql.DB } func NewServer( cfg *config.Config, services *service.Services, tokenAuth *auth.TokenAuth, db *sql.DB, ) *Server { s := &Server{ router: chi.NewRouter(), cfg: cfg, services: services, tokenAuth: tokenAuth, db: db, } s.configureRouter() s.configureMetrics() return s } func (s *Server) configureRouter() { allowedOrigins := []string{ "http://localhost:3000", "https://tailly.ru", "http://tailly.ru", "ws://tailly.ru", "wss://tailly.ru", "ws://localhost:3000", "http://localhost:3006", } logger := log.New(os.Stdout, "HTTP: ", log.LstdFlags) s.router.Use(middleware.LoggingMiddleware(logger)) s.router.Use(middleware.MetricsMiddleware) s.router.Use(middleware.CORS(allowedOrigins)) s.router.Use(middleware.WSAuthMiddleware(s.tokenAuth)) s.router.Use(middleware.AuthMiddleware(s.tokenAuth)) resolver := graph.NewResolver(s.services, s.db, s.services.Messages) srv := handler.NewDefaultServer(graph.NewExecutableSchema(graph.Config{ Resolvers: resolver, })) srv.AddTransport(&transport.POST{}) srv.AddTransport(&transport.GET{}) srv.AddTransport(&transport.Options{}) // Настройка SSE транспорта (правильный способ) sseTransport := &transport.SSE{} srv.AddTransport(sseTransport) // Простейший кеш запросов (альтернатива MemoryCache) srv.SetQueryCache(&NoopCache{}) }) s.router.Handle("/", playground.Handler("GraphQL playground", "/query")) s.router.Handle("/query", srv) s.router.Handle("/uploads/*", http.StripPrefix("/uploads/", http.FileServer(http.Dir("./uploads")))) } type NoopCache struct{} func (n *NoopCache) Get(ctx context.Context, key string) (*ast.QueryDocument, bool) { return nil, false } func (n *NoopCache) Add(ctx context.Context, key string, value *ast.QueryDocument) {} func (s *Server) configureMetrics() { metricsRouter := chi.NewRouter() metricsRouter.Get("/metrics", promhttp.Handler().ServeHTTP) go func() { if err := http.ListenAndServe(":9100", metricsRouter); err != nil { log.Printf("Metrics server error: %v", err) } }() } func (s *Server) Run() error { return http.ListenAndServe(s.cfg.Server.Host+":"+s.cfg.Server.Port, s.router) } func (s *Server) Shutdown(ctx context.Context) error { return nil }