package server import ( "fmt" "io/fs" "net/http" "strings" "time" "github.com/go-chi/chi/middleware" "github.com/navidrome/navidrome/log" "github.com/unrolled/secure" ) func requestLogger(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { scheme := "http" if r.TLS != nil { scheme = "https" } start := time.Now() ww := middleware.NewWrapResponseWriter(w, r.ProtoMajor) next.ServeHTTP(ww, r) status := ww.Status() message := fmt.Sprintf("HTTP: %s %s://%s%s", r.Method, scheme, r.Host, r.RequestURI) logArgs := []interface{}{ r.Context(), message, "remoteAddr", r.RemoteAddr, "elapsedTime", time.Since(start), "httpStatus", ww.Status(), "responseSize", ww.BytesWritten(), } if log.CurrentLevel() >= log.LevelDebug { logArgs = append(logArgs, "userAgent", r.UserAgent()) } switch { case status >= 500: log.Error(logArgs...) case status >= 400: log.Warn(logArgs...) default: log.Debug(logArgs...) } }) } func injectLogger(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ctx := r.Context() ctx = log.NewContext(r.Context(), "requestId", ctx.Value(middleware.RequestIDKey)) next.ServeHTTP(w, r.WithContext(ctx)) }) } func robotsTXT(fs fs.FS) func(next http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if strings.HasSuffix(r.URL.Path, "/robots.txt") { r.URL.Path = "/robots.txt" http.FileServer(http.FS(fs)).ServeHTTP(w, r) } else { next.ServeHTTP(w, r) } }) } } func secureMiddleware() func(h http.Handler) http.Handler { sec := secure.New(secure.Options{ ContentTypeNosniff: true, FrameDeny: true, ReferrerPolicy: "same-origin", FeaturePolicy: "autoplay 'none'; camera: 'none'; display-capture 'none'; microphone: 'none'; usb: 'none'", //ContentSecurityPolicy: "script-src 'self' 'unsafe-inline'", }) return sec.Handler }