init project
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
package middlewares
|
||||
|
||||
import (
|
||||
"furtuna-be/internal/common/mycontext"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"furtuna-be/internal/repository"
|
||||
)
|
||||
|
||||
func AuthorizationMiddleware(cryp repository.Crypto) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
// Get the JWT token from the header
|
||||
tokenString := c.GetHeader("Authorization")
|
||||
if tokenString == "" {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "Authorization header is required"})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
tokenString = strings.TrimPrefix(tokenString, "Bearer ")
|
||||
|
||||
claims, err := cryp.ParseAndValidateJWT(tokenString)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid JWT token"})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
customCtx, err := mycontext.NewMyContext(c.Request.Context(), claims)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "error initialize context"})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
c.Set("myCtx", customCtx)
|
||||
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
func SuperAdminMiddleware() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
ctx, exists := c.Get("myCtx")
|
||||
if !exists {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized"})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
myCtx, ok := ctx.(*mycontext.MyContextImpl)
|
||||
if !ok || !myCtx.IsSuperAdmin() {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized"})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package middlewares
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"furtuna-be/internal/common/logger"
|
||||
)
|
||||
|
||||
func Cors() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
c.Header("Access-Control-Allow-Origin", "*")
|
||||
c.Header("Access-Control-Allow-Credentials", "true")
|
||||
c.Header("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, Accept, origin, Referer, Cache-Control, X-Requested-With")
|
||||
c.Header("Access-Control-Allow-Methods", "POST,HEAD,PATCH, OPTIONS, GET, PUT, DELETE")
|
||||
c.Header("Vary", "Origin")
|
||||
|
||||
if c.Request.Method == "OPTIONS" {
|
||||
c.AbortWithStatus(204)
|
||||
return
|
||||
}
|
||||
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
func LogCorsError() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
c.Next()
|
||||
|
||||
// check if the request was blocked due to CORS
|
||||
if c.Writer.Status() == http.StatusForbidden && c.Writer.Header().Get("Access-Control-Allow-Origin") == "" {
|
||||
logger.GetLogger().Error(fmt.Sprintf("CORS error: %s", c.Writer.Header().Get("Access-Control-Allow-Origin")))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package middlewares
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"furtuna-be/internal/common/request"
|
||||
)
|
||||
|
||||
func Logger() gin.HandlerFunc {
|
||||
return gin.LoggerWithFormatter(func(param gin.LogFormatterParams) string {
|
||||
var parsedReqInfo request.RequestInfo
|
||||
|
||||
reqInfo, exists := param.Keys[request.ReqInfoKey]
|
||||
if exists {
|
||||
parsedReqInfo = reqInfo.(request.RequestInfo)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s - [HTTP] TraceId: %s; UserId: %d; Method: %s; Path: %s; Status: %d, Latency: %s;\n\n",
|
||||
param.TimeStamp.Format(time.RFC1123),
|
||||
parsedReqInfo.TraceId,
|
||||
parsedReqInfo.UserId,
|
||||
param.Method,
|
||||
param.Path,
|
||||
param.StatusCode,
|
||||
param.Latency,
|
||||
)
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
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)
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package middlewares
|
||||
|
||||
import (
|
||||
"furtuna-be/internal/common/request"
|
||||
"furtuna-be/internal/constants"
|
||||
"furtuna-be/internal/utils/generator"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func Trace() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
traceId := c.Request.Header.Get("Trace-Id")
|
||||
if traceId == "" {
|
||||
traceId = generator.GenerateUUID()
|
||||
}
|
||||
|
||||
request.SetTraceId(c, traceId)
|
||||
c.Set(constants.ContextRequestID, traceId)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user