74 lines
2.5 KiB
Go
74 lines
2.5 KiB
Go
/*
|
|
* Licensed to the Apache Software Foundation (ASF) under one
|
|
* or more contributor license agreements. See the NOTICE file
|
|
* distributed with this work for additional information
|
|
* regarding copyright ownership. The ASF licenses this file
|
|
* to you under the Apache License, Version 2.0 (the
|
|
* "License"); you may not use this file except in compliance
|
|
* with the License. You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing,
|
|
* software distributed under the License is distributed on an
|
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
* KIND, either express or implied. See the License for the
|
|
* specific language governing permissions and limitations
|
|
* under the License.
|
|
*/
|
|
|
|
package middleware
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
|
|
"github.com/apache/answer/internal/base/handler"
|
|
"github.com/apache/answer/internal/base/reason"
|
|
"github.com/apache/answer/internal/repo/limit"
|
|
"github.com/apache/answer/pkg/encryption"
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/segmentfault/pacman/errors"
|
|
"github.com/segmentfault/pacman/log"
|
|
)
|
|
|
|
type RateLimitMiddleware struct {
|
|
limitRepo *limit.LimitRepo
|
|
}
|
|
|
|
// NewRateLimitMiddleware new rate limit middleware
|
|
func NewRateLimitMiddleware(limitRepo *limit.LimitRepo) *RateLimitMiddleware {
|
|
return &RateLimitMiddleware{
|
|
limitRepo: limitRepo,
|
|
}
|
|
}
|
|
|
|
// DuplicateRequestRejection detects and rejects duplicate requests
|
|
// It only works for the requests that post content. Such as add question, add answer, comment etc.
|
|
func (rm *RateLimitMiddleware) DuplicateRequestRejection(ctx *gin.Context, req any) (reject bool, key string) {
|
|
userID := GetLoginUserIDFromContext(ctx)
|
|
fullPath := ctx.FullPath()
|
|
reqJson, _ := json.Marshal(req)
|
|
key = encryption.MD5(fmt.Sprintf("%s:%s:%s", userID, fullPath, string(reqJson)))
|
|
var err error
|
|
reject, err = rm.limitRepo.CheckAndRecord(ctx, key)
|
|
if err != nil {
|
|
log.Errorf("check and record rate limit error: %s", err.Error())
|
|
return false, key
|
|
}
|
|
if !reject {
|
|
return false, key
|
|
}
|
|
log.Debugf("duplicate request: [%s] %s", fullPath, string(reqJson))
|
|
handler.HandleResponse(ctx, errors.BadRequest(reason.DuplicateRequestError), nil)
|
|
return true, key
|
|
}
|
|
|
|
// DuplicateRequestClear clear duplicate request record
|
|
func (rm *RateLimitMiddleware) DuplicateRequestClear(ctx *gin.Context, key string) {
|
|
err := rm.limitRepo.ClearRecord(ctx, key)
|
|
if err != nil {
|
|
log.Errorf("clear rate limit error: %s", err.Error())
|
|
}
|
|
}
|