mirror of
https://github.com/WJQSERVER-STUDIO/ghproxy.git
synced 2026-02-03 08:11:11 +08:00
24w24a
This commit is contained in:
parent
0ce5c2c11c
commit
32584f9516
10 changed files with 97 additions and 10 deletions
20
CHANGELOG.md
20
CHANGELOG.md
|
|
@ -1,5 +1,25 @@
|
||||||
# 更新日志
|
# 更新日志
|
||||||
|
|
||||||
|
24w24a
|
||||||
|
---
|
||||||
|
- PRE-RELEASE: 此版本是v1.7.5的预发布版本,请勿在生产环境中使用
|
||||||
|
- ADD: `Rate`模块加入`IP`速率限制,可限制单个IP的请求速率
|
||||||
|
- CHANGE: 处理积攒的依赖库更新,更新如下依赖库:
|
||||||
|
- **github.com/gabriel-vasile/mimetype**: 从 v1.4.6 升级到 v1.4.7
|
||||||
|
- **github.com/go-playground/validator/v10**: 从 v10.22.1 升级到 v10.23.0
|
||||||
|
- **github.com/klauspost/cpuid/v2**: 从 v2.2.8 升级到 v2.2.9
|
||||||
|
- **github.com/onsi/ginkgo/v2**: 从 v2.21.0 升级到 v2.22.0
|
||||||
|
- **golang.org/x/arch**: 从 v0.11.0 升级到 v0.12.0
|
||||||
|
- **golang.org/x/crypto**: 从 v0.28.0 升级到 v0.29.0
|
||||||
|
- **golang.org/x/exp**: 从 v0.0.0-20241009180824-f66d83c29e7c 升级到 v0.0.0-20241108190413-2d47ceb2692f
|
||||||
|
- **golang.org/x/mod**: 从 v0.21.0 升级到 v0.22.0
|
||||||
|
- **golang.org/x/net**: 从 v0.30.0 升级到 v0.31.0
|
||||||
|
- **golang.org/x/sync**: 从 v0.8.0 升级到 v0.9.0
|
||||||
|
- **golang.org/x/sys**: 从 v0.26.0 升级到 v0.27.0
|
||||||
|
- **golang.org/x/text**: 从 v0.19.0 升级到 v0.20.0
|
||||||
|
- **golang.org/x/tools**: 从 v0.26.0 升级到 v0.27.0
|
||||||
|
- **google.golang.org/protobuf**: 从 v1.35.1 升级到 v1.35.2
|
||||||
|
|
||||||
v1.7.4
|
v1.7.4
|
||||||
---
|
---
|
||||||
- CHANGE: 对二进制文件部署脚本进行优化
|
- CHANGE: 对二进制文件部署脚本进行优化
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,7 @@ type WhitelistConfig struct {
|
||||||
|
|
||||||
type RateLimitConfig struct {
|
type RateLimitConfig struct {
|
||||||
Enabled bool `toml:"enabled"`
|
Enabled bool `toml:"enabled"`
|
||||||
|
RateMethod string `toml:"rateMethod"`
|
||||||
RatePerMinute int `toml:"ratePerMinute"`
|
RatePerMinute int `toml:"ratePerMinute"`
|
||||||
Burst int `toml:"burst"`
|
Burst int `toml:"burst"`
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,5 +30,6 @@ whitelistFile = "/data/ghproxy/config/whitelist.json"
|
||||||
|
|
||||||
[rateLimit]
|
[rateLimit]
|
||||||
enabled = false
|
enabled = false
|
||||||
|
rateMrthod = "total" # "ip" or "total"
|
||||||
ratePerMinute = 180
|
ratePerMinute = 180
|
||||||
burst = 5
|
burst = 5
|
||||||
|
|
|
||||||
|
|
@ -30,5 +30,6 @@ whitelistFile = "/usr/local/ghproxy/config/whitelist.json"
|
||||||
|
|
||||||
[rateLimit]
|
[rateLimit]
|
||||||
enabled = false
|
enabled = false
|
||||||
|
rateMrthod = "total" # "ip" or "total"
|
||||||
ratePerMinute = 180
|
ratePerMinute = 180
|
||||||
burst = 5
|
burst = 5
|
||||||
|
|
|
||||||
|
|
@ -30,5 +30,6 @@ whitelistFile = "/data/ghproxy/config/whitelist.json"
|
||||||
|
|
||||||
[rateLimit]
|
[rateLimit]
|
||||||
enabled = false
|
enabled = false
|
||||||
|
rateMrthod = "total" # "ip" or "total"
|
||||||
ratePerMinute = 180
|
ratePerMinute = 180
|
||||||
burst = 5
|
burst = 5
|
||||||
|
|
|
||||||
2
go.mod
2
go.mod
|
|
@ -23,7 +23,7 @@ require (
|
||||||
github.com/go-playground/validator/v10 v10.23.0 // indirect
|
github.com/go-playground/validator/v10 v10.23.0 // indirect
|
||||||
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
|
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
|
||||||
github.com/goccy/go-json v0.10.3 // indirect
|
github.com/goccy/go-json v0.10.3 // indirect
|
||||||
github.com/google/pprof v0.0.0-20241101162523-b92577c0c142 // indirect
|
github.com/google/pprof v0.0.0-20241122213907-cbe949e5a41b // indirect
|
||||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||||
github.com/json-iterator/go v1.1.12 // indirect
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
|
|
|
||||||
2
go.sum
2
go.sum
|
|
@ -46,6 +46,8 @@ github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN
|
||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
github.com/google/pprof v0.0.0-20241101162523-b92577c0c142 h1:sAGdeJj0bnMgUNVeUpp6AYlVdCt3/GdI3pGRqsNSQLs=
|
github.com/google/pprof v0.0.0-20241101162523-b92577c0c142 h1:sAGdeJj0bnMgUNVeUpp6AYlVdCt3/GdI3pGRqsNSQLs=
|
||||||
github.com/google/pprof v0.0.0-20241101162523-b92577c0c142/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
|
github.com/google/pprof v0.0.0-20241101162523-b92577c0c142/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
|
||||||
|
github.com/google/pprof v0.0.0-20241122213907-cbe949e5a41b h1:SXO0REt4iu865upYCk8aKBBJQ4BqoE0ReP23ClMu60s=
|
||||||
|
github.com/google/pprof v0.0.0-20241122213907-cbe949e5a41b/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
|
||||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||||
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||||
|
|
|
||||||
11
main.go
11
main.go
|
|
@ -22,8 +22,9 @@ var (
|
||||||
router *gin.Engine
|
router *gin.Engine
|
||||||
configfile = "/data/ghproxy/config/config.toml"
|
configfile = "/data/ghproxy/config/config.toml"
|
||||||
cfgfile string
|
cfgfile string
|
||||||
limiter *rate.RateLimiter
|
|
||||||
version string
|
version string
|
||||||
|
limiter *rate.RateLimiter
|
||||||
|
iplimiter *rate.IPRateLimiter
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
@ -68,7 +69,13 @@ func setupApi(cfg *config.Config, router *gin.Engine, version string) {
|
||||||
|
|
||||||
func setupRateLimit(cfg *config.Config) {
|
func setupRateLimit(cfg *config.Config) {
|
||||||
if cfg.RateLimit.Enabled {
|
if cfg.RateLimit.Enabled {
|
||||||
|
if cfg.RateLimit.RateMethod == "ip" {
|
||||||
|
iplimiter = rate.NewIPRateLimiter(cfg.RateLimit.RatePerMinute, cfg.RateLimit.Burst, 1*time.Minute)
|
||||||
|
} else if cfg.RateLimit.RateMethod == "total" {
|
||||||
limiter = rate.New(cfg.RateLimit.RatePerMinute, cfg.RateLimit.Burst, 1*time.Minute)
|
limiter = rate.New(cfg.RateLimit.RatePerMinute, cfg.RateLimit.Burst, 1*time.Minute)
|
||||||
|
} else {
|
||||||
|
logError("Invalid RateLimit Method: %s", cfg.RateLimit.RateMethod)
|
||||||
|
}
|
||||||
logInfo("Rate Limit Loaded")
|
logInfo("Rate Limit Loaded")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -106,7 +113,7 @@ func init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
router.NoRoute(func(c *gin.Context) {
|
router.NoRoute(func(c *gin.Context) {
|
||||||
proxy.NoRouteHandler(cfg, limiter)(c)
|
proxy.NoRouteHandler(cfg, limiter, iplimiter)(c)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,12 +33,24 @@ var exps = []*regexp.Regexp{
|
||||||
regexp.MustCompile(`^(?:https?://)?gist\.github(?:usercontent|)\.com/([^/]+)/.+?/.+`),
|
regexp.MustCompile(`^(?:https?://)?gist\.github(?:usercontent|)\.com/([^/]+)/.+?/.+`),
|
||||||
}
|
}
|
||||||
|
|
||||||
func NoRouteHandler(cfg *config.Config, limiter *rate.RateLimiter) gin.HandlerFunc {
|
func NoRouteHandler(cfg *config.Config, limiter *rate.RateLimiter, iplimiter *rate.IPRateLimiter) gin.HandlerFunc {
|
||||||
return func(c *gin.Context) {
|
return func(c *gin.Context) {
|
||||||
// 限制访问频率
|
// 限制访问频率
|
||||||
if cfg.RateLimit.Enabled {
|
if cfg.RateLimit.Enabled {
|
||||||
logInfo("Rate_Limit Enabled")
|
|
||||||
if !limiter.Allow() {
|
var allowed bool
|
||||||
|
|
||||||
|
switch cfg.RateLimit.RateMethod {
|
||||||
|
case "ip":
|
||||||
|
allowed = iplimiter.Allow(c.ClientIP())
|
||||||
|
case "total":
|
||||||
|
allowed = limiter.Allow()
|
||||||
|
default:
|
||||||
|
logWarning("Invalid RateLimit Method")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !allowed {
|
||||||
c.JSON(http.StatusTooManyRequests, gin.H{"error": "Too Many Requests"})
|
c.JSON(http.StatusTooManyRequests, gin.H{"error": "Too Many Requests"})
|
||||||
logWarning("%s %s %s %s %s 429-TooManyRequests", c.ClientIP(), c.Request.Method, c.Request.URL.RequestURI(), c.Request.Header.Get("User-Agent"), c.Request.Proto)
|
logWarning("%s %s %s %s %s 429-TooManyRequests", c.ClientIP(), c.Request.Method, c.Request.URL.RequestURI(), c.Request.Header.Get("User-Agent"), c.Request.Proto)
|
||||||
return
|
return
|
||||||
|
|
|
||||||
42
rate/rate.go
42
rate/rate.go
|
|
@ -1,15 +1,33 @@
|
||||||
package rate
|
package rate
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"ghproxy/logger"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/time/rate"
|
"golang.org/x/time/rate"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// 日志输出
|
||||||
|
var (
|
||||||
|
logw = logger.Logw
|
||||||
|
logInfo = logger.LogInfo
|
||||||
|
logWarning = logger.LogWarning
|
||||||
|
logError = logger.LogError
|
||||||
|
)
|
||||||
|
|
||||||
|
// 总体限流器
|
||||||
type RateLimiter struct {
|
type RateLimiter struct {
|
||||||
limiter *rate.Limiter
|
limiter *rate.Limiter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 基于IP的限流器
|
||||||
|
type IPRateLimiter struct {
|
||||||
|
limiters map[string]*RateLimiter
|
||||||
|
limit int
|
||||||
|
burst int
|
||||||
|
duration time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
func New(limit int, burst int, duration time.Duration) *RateLimiter {
|
func New(limit int, burst int, duration time.Duration) *RateLimiter {
|
||||||
return &RateLimiter{
|
return &RateLimiter{
|
||||||
limiter: rate.NewLimiter(rate.Limit(float64(limit)/duration.Seconds()), burst),
|
limiter: rate.NewLimiter(rate.Limit(float64(limit)/duration.Seconds()), burst),
|
||||||
|
|
@ -19,3 +37,27 @@ func New(limit int, burst int, duration time.Duration) *RateLimiter {
|
||||||
func (rl *RateLimiter) Allow() bool {
|
func (rl *RateLimiter) Allow() bool {
|
||||||
return rl.limiter.Allow()
|
return rl.limiter.Allow()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewIPRateLimiter(limit int, burst int, duration time.Duration) *IPRateLimiter {
|
||||||
|
return &IPRateLimiter{
|
||||||
|
limiters: make(map[string]*RateLimiter),
|
||||||
|
limit: limit,
|
||||||
|
burst: burst,
|
||||||
|
duration: duration,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rl *IPRateLimiter) Allow(ip string) bool {
|
||||||
|
if ip == "" {
|
||||||
|
logWarning("empty ip")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
limiter, ok := rl.limiters[ip]
|
||||||
|
if !ok {
|
||||||
|
// 创建新的 RateLimiter 并存储
|
||||||
|
limiter = New(rl.limit, rl.burst, rl.duration)
|
||||||
|
rl.limiters[ip] = limiter
|
||||||
|
}
|
||||||
|
return limiter.Allow()
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue