From d4eef320277720a7e09a2e96143273314ec1f5ef Mon Sep 17 00:00:00 2001 From: leofvo Date: Thu, 24 Oct 2024 12:04:59 +0200 Subject: [PATCH 1/2] build: expose openapi on http server --- example.config.yaml | 1 + internal/config/config.go | 2 ++ internal/servers/server.go | 27 ++++++++++++++++++++------- 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/example.config.yaml b/example.config.yaml index c6af74201..b1e063030 100644 --- a/example.config.yaml +++ b/example.config.yaml @@ -8,6 +8,7 @@ server: http: enabled: true port: 3476 + expose_openapi: false tls: enabled: false cert: /etc/letsencrypt/live/yourdomain.com/fullchain.pem diff --git a/internal/config/config.go b/internal/config/config.go index e99ae81ad..7866f7c10 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -37,6 +37,7 @@ type ( HTTP struct { Enabled bool `mapstructure:"enabled"` // Whether the HTTP server is enabled Port string `mapstructure:"port"` // Port for the HTTP server + ExposeOpenAPI bool `mapstructure:"expose_openapi"` // expose OpenAPI configuration TLSConfig TLSConfig `mapstructure:"tls"` // TLS configuration for the HTTP server CORSAllowedOrigins []string `mapstructure:"cors_allowed_origins"` // List of allowed origins for CORS CORSAllowedHeaders []string `mapstructure:"cors_allowed_headers"` // List of allowed headers for CORS @@ -276,6 +277,7 @@ func DefaultConfig() *Config { HTTP: HTTP{ Enabled: true, Port: "3476", + ExposeOpenAPI: false, TLSConfig: TLSConfig{ Enabled: false, }, diff --git a/internal/servers/server.go b/internal/servers/server.go index 5f84626d0..7f99f6a53 100644 --- a/internal/servers/server.go +++ b/internal/servers/server.go @@ -305,24 +305,37 @@ func (s *Container) Run( }), } - mux := runtime.NewServeMux(muxOpts...) + // Create the gRPC gateway mux + grpcMux := runtime.NewServeMux(muxOpts...) - if err = grpcV1.RegisterPermissionHandler(ctx, mux, conn); err != nil { + if err = grpcV1.RegisterPermissionHandler(ctx, grpcMux, conn); err != nil { return err } - if err = grpcV1.RegisterSchemaHandler(ctx, mux, conn); err != nil { + if err = grpcV1.RegisterSchemaHandler(ctx, grpcMux, conn); err != nil { return err } - if err = grpcV1.RegisterDataHandler(ctx, mux, conn); err != nil { + if err = grpcV1.RegisterDataHandler(ctx, grpcMux, conn); err != nil { return err } - if err = grpcV1.RegisterBundleHandler(ctx, mux, conn); err != nil { + if err = grpcV1.RegisterBundleHandler(ctx, grpcMux, conn); err != nil { return err } - if err = grpcV1.RegisterTenancyHandler(ctx, mux, conn); err != nil { + if err = grpcV1.RegisterTenancyHandler(ctx, grpcMux, conn); err != nil { return err } + // Create a new http.ServeMux for serving your OpenAPI file and gRPC gateway + httpMux := http.NewServeMux() + + if srv.HTTP.ExposeOpenAPI { + httpMux.HandleFunc("/openapi.json", func(w http.ResponseWriter, r *http.Request) { + http.ServeFile(w, r, "./docs/api-reference/openapi.json") + }) + } + + // Handle all gRPC gateway routes + httpMux.Handle("/", grpcMux) + httpServer = &http.Server{ Addr: ":" + srv.HTTP.Port, Handler: cors.New(cors.Options{ @@ -333,7 +346,7 @@ func (s *Container) Run( http.MethodGet, http.MethodPost, http.MethodHead, http.MethodPatch, http.MethodDelete, http.MethodPut, }, - }).Handler(mux), + }).Handler(httpMux), ReadHeaderTimeout: 5 * time.Second, } From a93e520c20d20f3446435371860b4c056f498c25 Mon Sep 17 00:00:00 2001 From: leofvo Date: Thu, 24 Oct 2024 14:37:22 +0200 Subject: [PATCH 2/2] refactor(server.go): improve code lisibility factorize repetitive blocks verify http method on openapi request --- internal/servers/server.go | 39 ++++++++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/internal/servers/server.go b/internal/servers/server.go index 7f99f6a53..553102619 100644 --- a/internal/servers/server.go +++ b/internal/servers/server.go @@ -8,6 +8,7 @@ import ( "net" "net/http" "net/http/pprof" + "os" "time" "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/logging" @@ -308,28 +309,38 @@ func (s *Container) Run( // Create the gRPC gateway mux grpcMux := runtime.NewServeMux(muxOpts...) - if err = grpcV1.RegisterPermissionHandler(ctx, grpcMux, conn); err != nil { - return err - } - if err = grpcV1.RegisterSchemaHandler(ctx, grpcMux, conn); err != nil { - return err + handlers := []func(context.Context, *runtime.ServeMux, *grpc.ClientConn) error{ + grpcV1.RegisterPermissionHandler, + grpcV1.RegisterSchemaHandler, + grpcV1.RegisterDataHandler, + grpcV1.RegisterBundleHandler, + grpcV1.RegisterTenancyHandler, } - if err = grpcV1.RegisterDataHandler(ctx, grpcMux, conn); err != nil { - return err - } - if err = grpcV1.RegisterBundleHandler(ctx, grpcMux, conn); err != nil { - return err - } - if err = grpcV1.RegisterTenancyHandler(ctx, grpcMux, conn); err != nil { - return err + + for _, handler := range handlers { + if err = handler(ctx, grpcMux, conn); err != nil { + return fmt.Errorf("failed to register handler: %w", err) + } } // Create a new http.ServeMux for serving your OpenAPI file and gRPC gateway httpMux := http.NewServeMux() + const openAPIPath = "./docs/api-reference/openapi.json" if srv.HTTP.ExposeOpenAPI { httpMux.HandleFunc("/openapi.json", func(w http.ResponseWriter, r *http.Request) { - http.ServeFile(w, r, "./docs/api-reference/openapi.json") + if r.Method != http.MethodGet { + http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) + return + } + w.Header().Set("Content-Type", "application/json") + w.Header().Set("Cache-Control", "public, max-age=3600") + if _, err := os.Stat(openAPIPath); os.IsNotExist(err) { + http.Error(w, "OpenAPI specification not found", http.StatusNotFound) + return + } + + http.ServeFile(w, r, openAPIPath) }) }