package middleware import ( "fmt" "net/http" "time" apierrors "git.yuntuo.hk/k2safe/go-omni/errors" "git.yuntuo.hk/k2safe/go-omni/httpx" "git.yuntuo.hk/k2safe/go-omni/logx" ) // RequestIDKey is the value key used by the request ID middleware. const RequestIDKey = "go-omni.requestId" // RequestID reads or creates a request ID and returns it in the response // header. It is a transport-neutral GoOmni middleware. func RequestID(header string) httpx.Middleware { return func(next httpx.Handler) httpx.Handler { return func(ctx *httpx.Context) error { requestID := ctx.Header(header) if requestID == "" { requestID = newRequestID() } ctx.Set(RequestIDKey, requestID) ctx.SetResponseHeader(header, requestID) ctx.WithContext(logx.WithRequestID(ctx.Context(), requestID)) return next(ctx) } } } // Recovery converts panics from handlers and middleware into the standard // internal error. The network adapter does not need a Gin-specific recovery // hook for this behavior. func Recovery() httpx.Middleware { return func(next httpx.Handler) httpx.Handler { return func(ctx *httpx.Context) (err error) { defer func() { if recovered := recover(); recovered != nil { err = apierrors.Internal(fmt.Errorf("panic recovered: %v", recovered)) } }() return next(ctx) } } } // AccessLog records one structured event after each request. It deliberately // lives in GoOmni instead of using a router-specific logger so business // applications get the same log format with every transport adapter. func AccessLog() httpx.Middleware { return func(next httpx.Handler) httpx.Handler { return func(ctx *httpx.Context) error { started := time.Now() err := next(ctx) status := ctx.Status() if err != nil { status = apierrors.From(err).Status } logx.InfoContext(ctx.Context(), "http.request", "method", ctx.Request().Method, "path", ctx.Request().URL.Path, "route", httpx.RoutePattern(ctx.Context()), "status", status, "durationMs", time.Since(started).Seconds()*1000, "clientIp", clientIP(ctx.Request()), ) return err } } } func clientIP(request *http.Request) string { if request == nil { return "" } if value := request.Header.Get("X-Forwarded-For"); value != "" { return value } return request.RemoteAddr }