139 lines
3.7 KiB
Go
139 lines
3.7 KiB
Go
package http
|
||
|
||
import (
|
||
"context"
|
||
"database/sql"
|
||
"github.com/99designs/gqlgen/graphql/handler"
|
||
"github.com/99designs/gqlgen/graphql/handler/transport"
|
||
"github.com/99designs/gqlgen/graphql/playground"
|
||
"github.com/go-chi/chi/v5"
|
||
"github.com/gorilla/websocket"
|
||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||
"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"
|
||
"time"
|
||
)
|
||
|
||
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))
|
||
|
||
// Создаем отдельный обработчик для WebSocket
|
||
wsHandler := handler.NewDefaultServer(graph.NewExecutableSchema(graph.Config{
|
||
Resolvers: graph.NewResolver(s.services, s.db, s.services.Messages),
|
||
}))
|
||
|
||
// Настройка WebSocket транспорта
|
||
wsUpgrader := websocket.Upgrader{
|
||
CheckOrigin: func(r *http.Request) bool {
|
||
origin := r.Header.Get("Origin")
|
||
for _, allowed := range allowedOrigins {
|
||
if origin == allowed {
|
||
return true
|
||
}
|
||
}
|
||
return false
|
||
},
|
||
ReadBufferSize: 1024,
|
||
WriteBufferSize: 1024,
|
||
}
|
||
|
||
wsHandler.AddTransport(&transport.Websocket{
|
||
Upgrader: wsUpgrader,
|
||
KeepAlivePingInterval: 30 * time.Second,
|
||
})
|
||
|
||
// Создаем обычный HTTP обработчик
|
||
httpHandler := handler.NewDefaultServer(graph.NewExecutableSchema(graph.Config{
|
||
Resolvers: graph.NewResolver(s.services, s.db, s.services.Messages),
|
||
}))
|
||
|
||
// Раздельные эндпоинты:
|
||
|
||
// 1. Обычные HTTP GraphQL запросы (только POST)
|
||
s.router.With(middleware.AuthMiddleware(s.tokenAuth)).Post("/query", httpHandler.ServeHTTP)
|
||
|
||
// 2. WebSocket подписки (отдельный эндпоинт с WS middleware)
|
||
s.router.With(middleware.WSAuthMiddleware(s.tokenAuth)).Get("/ws", func(w http.ResponseWriter, r *http.Request) {
|
||
// Проверяем, что это WebSocket запрос
|
||
if websocket.IsWebSocketUpgrade(r) {
|
||
wsHandler.ServeHTTP(w, r)
|
||
return
|
||
}
|
||
http.Error(w, "WebSocket upgrade required", http.StatusBadRequest)
|
||
})
|
||
|
||
// 3. Playground и другие обработчики
|
||
s.router.Handle("/", playground.Handler("GraphQL playground", "/query"))
|
||
s.router.Handle("/uploads/*", http.StripPrefix("/uploads/", http.FileServer(http.Dir("./uploads"))))
|
||
}
|
||
|
||
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 {
|
||
log.Printf("Server running on %s:%s", s.cfg.Server.Host, s.cfg.Server.Port)
|
||
log.Printf("WebSocket subscriptions at /ws")
|
||
return http.ListenAndServe(s.cfg.Server.Host+":"+s.cfg.Server.Port, s.router)
|
||
}
|
||
|
||
func (s *Server) Shutdown(ctx context.Context) error {
|
||
// Graceful shutdown logic
|
||
return nil
|
||
}
|