105 lines
2.8 KiB
Go
105 lines
2.8 KiB
Go
package main
|
|
|
|
import (
|
|
"log"
|
|
"net/http"
|
|
"time"
|
|
|
|
"git.farahty.com/nimer/go-mongo/app"
|
|
"git.farahty.com/nimer/go-mongo/controllers"
|
|
"git.farahty.com/nimer/go-mongo/directives"
|
|
"git.farahty.com/nimer/go-mongo/generated"
|
|
"git.farahty.com/nimer/go-mongo/utils"
|
|
"github.com/99designs/gqlgen/graphql/handler"
|
|
"github.com/99designs/gqlgen/graphql/handler/extension"
|
|
"github.com/99designs/gqlgen/graphql/handler/transport"
|
|
"github.com/99designs/gqlgen/graphql/playground"
|
|
|
|
"git.farahty.com/nimer/go-mongo/resolvers"
|
|
"github.com/go-chi/chi/v5"
|
|
"github.com/go-chi/chi/v5/middleware"
|
|
"github.com/go-chi/httprate"
|
|
|
|
"github.com/gorilla/websocket"
|
|
)
|
|
|
|
func createRouter(graphqlServer http.Handler) chi.Router {
|
|
router := chi.NewRouter()
|
|
|
|
// Apply middleware
|
|
if app.Config.Env == "production" {
|
|
router.Use(httprate.LimitByIP(100, 1*time.Minute)) // 100 requests/minute/IP
|
|
}
|
|
|
|
router.Use(middleware.Logger)
|
|
router.Use(middleware.StripSlashes)
|
|
router.Use(middleware.RealIP)
|
|
router.Use(middleware.Recoverer)
|
|
|
|
// Custom middleware for Auth
|
|
router.Use(app.AuthMiddleware)
|
|
|
|
router.Use(app.WriterMiddleware)
|
|
router.Use(app.RequestMiddleware)
|
|
|
|
// REST routes
|
|
router.Mount("/users", controllers.UserRouter())
|
|
|
|
// GraphQL endpoints
|
|
router.Handle("/", playground.Handler("GraphQL Playground", "/graphql"))
|
|
router.Handle("/graphql", graphqlServer)
|
|
|
|
return router
|
|
}
|
|
|
|
func createGraphqlServer() http.Handler {
|
|
cache, err := utils.NewCache(app.RedisClient, 24*time.Hour, "apq:")
|
|
if err != nil {
|
|
log.Fatalf("cannot create APQ redis cache: %v", err)
|
|
}
|
|
|
|
// Setup gqlgen with resolvers and Redis client
|
|
schema := generated.Config{
|
|
Resolvers: &resolvers.Resolver{
|
|
Redis: app.RedisClient,
|
|
},
|
|
}
|
|
|
|
// Map directives (e.g., @auth)
|
|
mapDirectives(&schema)
|
|
|
|
// Initialize GraphQL handler
|
|
srv := handler.New(generated.NewExecutableSchema(schema))
|
|
|
|
// Enable transports (WebSocket, GET, POST, etc.)
|
|
srv.AddTransport(transport.Websocket{
|
|
KeepAlivePingInterval: 10 * time.Second,
|
|
Upgrader: websocket.Upgrader{
|
|
HandshakeTimeout: time.Minute,
|
|
EnableCompression: true,
|
|
CheckOrigin: func(r *http.Request) bool { return true },
|
|
},
|
|
InitFunc: app.AuthorizeWebSocket,
|
|
})
|
|
srv.AddTransport(transport.Options{})
|
|
srv.AddTransport(transport.GET{})
|
|
srv.AddTransport(transport.POST{})
|
|
srv.AddTransport(transport.MultipartForm{})
|
|
|
|
srv.Use(extension.AutomaticPersistedQuery{Cache: cache})
|
|
srv.Use(extension.Introspection{})
|
|
|
|
// Apply global middleware
|
|
srv.AroundRootFields(app.RootFieldsAuthorizer) // Check for @auth at root fields
|
|
//srv.AroundResponses(app.ExpiryMiddleware) // Token expiry validation
|
|
|
|
// Inject DataLoaders into request context
|
|
return app.LoaderMiddleware(app.NewLoaders(), srv)
|
|
}
|
|
|
|
func mapDirectives(config *generated.Config) {
|
|
config.Directives.Auth = directives.Auth
|
|
config.Directives.HasRole = directives.HasRole
|
|
|
|
}
|