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 }