From 427d350e3d05fce30a28af395f32b25d64aab351 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20=C5=A0ediv=C3=BD?= Date: Wed, 3 Aug 2022 22:31:59 +0200 Subject: [PATCH] join static, api and proxy in server. --- internal/config/server.go | 19 ++++----- internal/server/manager.go | 82 +++++++++++++++++++++++--------------- 2 files changed, 58 insertions(+), 43 deletions(-) diff --git a/internal/config/server.go b/internal/config/server.go index 80280b9..dc8b947 100644 --- a/internal/config/server.go +++ b/internal/config/server.go @@ -1,16 +1,14 @@ package config import ( - "path" - "github.com/spf13/cobra" "github.com/spf13/viper" ) type Admin struct { - Static string - Password string - PathPrefix string + Static string + Username string + Password string } type Server struct { @@ -56,14 +54,13 @@ func (Server) Init(cmd *cobra.Command) error { return err } - cmd.PersistentFlags().String("admin.password", "", "admin password") - if err := viper.BindPFlag("admin.password", cmd.PersistentFlags().Lookup("admin.password")); err != nil { + cmd.PersistentFlags().String("admin.username", "admin", "require auth: admin username") + if err := viper.BindPFlag("admin.username", cmd.PersistentFlags().Lookup("admin.username")); err != nil { return err } - // TODO: Default in v2 will be '/admin'. - cmd.PersistentFlags().String("admin.path_prefix", "", "set custom path prefix for admin") - if err := viper.BindPFlag("admin.path_prefix", cmd.PersistentFlags().Lookup("admin.path_prefix")); err != nil { + cmd.PersistentFlags().String("admin.password", "", "require auth: admin password") + if err := viper.BindPFlag("admin.password", cmd.PersistentFlags().Lookup("admin.password")); err != nil { return err } @@ -78,6 +75,6 @@ func (s *Server) Set() { s.PProf = viper.GetBool("pprof") s.Admin.Static = viper.GetString("admin.static") + s.Admin.Username = viper.GetString("admin.username") s.Admin.Password = viper.GetString("admin.password") - s.Admin.PathPrefix = path.Join("/", path.Clean(viper.GetString("admin.path_prefix"))) } diff --git a/internal/server/manager.go b/internal/server/manager.go index e2568dd..5ec8ca4 100644 --- a/internal/server/manager.go +++ b/internal/server/manager.go @@ -3,8 +3,9 @@ package server import ( "context" "net/http" + "os" "path" - "strings" + "path/filepath" "time" "github.com/go-chi/chi" @@ -59,45 +60,62 @@ func New(ApiManager types.ApiManager, roomConfig *config.Room, config *config.Se // admin page // - // in v1 default location was at / with traefik overriding - // the actual room address. in order to keep this setting - // we set new default path prefix only without traefik - if !roomConfig.Traefik.Enabled && config.Admin.PathPrefix == "" { - config.Admin.PathPrefix = "/admin" + protected := func(next http.Handler) http.Handler { + // if auth is disabled + if config.Admin.Username == "" || config.Admin.Password == "" { + return next + } + + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + user, pass, ok := r.BasicAuth() + if !ok || user != config.Admin.Username || pass != config.Admin.Password { + w.Header().Add("WWW-Authenticate", `Basic realm="neko-rooms admin"`) + w.WriteHeader(http.StatusUnauthorized) + return + } + + next.ServeHTTP(w, r) + }) } - router.Group(func(r chi.Router) { - // handle authorization - if config.Admin.Password != "" { - r.Use(middleware.BasicAuth("neko-rooms admin", map[string]string{ - "admin": config.Admin.Password, - })) + // cache static file paths + staticFiles := map[string]struct{}{} + if config.Admin.Static != "" { + filepath.Walk(config.Admin.Static, + func(p string, info os.FileInfo, err error) error { + if err != nil { + return err + } + staticFiles[path.Clean(p)] = struct{}{} + return nil + }) + } + + // serve static files + router.Use(func(next http.Handler) http.Handler { + // if static files are disabled + if config.Admin.Static == "" { + return next } - // bind API - apiPath := path.Join("/", config.Admin.PathPrefix, "/api") - router.Route(apiPath, ApiManager.Mount) + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + filePath := path.Join(config.Admin.Static, r.URL.Path) - // serve static files - if config.Admin.Static != "" { - fs := http.FileServer(http.Dir(config.Admin.Static)) - fs = http.StripPrefix(config.Admin.PathPrefix, fs) + // check if file exists to serve it + if _, ok := staticFiles[filePath]; ok { + // serve protected assets + protected(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + http.ServeFile(w, r, filePath) + })).ServeHTTP(w, r) + return + } - router.Handle(path.Join("/", config.Admin.PathPrefix, "*"), fs) - } + next.ServeHTTP(w, r) + }) }) - if config.Admin.PathPrefix != "/" { - // redirect / to admin path prefix - router.Get("/", func(w http.ResponseWriter, r *http.Request) { - http.Redirect(w, r, strings.TrimPrefix(config.Admin.PathPrefix, "/")+"/", http.StatusTemporaryRedirect) - }) - - // redirect force admin path prefix ending with / - router.Get(config.Admin.PathPrefix, func(w http.ResponseWriter, r *http.Request) { - http.Redirect(w, r, strings.TrimPrefix(config.Admin.PathPrefix, "/")+"/", http.StatusTemporaryRedirect) - }) - } + // serve protected API + router.With(protected).Route("/api", ApiManager.Mount) // handle all remaining paths with proxy router.Handle("/*", proxyHandler)