From c13dd9addbf89f716e4ef5cfdf1d673139ffcb68 Mon Sep 17 00:00:00 2001 From: Mo Tarbin Date: Sun, 30 Jun 2024 21:41:41 -0400 Subject: Move to Donetick Org, first commit --- internal/utils/key_generator.go | 28 ++++++++++++++++ internal/utils/middleware.go | 72 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 100 insertions(+) create mode 100644 internal/utils/key_generator.go create mode 100644 internal/utils/middleware.go (limited to 'internal/utils') diff --git a/internal/utils/key_generator.go b/internal/utils/key_generator.go new file mode 100644 index 0000000..1f88be9 --- /dev/null +++ b/internal/utils/key_generator.go @@ -0,0 +1,28 @@ +package utils + +import ( + "encoding/base64" + + crand "crypto/rand" + + "donetick.com/core/logging" + "github.com/gin-gonic/gin" +) + +func GenerateInviteCode(c *gin.Context) string { + logger := logging.FromContext(c) + // Define the length of the token (in bytes). For example, 32 bytes will result in a 44-character base64-encoded token. + tokenLength := 12 + + // Generate a random byte slice. + tokenBytes := make([]byte, tokenLength) + _, err := crand.Read(tokenBytes) + if err != nil { + logger.Errorw("utility.GenerateEmailResetToken failed to generate random bytes", "err", err) + } + + // Encode the byte slice to a base64 string. + token := base64.URLEncoding.EncodeToString(tokenBytes) + + return token +} diff --git a/internal/utils/middleware.go b/internal/utils/middleware.go new file mode 100644 index 0000000..f31184b --- /dev/null +++ b/internal/utils/middleware.go @@ -0,0 +1,72 @@ +package utils + +import ( + "context" + "net/http" + "strconv" + "time" + + "donetick.com/core/config" + "github.com/gin-gonic/gin" + "github.com/ulule/limiter/v3" + "github.com/ulule/limiter/v3/drivers/store/memory" +) + +const ( + XRequestIdKey = "X-Request-ID" // request id header key +) + +func NewRateLimiter(cfg *config.Config) *limiter.Limiter { + + store := memory.NewStore() + + // rate, err := limiter.NewRateFromFormatted("10-H") + rate := limiter.Rate{ + Period: cfg.Server.RatePeriod, + Limit: int64(cfg.Server.RateLimit), + } + + // Then, create the limiter instance which takes the store and the rate as arguments. + // Now, you can give this instance to any supported middleware. + return limiter.New(store, rate) + +} + +// wrapper ratelimiter and have it as a middkewatr function: +func RateLimitMiddleware(limiter *limiter.Limiter) gin.HandlerFunc { + return func(c *gin.Context) { + // Use the IP as the key, which is the client IP. + // And set the expiration time to 10 seconds. + context, err := limiter.Get(c.Request.Context(), c.ClientIP()) + if err != nil { + panic(err) // perhaps handle this nicer + } + // Check if the client is ratelimited. + if context.Reached { + c.AbortWithStatusJSON(http.StatusTooManyRequests, gin.H{"message": "Too many requests"}) + return + } + // Add a header in response to inform the current quota. + c.Header("X-RateLimit-Limit", strconv.FormatInt(context.Limit, 10)) + // Add a header in response to inform the remaining quota. + c.Header("X-RateLimit-Remaining", strconv.FormatInt(context.Remaining, 10)) + // Add a header in response to inform the time to wait before retry. + c.Header("X-RateLimit-Reset", strconv.FormatInt(context.Reset, 10)) + c.Next() + } +} + +func TimeoutMiddleware(timeout time.Duration) gin.HandlerFunc { + return func(c *gin.Context) { + ctx, cancel := context.WithTimeout(c.Request.Context(), timeout) + + defer func() { + if ctx.Err() == context.DeadlineExceeded { + c.AbortWithStatus(http.StatusGatewayTimeout) + } + cancel() + }() + c.Request = c.Request.WithContext(ctx) + c.Next() + } +} -- cgit