package server import ( "context" "net/http" "slices" "strings" "time" "github.com/rs/zerolog/hlog" "github.com/rs/zerolog/log" "gitlab.com/mstarongitlab/goutils/other" "gitlab.com/mstarongitlab/linstrom/config" ) type HandlerBuilder func(http.Handler) http.Handler func ChainMiddlewares(base http.Handler, links ...HandlerBuilder) http.Handler { slices.Reverse(links) for _, f := range links { base = f(base) } return base } func ContextValsMiddleware(pairs map[any]any) HandlerBuilder { return func(h http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ctx := r.Context() for key, val := range pairs { ctx = context.WithValue(ctx, key, val) } newRequest := r.WithContext(ctx) h.ServeHTTP(w, newRequest) }) } } func LoggingMiddleware(handler http.Handler) http.Handler { return ChainMiddlewares(handler, hlog.NewHandler(log.Logger), hlog.AccessHandler(func(r *http.Request, status, size int, duration time.Duration) { if strings.HasPrefix(r.URL.Path, "/assets") { return } hlog.FromRequest(r).Info(). Str("method", r.Method). Stringer("url", r.URL). Int("status", status). Int("size", size). Dur("duration", duration). Send() }), hlog.RemoteAddrHandler("ip"), hlog.UserAgentHandler("user_agent"), hlog.RefererHandler("referer"), hlog.RequestIDHandler("req_id", "Request-Id"), ) } func passkeyIdToAccountIdTransformerMiddleware(handler http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { s := StorageFromRequest(w, r) if s == nil { return } log := hlog.FromRequest(r) passkeyId, ok := r.Context().Value(ContextKeyPasskeyUsername).(string) if !ok { other.HttpErr( w, HttpErrIdMissingContextValue, "Actor name missing", http.StatusInternalServerError, ) return } log.Debug().Bytes("passkey-bytes", []byte(passkeyId)).Msg("Id from passkey auth") acc, err := s.FindAccountByPasskeyId([]byte(passkeyId)) if err != nil { other.HttpErr( w, HttpErrIdDbFailure, "Failed to get account from storage", http.StatusInternalServerError, ) return } r = r.WithContext(context.WithValue(r.Context(), ContextKeyActorId, acc.ID)) handler.ServeHTTP(w, r) }) } func profilingAuthenticationMiddleware(handler http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.FormValue("password") != config.GlobalConfig.Admin.ProfilingPassword { other.HttpErr(w, HttpErrIdNotAuthenticated, "Bad password", http.StatusUnauthorized) return } handler.ServeHTTP(w, r) }) }