156 lines
3.2 KiB
Go
156 lines
3.2 KiB
Go
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
|
|
}
|
|
}
|