101 lines
2.5 KiB
Go
101 lines
2.5 KiB
Go
|
|
package middleware
|
||
|
|
|
||
|
|
import (
|
||
|
|
"time"
|
||
|
|
|
||
|
|
"github.com/gin-gonic/gin"
|
||
|
|
"github.com/rs/zerolog/log"
|
||
|
|
)
|
||
|
|
|
||
|
|
func Logger() gin.HandlerFunc {
|
||
|
|
return func(c *gin.Context) {
|
||
|
|
start := time.Now()
|
||
|
|
path := c.Request.URL.Path
|
||
|
|
raw := c.Request.URL.RawQuery
|
||
|
|
|
||
|
|
c.Next()
|
||
|
|
|
||
|
|
latency := time.Since(start)
|
||
|
|
clientIP := c.ClientIP()
|
||
|
|
method := c.Request.Method
|
||
|
|
statusCode := c.Writer.Status()
|
||
|
|
bodySize := c.Writer.Size()
|
||
|
|
|
||
|
|
if raw != "" {
|
||
|
|
path = path + "?" + raw
|
||
|
|
}
|
||
|
|
|
||
|
|
log.Info().
|
||
|
|
Str("method", method).
|
||
|
|
Str("path", path).
|
||
|
|
Int("status", statusCode).
|
||
|
|
Dur("latency", latency).
|
||
|
|
Str("client_ip", clientIP).
|
||
|
|
Int("body_size", bodySize).
|
||
|
|
Msg("HTTP request")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func Recovery() gin.HandlerFunc {
|
||
|
|
return gin.CustomRecovery(func(c *gin.Context, recovered interface{}) {
|
||
|
|
log.Error().
|
||
|
|
Interface("error", recovered).
|
||
|
|
Str("path", c.Request.URL.Path).
|
||
|
|
Msg("Panic recovered")
|
||
|
|
c.JSON(500, gin.H{
|
||
|
|
"success": false,
|
||
|
|
"error": gin.H{
|
||
|
|
"code": "INTERNAL_ERROR",
|
||
|
|
"message": "Internal server error",
|
||
|
|
},
|
||
|
|
})
|
||
|
|
c.Abort()
|
||
|
|
})
|
||
|
|
}
|
||
|
|
|
||
|
|
func CORS(allowedOrigins []string) gin.HandlerFunc {
|
||
|
|
return func(c *gin.Context) {
|
||
|
|
origin := c.Request.Header.Get("Origin")
|
||
|
|
|
||
|
|
allowed := false
|
||
|
|
for _, o := range allowedOrigins {
|
||
|
|
if o == "*" || o == origin {
|
||
|
|
allowed = true
|
||
|
|
break
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if allowed {
|
||
|
|
c.Writer.Header().Set("Access-Control-Allow-Origin", origin)
|
||
|
|
}
|
||
|
|
|
||
|
|
c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
|
||
|
|
c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With")
|
||
|
|
c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS, GET, PUT, PATCH, DELETE")
|
||
|
|
|
||
|
|
if c.Request.Method == "OPTIONS" {
|
||
|
|
c.AbortWithStatus(204)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
c.Next()
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func SecurityHeaders() gin.HandlerFunc {
|
||
|
|
return func(c *gin.Context) {
|
||
|
|
c.Writer.Header().Set("X-Content-Type-Options", "nosniff")
|
||
|
|
c.Writer.Header().Set("X-Frame-Options", "DENY")
|
||
|
|
c.Writer.Header().Set("X-XSS-Protection", "1; mode=block")
|
||
|
|
c.Writer.Header().Set("Referrer-Policy", "strict-origin-when-cross-origin")
|
||
|
|
c.Writer.Header().Set("Content-Security-Policy", "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; connect-src 'self'")
|
||
|
|
c.Next()
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func RateLimiter(requests int, window time.Duration) gin.HandlerFunc {
|
||
|
|
return func(c *gin.Context) {
|
||
|
|
c.Next()
|
||
|
|
}
|
||
|
|
}
|