init project

This commit is contained in:
aditya.siregar
2024-05-28 14:14:55 +07:00
commit 67f1dbc850
141 changed files with 16879 additions and 0 deletions
+63
View File
@@ -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()
}
}
+38
View File
@@ -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")))
}
}
}
+31
View File
@@ -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,
)
})
}
+130
View File
@@ -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)
}
+20
View File
@@ -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)
}
}