aditya.siregar 67f1dbc850 init project
2024-05-28 14:14:55 +07:00

131 lines
2.9 KiB
Go

package middlewares
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"strconv"
"strings"
"time"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
"furtuna-be/internal/common/logger"
)
func RequestMiddleware() (handler gin.HandlerFunc) {
return func(ctx *gin.Context) {
start := time.Now()
body, _ := readRequestBody(ctx.Request)
reqData := getRequestParam(ctx.Request, body)
// Check if the request contains a file
isFileUpload := false
contentType := ctx.Request.Header.Get("Content-Type")
if strings.HasPrefix(contentType, "multipart/form-data") {
isFileUpload = true
}
// Log the request if it's not a file upload
if !isFileUpload {
logger.ContextLogger(ctx).With(reqData...).Info("Request")
}
rbw := &ResponseBodyWriter{body: bytes.NewBufferString(""), ResponseWriter: ctx.Writer}
ctx.Writer = rbw
stop := time.Now()
latency := stop.Sub(start).Milliseconds()
resData := reqData
resData = append(resData, getResponseParam(rbw, latency)...)
if !isFileUpload {
logger.ContextLogger(ctx).With(resData...).Info("Response")
}
}
}
func readRequestBody(req *http.Request) ([]byte, error) {
body, err := io.ReadAll(req.Body)
if err != nil {
logger.ContextLogger(req.Context()).Error(fmt.Sprintf("Error reading body: %v", err))
return nil, err
}
req.Body = io.NopCloser(bytes.NewBuffer(body))
return body, nil
}
type ResponseBodyWriter struct {
gin.ResponseWriter
body *bytes.Buffer
}
func excludeSensitiveFields(data []interface{}) []interface{} {
var result []interface{}
for _, item := range data {
if param, ok := item.(gin.Param); ok {
// Exclude Authorization and Password fields
if param.Key != "Authorization" && param.Key != "Password" {
result = append(result, item)
}
} else {
result = append(result, item)
}
}
return result
}
func getRequestParam(req *http.Request, body []byte) []zap.Field {
var reqData []zap.Field
reqData = append(reqData, zap.Any("host", req.Host),
zap.Any("uri", req.RequestURI),
zap.Any("method", req.Method),
zap.Any("path", func() interface{} {
p := req.URL.Path
if p == "" {
p = "/"
}
return p
}()),
zap.Any("protocol", req.Proto),
zap.Any("referer", req.Referer()),
zap.Any("user_agent", req.UserAgent()),
zap.Any("headers", req.Header),
zap.Any("remote_ip", req.RemoteAddr),
zap.Any("body", excludeSensitiveFieldsFromBody(body)),
)
return reqData
}
func getResponseParam(rbw *ResponseBodyWriter, latency int64) []zap.Field {
var resData []zap.Field
resData = append(resData,
zap.Any("httpStatus", rbw.Status()),
zap.Any("body", rbw.body.String()),
zap.Any("latency_human", strconv.FormatInt(latency, 10)),
zap.Any("headers", rbw.Header()),
)
return resData
}
func excludeSensitiveFieldsFromBody(body []byte) string {
var data map[string]interface{}
if err := json.Unmarshal(body, &data); err != nil {
return string(body)
}
delete(data, "password")
result, _ := json.Marshal(data)
return string(result)
}