package routes import ( "dashboard/logger" "dashboard/models" "dashboard/pkg/jwt" "dashboard/pkg/rate" "dashboard/settings" "net" "net/http" "net/http/httputil" "os" "runtime/debug" "strings" "time" "github.com/gin-gonic/gin" "go.uber.org/zap" ) func GinLogger(log *logger.Logger) gin.HandlerFunc { return func(c *gin.Context) { start := time.Now() path := c.Request.URL.Path query := c.Request.URL.RawQuery c.Next() cost := time.Since(start) log.Info(path, zap.Int("status", c.Writer.Status()), zap.String("method", c.Request.Method), zap.String("path", path), zap.String("query", query), zap.String("ip", c.ClientIP()), zap.String("user-agent", c.Request.UserAgent()), zap.String("errors", c.Errors.ByType(gin.ErrorTypePrivate).String()), zap.Duration("cost", cost), ) } } func GinRecovery(log *logger.Logger, stack bool) gin.HandlerFunc { return func(c *gin.Context) { defer func() { if err := recover(); err != nil { var brokenPipe bool if ne, ok := err.(*net.OpError); ok { if se, ok := ne.Err.(*os.SyscallError); ok { expectStr := strings.ToLower(se.Error()) if strings.Contains(expectStr, "broken pipe") || strings.Contains(expectStr, "connection reset by peer") { brokenPipe = true } } } httpRequest, _ := httputil.DumpRequest(c.Request, false) if brokenPipe { log.Error(c.Request.URL.Path, zap.Any("error", err), zap.String("request", string(httpRequest)), ) c.Error(err.(error)) c.Abort() return } if stack { log.Error("[Recovery from panic]", zap.Any("error", err), zap.String("request", string(httpRequest)), zap.String("stack", string(debug.Stack())), ) } else { log.Error("[Recovery from panic]", zap.Any("error", err), zap.String("request", string(httpRequest)), ) } c.AbortWithStatus(http.StatusInternalServerError) } }() c.Next() } } func GinLog(log *logger.Logger) gin.HandlerFunc { return func(c *gin.Context) { c.Set(models.GinContxtLog, log) c.Next() } } func GinJwtAuthor(jwtC *jwt.Jwt) appHandler { return func(c *gin.Context) error { authHeader, err := c.Cookie(models.GinAuthorKey) if err != nil || !strings.HasPrefix(authHeader, models.GinAuthorPrefixKey) { c.Abort() return &models.BaseError{ Code: http.StatusUnauthorized, Msg: "Missing or invalid token", } } tokenStr := strings.TrimPrefix(authHeader, models.GinAuthorPrefixKey) _, err = jwtC.ParseToken(tokenStr) if err != nil { c.Abort() if jwt.ErrorIsJwtExpired(err) { return &models.BaseError{ Code: http.StatusUnauthorized, Msg: "Expired token", } } return &models.BaseError{ Code: http.StatusUnauthorized, Msg: "Invalid token", } } c.Next() return nil } } func GinRateLimit(rateC settings.RateLimitConfig) appHandler { lrate, err := rate.New(rate.WithCapacity(rateC.Capacity), rate.WithFillInterval(time.Duration(rateC.FillInterval)), ) if err != nil { panic(err) } return func(c *gin.Context) error { if lrate.Available() ==0 { return &models.BaseError{ Code: http.StatusServiceUnavailable, Msg: "Exceeded rate limit", } } c.Next() return nil } }