package middleware import ( "bytes" "encoding/json" "io" "time" "cardverse/internal/pkg/logger" "github.com/gin-gonic/gin" ) // Logger mencatat setiap request: method, path, status, durasi, IP, dan body request // (kalau JSON) supaya gampang di-debug. Field sensitif seperti password, token, dsb // OTOMATIS disensor oleh package logger (lihat internal/pkg/logger), jadi aman // dipasang bahkan di endpoint login/register. func Logger() gin.HandlerFunc { return func(c *gin.Context) { start := time.Now() bodyFields := readBodyForLog(c) c.Next() duration := time.Since(start) status := c.Writer.Status() fields := logger.Fields{ "method": c.Request.Method, "path": c.Request.URL.Path, "status": status, "duration": duration.String(), "ip": c.ClientIP(), } if bodyFields != nil { fields["body"] = bodyFields } if len(c.Errors) > 0 { fields["gin_errors"] = c.Errors.String() } switch { case status >= 500: logger.Error("request selesai", fields) case status >= 400: logger.Warn("request selesai", fields) default: logger.Info("request selesai", fields) } } } // readBodyForLog membaca body request (kalau berupa JSON) untuk keperluan log, // lalu mengembalikan body itu ke request supaya handler asli tetap bisa membacanya // seperti biasa. Field sensitif di dalam body (misal "password") akan disensor // otomatis oleh package logger saat log ditulis, bukan di sini. func readBodyForLog(c *gin.Context) map[string]interface{} { if c.Request.Body == nil { return nil } if c.Request.Method != "POST" && c.Request.Method != "PUT" && c.Request.Method != "PATCH" { return nil } bodyBytes, err := io.ReadAll(c.Request.Body) if err != nil { return nil } // kembalikan body supaya bisa dibaca lagi oleh ShouldBindJSON di handler c.Request.Body = io.NopCloser(bytes.NewBuffer(bodyBytes)) var parsed map[string]interface{} if err := json.Unmarshal(bodyBytes, &parsed); err != nil { return nil // bukan JSON (atau body kosong), tidak masalah, cukup skip } return parsed }