github.com/hellofresh/janus@v0.0.0-20230925145208-ce8de8183c67/pkg/web/provider.go (about)

     1  package web
     2  
     3  import (
     4  	"fmt"
     5  	"net/http"
     6  	"net/http/pprof"
     7  
     8  	chiMiddleware "github.com/go-chi/chi/middleware"
     9  	"github.com/hellofresh/janus/pkg/api"
    10  	"github.com/hellofresh/janus/pkg/config"
    11  	httpErrors "github.com/hellofresh/janus/pkg/errors"
    12  	"github.com/hellofresh/janus/pkg/jwt"
    13  	"github.com/hellofresh/janus/pkg/middleware"
    14  	obs "github.com/hellofresh/janus/pkg/observability"
    15  	"github.com/hellofresh/janus/pkg/plugin"
    16  	"github.com/hellofresh/janus/pkg/router"
    17  	"github.com/rs/cors"
    18  	log "github.com/sirupsen/logrus"
    19  )
    20  
    21  // Server represents the web server
    22  type Server struct {
    23  	Port              int
    24  	Credentials       config.Credentials
    25  	TLS               config.TLS
    26  	ConfigurationChan chan api.ConfigurationMessage
    27  	apiHandler        *APIHandler
    28  	profilingEnabled  bool
    29  	profilingPublic   bool
    30  }
    31  
    32  // New creates a new web server
    33  func New(opts ...Option) *Server {
    34  	cfgChan := make(chan api.ConfigurationMessage)
    35  	s := Server{
    36  		ConfigurationChan: cfgChan,
    37  		apiHandler:        NewAPIHandler(cfgChan),
    38  	}
    39  
    40  	for _, opt := range opts {
    41  		opt(&s)
    42  	}
    43  
    44  	return &s
    45  }
    46  
    47  // Start creates a router and serves requests async
    48  func (s *Server) Start() error {
    49  	log.Info("Janus Admin API starting...")
    50  	router.DefaultOptions.NotFoundHandler = httpErrors.NotFound
    51  	r := router.NewChiRouterWithOptions(router.DefaultOptions)
    52  	go s.listenAndServe(r)
    53  
    54  	s.AddRoutes(r)
    55  	plugin.EmitEvent(plugin.AdminAPIStartupEvent, plugin.OnAdminAPIStartup{Router: r})
    56  
    57  	return nil
    58  }
    59  
    60  // Stop stops the server
    61  func (s *Server) Stop() {
    62  	close(s.ConfigurationChan)
    63  }
    64  
    65  // AddRoutes adds the admin routes
    66  func (s *Server) AddRoutes(r router.Router) {
    67  	// create authentication for Janus
    68  	guard := jwt.NewGuard(s.Credentials)
    69  	r.Use(
    70  		chiMiddleware.StripSlashes,
    71  		chiMiddleware.DefaultCompress,
    72  		middleware.NewLogger().Handler,
    73  		middleware.NewRecovery(httpErrors.RecoveryHandler),
    74  		cors.New(cors.Options{
    75  			AllowedOrigins:   []string{"*"},
    76  			AllowedHeaders:   []string{"*"},
    77  			AllowedMethods:   []string{"GET", "POST", "PUT", "PATCH", "DELETE"},
    78  			AllowCredentials: true,
    79  		}).Handler,
    80  	)
    81  
    82  	s.addInternalPublicRoutes(r)
    83  	s.addInternalAuthRoutes(r, guard)
    84  	s.addInternalRoutes(r, guard)
    85  }
    86  
    87  func (s *Server) addInternalPublicRoutes(r router.Router) {
    88  	r.GET("/", Home())
    89  	r.GET("/status", NewOverviewHandler(s.apiHandler.Cfgs))
    90  	r.GET("/status/{name}", NewStatusHandler(s.apiHandler.Cfgs))
    91  	if obs.PrometheusExporter != nil {
    92  		r.Any("/metrics", obs.PrometheusExporter.ServeHTTP)
    93  	}
    94  }
    95  
    96  func (s *Server) addInternalAuthRoutes(r router.Router, guard jwt.Guard) {
    97  	handlers := jwt.Handler{Guard: guard}
    98  	r.POST("/login", handlers.Login(s.Credentials))
    99  	authGroup := r.Group("/auth")
   100  	{
   101  		authGroup.GET("/refresh_token", handlers.Refresh())
   102  	}
   103  }
   104  
   105  func (s *Server) addInternalRoutes(r router.Router, guard jwt.Guard) {
   106  	log.Debug("Loading API Endpoints")
   107  
   108  	// APIs endpoints
   109  	groupAPI := r.Group("/apis")
   110  	groupAPI.Use(jwt.NewMiddleware(guard).Handler)
   111  	{
   112  		groupAPI.GET("/", s.apiHandler.Get())
   113  		groupAPI.GET("/{name}", s.apiHandler.GetBy())
   114  		groupAPI.POST("/", s.apiHandler.Post())
   115  		groupAPI.PUT("/{name}", s.apiHandler.PutBy())
   116  		groupAPI.DELETE("/{name}", s.apiHandler.DeleteBy())
   117  	}
   118  
   119  	if s.profilingEnabled {
   120  		groupProfiler := r.Group("/debug/pprof")
   121  		if !s.profilingPublic {
   122  			groupProfiler.Use(jwt.NewMiddleware(guard).Handler)
   123  		}
   124  		{
   125  			groupProfiler.GET("/*", pprof.Index)
   126  			groupProfiler.GET("/cmdline", pprof.Cmdline)
   127  			groupProfiler.GET("/profile", pprof.Profile)
   128  			groupProfiler.GET("/symbol", pprof.Symbol)
   129  			groupProfiler.GET("/trace", pprof.Trace)
   130  		}
   131  	}
   132  }
   133  
   134  func (s *Server) listenAndServe(handler http.Handler) error {
   135  	address := fmt.Sprintf(":%v", s.Port)
   136  
   137  	log.Info("Janus Admin API started")
   138  	if s.TLS.IsHTTPS() {
   139  		addressTLS := fmt.Sprintf(":%v", s.TLS.Port)
   140  		if s.TLS.Redirect {
   141  			go func() {
   142  				log.WithField("address", address).Info("Listening HTTP redirects to HTTPS")
   143  				log.Fatal(http.ListenAndServe(address, RedirectHTTPS(s.TLS.Port)))
   144  			}()
   145  		}
   146  
   147  		log.WithField("address", addressTLS).Info("Listening HTTPS")
   148  		return http.ListenAndServeTLS(addressTLS, s.TLS.CertFile, s.TLS.KeyFile, handler)
   149  	}
   150  
   151  	log.WithField("address", address).Info("Certificate and certificate key were not found, defaulting to HTTP")
   152  	return http.ListenAndServe(address, handler)
   153  }