feat(server/v2): add swagger server component (#23486)
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
package swagger
|
||||
|
||||
// Config defines the configuration for the Swagger UI server
|
||||
type Config struct {
|
||||
// Enable enables/disables the Swagger UI server
|
||||
Enable bool `mapstructure:"enable" toml:"enable" comment:"Enable enables/disables the Swagger UI server"`
|
||||
// Address defines the server address to bind to
|
||||
Address string `mapstructure:"address" toml:"address" comment:"Address defines the server address to bind to"`
|
||||
}
|
||||
|
||||
// DefaultConfig returns the default configuration
|
||||
func DefaultConfig() *Config {
|
||||
return &Config{
|
||||
Enable: true,
|
||||
Address: "localhost:8090",
|
||||
}
|
||||
}
|
||||
|
||||
// CfgOption defines a function for configuring the settings
|
||||
type CfgOption func(*Config)
|
||||
|
||||
// OverwriteDefaultConfig overwrites the default config with the new config.
|
||||
func OverwriteDefaultConfig(newCfg *Config) CfgOption {
|
||||
return func(cfg *Config) {
|
||||
*cfg = *newCfg
|
||||
}
|
||||
}
|
||||
|
||||
// Disable the grpc server by default (default enabled).
|
||||
func Disable() CfgOption {
|
||||
return func(cfg *Config) {
|
||||
cfg.Enable = false
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
Package swagger provides Swagger UI server/v2 component.
|
||||
|
||||
Example usage in commands.go:
|
||||
|
||||
swaggerServer, err := swaggerv2.New[T](
|
||||
logger.With(log.ModuleKey, "swagger"),
|
||||
deps.GlobalConfig,
|
||||
swaggerv2.CfgOption(func(cfg *swaggerv2.Config) {
|
||||
cfg.SwaggerUI = docs.SwaggerUI
|
||||
}),
|
||||
)
|
||||
*/
|
||||
package swagger
|
||||
@@ -0,0 +1,22 @@
|
||||
package swagger
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type swaggerHandler struct {
|
||||
swaggerFS fs.FS
|
||||
}
|
||||
|
||||
func (h *swaggerHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
// CORS headers
|
||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, OPTIONS")
|
||||
|
||||
if r.Method == http.MethodOptions {
|
||||
return
|
||||
}
|
||||
|
||||
http.StripPrefix("/swagger/", http.FileServer(http.FS(h.swaggerFS))).ServeHTTP(w, r)
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
package swagger
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"net/http"
|
||||
|
||||
"cosmossdk.io/core/server"
|
||||
"cosmossdk.io/core/transaction"
|
||||
"cosmossdk.io/log"
|
||||
serverv2 "cosmossdk.io/server/v2"
|
||||
)
|
||||
|
||||
var (
|
||||
_ serverv2.ServerComponent[transaction.Tx] = (*Server[transaction.Tx])(nil)
|
||||
_ serverv2.HasConfig = (*Server[transaction.Tx])(nil)
|
||||
)
|
||||
|
||||
const ServerName = "swagger"
|
||||
|
||||
// Server represents a Swagger UI server
|
||||
type Server[T transaction.Tx] struct {
|
||||
logger log.Logger
|
||||
config *Config
|
||||
cfgOptions []CfgOption
|
||||
|
||||
server *http.Server
|
||||
}
|
||||
|
||||
// New creates a new Swagger UI server
|
||||
func New[T transaction.Tx](
|
||||
logger log.Logger,
|
||||
swaggerUI fs.FS,
|
||||
config server.ConfigMap,
|
||||
cfgOptions ...CfgOption,
|
||||
) (*Server[T], error) {
|
||||
s := &Server[T]{
|
||||
logger: logger.With(log.ModuleKey, ServerName),
|
||||
cfgOptions: cfgOptions,
|
||||
}
|
||||
|
||||
serverCfg := s.Config().(*Config)
|
||||
if len(config) > 0 {
|
||||
if err := serverv2.UnmarshalSubConfig(config, s.Name(), &serverCfg); err != nil {
|
||||
return s, fmt.Errorf("failed to unmarshal config: %w", err)
|
||||
}
|
||||
}
|
||||
s.config = serverCfg
|
||||
|
||||
mux := http.NewServeMux()
|
||||
mux.Handle("/swagger/", &swaggerHandler{
|
||||
swaggerFS: swaggerUI,
|
||||
})
|
||||
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
http.Redirect(w, r, "/swagger/", http.StatusMovedPermanently)
|
||||
})
|
||||
|
||||
s.server = &http.Server{
|
||||
Addr: s.config.Address,
|
||||
Handler: mux,
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// Name returns the server's name
|
||||
func (s *Server[T]) Name() string {
|
||||
return ServerName
|
||||
}
|
||||
|
||||
// Config returns the server configuration
|
||||
func (s *Server[T]) Config() any {
|
||||
if s.config == nil || s.config.Address == "" {
|
||||
cfg := DefaultConfig()
|
||||
// overwrite the default config with the provided options
|
||||
for _, opt := range s.cfgOptions {
|
||||
opt(cfg)
|
||||
}
|
||||
|
||||
return cfg
|
||||
}
|
||||
|
||||
return s.config
|
||||
}
|
||||
|
||||
// Start starts the server
|
||||
func (s *Server[T]) Start(ctx context.Context) error {
|
||||
if !s.config.Enable {
|
||||
s.logger.Info(fmt.Sprintf("%s server is disabled via config", s.Name()))
|
||||
return nil
|
||||
}
|
||||
|
||||
s.logger.Info("starting swagger server...", "address", s.config.Address)
|
||||
if err := s.server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||||
return fmt.Errorf("failed to start swagger server: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Stop stops the server
|
||||
func (s *Server[T]) Stop(ctx context.Context) error {
|
||||
if !s.config.Enable {
|
||||
return nil
|
||||
}
|
||||
|
||||
s.logger.Info("stopping swagger server...", "address", s.config.Address)
|
||||
return s.server.Shutdown(ctx)
|
||||
}
|
||||
Reference in New Issue
Block a user