Compare commits
75 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
00ae38753e | ||
|
|
2aa665d89a | ||
|
|
17a2ba173d | ||
|
|
a0e5846e11 | ||
|
|
972baee564 | ||
|
|
a281d4c779 | ||
|
|
e4252d0596 | ||
|
|
de65889a4d | ||
|
|
90b9c69dad | ||
|
|
acd38f4fe0 | ||
|
|
83e6b78a93 | ||
|
|
8371f9564f | ||
|
|
546a8ca981 | ||
|
|
be6314bd53 | ||
|
|
28331e9ec5 | ||
|
|
51f179f9e9 | ||
|
|
33eb0e2d34 | ||
|
|
9bfca20d11 | ||
|
|
ede418420d | ||
|
|
243172c988 | ||
|
|
a8d2b0700c | ||
|
|
891ce86101 | ||
|
|
1bf4eca13d | ||
|
|
e01028df08 | ||
|
|
708f25c02b | ||
|
|
31678b243c | ||
|
|
0944b81dcb | ||
|
|
723b849ee0 | ||
|
|
a537c09491 | ||
|
|
6f050d38ac | ||
|
|
d8c57b7191 | ||
|
|
a77f265a17 | ||
|
|
7b9a18225a | ||
|
|
44105fc0cf | ||
|
|
3d742960cc | ||
|
|
0fb7ee3679 | ||
|
|
efe734d976 | ||
|
|
890dc067b9 | ||
|
|
a43f1f20f6 | ||
|
|
e59c118475 | ||
|
|
d1f862e799 | ||
|
|
d5e3b7fc23 | ||
|
|
8dea98f795 | ||
|
|
b1a60017e4 | ||
|
|
96bd4a2c18 | ||
|
|
43c576bd82 | ||
|
|
580865d082 | ||
|
|
c2af045019 | ||
|
|
d297f785db | ||
|
|
f6f1ff7362 | ||
|
|
920b025fda | ||
|
|
59428e47aa | ||
|
|
e3f9a0d4c6 | ||
|
|
a14202f75d | ||
|
|
9d59731afe | ||
|
|
784407b2dd | ||
|
|
32fcc1071f | ||
|
|
32584f9516 | ||
|
|
0ce5c2c11c | ||
|
|
1bfb2af51d | ||
|
|
6bd4a84dc1 | ||
|
|
06dc764063 | ||
|
|
c55ae4a5b7 | ||
|
|
85896ffbfe | ||
|
|
4397bc2cf9 | ||
|
|
3ea6d34fb2 | ||
|
|
391c4b3594 | ||
|
|
1b5b34d265 | ||
|
|
505c2e559e | ||
|
|
dd9a0c8adb | ||
|
|
f4cb77a72e | ||
|
|
4155b9cf4f | ||
|
|
00538f3d91 | ||
|
|
1fd1e3bc2a | ||
|
|
b83c242416 |
2
.github/dependabot.yml
vendored
2
.github/dependabot.yml
vendored
@@ -8,4 +8,4 @@ updates:
|
||||
- package-ecosystem: "gomod" # See documentation for possible values
|
||||
directory: "/" # Location of package manifests
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
interval: "daily"
|
||||
|
||||
9
.github/workflows/build-dev.yml
vendored
9
.github/workflows/build-dev.yml
vendored
@@ -17,7 +17,7 @@ jobs:
|
||||
goarch: [amd64, arm64]
|
||||
env:
|
||||
OUTPUT_BINARY: ghproxy
|
||||
GO_VERSION: 1.23.3
|
||||
GO_VERSION: 1.23.4
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
@@ -32,17 +32,12 @@ jobs:
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
- name: 安装UPX
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt install upx -y
|
||||
- name: 编译
|
||||
env:
|
||||
GOOS: ${{ matrix.goos }}
|
||||
GOARCH: ${{ matrix.goarch }}
|
||||
run: |
|
||||
CGO_ENABLED=0 go build -ldflags "-X main.version=${{ env.VERSION }}" -o ${{ env.OUTPUT_BINARY }}-${{matrix.goos}}-${{matrix.goarch}} ./main.go
|
||||
upx -9 ${{ env.OUTPUT_BINARY }}-${{matrix.goos}}-${{matrix.goarch}}
|
||||
CGO_ENABLED=0 go build -ldflags "-X main.version=${{ env.VERSION }} -X main.dev=true" -o ${{ env.OUTPUT_BINARY }}-${{matrix.goos}}-${{matrix.goarch}} ./main.go
|
||||
- name: 打包
|
||||
run: |
|
||||
mkdir ghproxyd
|
||||
|
||||
7
.github/workflows/build.yml
vendored
7
.github/workflows/build.yml
vendored
@@ -17,7 +17,7 @@ jobs:
|
||||
goarch: [amd64, arm64]
|
||||
env:
|
||||
OUTPUT_BINARY: ghproxy
|
||||
GO_VERSION: 1.23.3
|
||||
GO_VERSION: 1.23.4
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
@@ -32,17 +32,12 @@ jobs:
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
- name: 安装 UPX
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y upx
|
||||
- name: 编译
|
||||
env:
|
||||
GOOS: ${{ matrix.goos }}
|
||||
GOARCH: ${{ matrix.goarch }}
|
||||
run: |
|
||||
CGO_ENABLED=0 go build -ldflags "-s -w -X main.version=${{ env.VERSION }}" -o ${{ env.OUTPUT_BINARY }}-${{matrix.goos}}-${{matrix.goarch}} ./main.go
|
||||
upx -9 ${{ env.OUTPUT_BINARY }}-${{matrix.goos}}-${{matrix.goarch}}
|
||||
- name: 打包
|
||||
run: |
|
||||
mkdir ghproxyd
|
||||
|
||||
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
demo.toml
|
||||
dev
|
||||
*.log
|
||||
188
CHANGELOG.md
188
CHANGELOG.md
@@ -1,5 +1,193 @@
|
||||
# 更新日志
|
||||
|
||||
v1.8.0
|
||||
---
|
||||
- RELEASE: v1.8.0正式版发布; 这是2025年的第一个正式版本发版,祝各位新年快乐!
|
||||
- CHANGE: 更新底包至`v2.9.0`
|
||||
- CHANGE: 优化`Auth`参数透传至`"Authorization: token {token}"`功能, 增加`dev`参数以便调试
|
||||
- CHANGE: 优化`config.toml`默认配置, 增加`embed.FS`内嵌前端支持, 并优化相关逻辑
|
||||
- CHANGE: 更新前端页面版权声明
|
||||
|
||||
25w01e
|
||||
---
|
||||
- PRE-RELEASE: 此版本是v1.8.0的预发布版本,请勿在生产环境中使用
|
||||
- FIX: 修复引入token参数透传功能导致的一些问题
|
||||
|
||||
25w01d
|
||||
---
|
||||
- PRE-RELEASE: 此版本是v1.8.0的预发布版本,请勿在生产环境中使用
|
||||
- CHANGE: 尝试修复部分问题
|
||||
|
||||
25w01c
|
||||
---
|
||||
- PRE-RELEASE: 此版本是v1.8.0的预发布版本,请勿在生产环境中使用
|
||||
- CHANGE: 改进token参数透传功能
|
||||
|
||||
25w01b
|
||||
---
|
||||
- PRE-RELEASE: 此版本是v1.8.0的预发布版本,请勿在生产环境中使用
|
||||
- CHANGE: 将底包更新至`v2.9.0`
|
||||
|
||||
25w01a
|
||||
---
|
||||
- PRE-RELEASE: 此版本是v1.8.0的预发布版本,请勿在生产环境中使用; 同时,这也是2025年的第一个pre-release版本,祝各位新年快乐! (同时,请注意版本号的变化)
|
||||
- ADD: 加入`dev`参数, 以便pre-release版本调试(实验性)
|
||||
- ADD: 加入基于`embed.FS`的内嵌前端, config.toml中的`[pages]`配置为false时自动使用内嵌前端
|
||||
- CHANGE: 完善24w29a版本新加入的`Auth`参数透传至`"Authorization: token {token}"`功能,对相关逻辑进行完善
|
||||
- FIX: 修正24w29a版本新加入的`Auth`参数透传至`"Authorization: token {token}"`功能的一些问题
|
||||
|
||||
24w29a
|
||||
---
|
||||
- PRE-RELEASE: 此版本是一个实验性功能测试版本,请勿在生产环境中使用; 同时,这也是2024年的最后一个pre-release版本
|
||||
- ADD: `Auth` token参数透传至`"Authorization: token {token}"`, 为私有仓库拉取提供一定便利性(需要更多测试)
|
||||
- CHANGE: 更新相关依赖库
|
||||
|
||||
v1.7.9
|
||||
---
|
||||
- RELEASE: 安全性及小型修复, 建议用户自行选择是否升级
|
||||
- CHANGE: 将`logger`库作为外部库引入, 使维护性更好, 同时修正了部分日志问题并提升部分性能
|
||||
- CHANGE: 更新相关依赖库, 更新`req`库以解决`net`标准库的`CVE-2021-38561`漏洞
|
||||
- FIX: 修复安装脚本内的错误
|
||||
|
||||
24w28b
|
||||
---
|
||||
- PRE-RELEASE: 此版本是v1.7.9的预发布版本,请勿在生产环境中使用
|
||||
- CHANGE: 将`logger`库作为外部库引入, 使维护性更好, 同时修正了部分日志问题并提升部分性能
|
||||
|
||||
24w28a
|
||||
---
|
||||
- PRE-RELEASE: 此版本是v1.7.9的预发布版本,请勿在生产环境中使用
|
||||
- CHANGE: 更新相关依赖库, 更新`req`库以解决`net`标准库的`CVE-2021-38561`漏洞
|
||||
- FIX: 修复安装脚本内的错误
|
||||
|
||||
v1.7.8
|
||||
---
|
||||
- RELEASE: 我们建议您升级到此版本, 以解决一些依赖库的安全漏洞和与caddy相关的内存问题
|
||||
- CHANGE: 更新底包至`v24.12.20`可能解决部分与`caddy`相关的内存问题
|
||||
- CHANGE: 更新相关依赖库,解决`net`标准库的`CVE-2024-45338`
|
||||
- CHANGE: 小幅更新前端页面
|
||||
- FIX: 修复`config.toml`默认配置内的错误
|
||||
- ADD: 新增`api.github.com`反代支持, 强制性要求开启`Header Auth`功能(需要更多测试)
|
||||
|
||||
24w27e
|
||||
---
|
||||
- PRE-RELEASE: 此版本是v1.7.8的预发布候选版本(若无问题,此版本将会成为v1.7.8正式版本),请勿在生产环境中使用
|
||||
- CHANGE: 更新底包至`v24.12.20`可能解决部分与`caddy`相关的内存问题
|
||||
|
||||
24w27d
|
||||
---
|
||||
- PRE-RELEASE: 此版本是v1.7.8的预发布候选版本,请勿在生产环境中使用
|
||||
- CHANGE: 更新相关依赖库,解决`net`标准库的`CVE-2024-45338`
|
||||
- CHANGE: 小幅更新前端页面
|
||||
|
||||
24w27c
|
||||
---
|
||||
- PRE-RELEASE: 此版本做为实验性功能测试版本,请勿在生产环境中使用
|
||||
- CHANGE: 更新docker底包至`v2.9.0-beta.3` , 可能解决部分内存相关问题
|
||||
- CHANGE: 更新相关依赖库
|
||||
|
||||
24w27b
|
||||
---
|
||||
- PRE-RELEASE: 此版本做为实验性功能测试版本,请勿在生产环境中使用
|
||||
- FIX: 修复`config.toml`默认配置内的错误
|
||||
|
||||
24w27a
|
||||
---
|
||||
- PRE-RELEASE: 此版本做为实验性功能测试版本,请勿在生产环境中使用
|
||||
- ADD: 新增`api.github.com`反代支持, 强制性要求开启`Header Auth`功能
|
||||
|
||||
v1.7.7
|
||||
---
|
||||
- CHANGE: 更新相关依赖库
|
||||
- CHANGE: 更新Go版本至1.23.4
|
||||
- CHANGE: 更新release及dev版本底包
|
||||
|
||||
24w26a
|
||||
---
|
||||
- PRE-RELEASE: 此版本是v1.7.7的预发布版本,请勿在生产环境中使用
|
||||
- CHANGE: 更新相关依赖库
|
||||
- CHANGE: 更新Go版本至1.23.4
|
||||
- CHANGE: 更新release及dev版本底包
|
||||
|
||||
v1.7.6
|
||||
---
|
||||
- RELEASE: 版本在v1.7.4及以上的用户,我们建议升级到此版本以解决于v1.7.4版本功能更新所引入的问题
|
||||
- FIX: 进一步修正 H2C相关配置逻辑问题
|
||||
- CHANGE: 对Caddy配置进行实验性修改,优化H2C配置
|
||||
- CHANGE: 更新相关依赖库
|
||||
|
||||
24w25b
|
||||
---
|
||||
- PRE-RELEASE: 此版本是v1.7.6的预发布版本,请勿在生产环境中使用
|
||||
- 说明: 本版本为24w25a-fix0
|
||||
- FIX: 进一步修正 H2C相关配置逻辑问题
|
||||
|
||||
24w25a
|
||||
---
|
||||
- PRE-RELEASE: 此版本是v1.7.6的预发布版本,请勿在生产环境中使用
|
||||
- 说明: 本版本为v1.7.6的其中一个候选与开发测试版本,相关改动不一定实装
|
||||
- FIX: 进一步修正 H2C相关配置逻辑问题
|
||||
- CHANGE: 对Caddy配置进行实验性修改,优化H2C配置
|
||||
- CHANGE: 更新相关依赖库
|
||||
|
||||
v1.7.5
|
||||
---
|
||||
- FIX: 修复 v1.7.4 版本 Docker 镜像默认配置导致的 403 问题
|
||||
- ADD: `Rate`模块加入`IP`速率限制,可限制单个IP的请求速率 (需要更多测试)
|
||||
- CHANGE: 处理积攒的依赖库更新
|
||||
|
||||
24w24c
|
||||
---
|
||||
- PRE-RELEASE: 此版本是v1.7.5的预发布版本,请勿在生产环境中使用
|
||||
- CHANGE: 更新依赖
|
||||
|
||||
24w24b
|
||||
---
|
||||
- PRE-RELEASE: 此版本是v1.7.5的预发布版本,请勿在生产环境中使用
|
||||
- FIX: 修复 Docker 默认配置导致的 403 问题
|
||||
|
||||
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
|
||||
---
|
||||
- CHANGE: 对二进制文件部署脚本进行优化
|
||||
- CHANGE&ADD: 新增H2C相关配置
|
||||
- ADD: `Auth`模块加入`Header`鉴权,使用`GH-Auth`的值进行鉴权
|
||||
|
||||
24w23a
|
||||
---
|
||||
- PRE-RELEASE: 此版本是v1.7.4的预发布版本,请勿在生产环境中使用
|
||||
- ADD: `Auth`模块加入`Header`鉴权,使用`GH-Auth`的值进行鉴权
|
||||
- CHANGE: 对二进制文件部署脚本进行优化
|
||||
- CHANGE&ADD: 新增H2C相关配置
|
||||
|
||||
v1.7.3
|
||||
---
|
||||
- CHANGE: Bump golang.org/x/time from 0.7.0 to 0.8.0
|
||||
- FIX: 修复故障熔断的相关问题
|
||||
|
||||
v1.7.2
|
||||
---
|
||||
- CHANGE: 为`nocache`版本加入测试性的故障熔断机制
|
||||
|
||||
v1.7.1
|
||||
---
|
||||
- CHANGE: 更新Go版本至1.23.3
|
||||
|
||||
@@ -1 +1 @@
|
||||
24w22b
|
||||
25w01e
|
||||
@@ -81,6 +81,7 @@ wget -O install.sh https://raw.githubusercontent.com/WJQSERVER-STUDIO/ghproxy/ma
|
||||
host = "127.0.0.1" # 监听地址
|
||||
port = 8080 # 监听端口
|
||||
sizeLimit = 125 # 125MB
|
||||
enableH2C = "on" # 是否开启H2C传输(latest和dev版本请开启) on/off
|
||||
|
||||
[pages]
|
||||
enabled = false # 是否开启内置静态页面(Docker版本请关闭此项)
|
||||
@@ -94,6 +95,7 @@ maxLogSize = 5 # MB 日志文件最大大小
|
||||
enabled = true # 是否开启跨域
|
||||
|
||||
[auth]
|
||||
authMethod = "parameters" # 鉴权方式,支持parameters,header
|
||||
authToken = "token" # 用户鉴权Token
|
||||
enabled = false # 是否开启用户鉴权
|
||||
|
||||
@@ -107,6 +109,7 @@ whitelistFile = "/data/ghproxy/config/whitelist.json" # 白名单文件路径
|
||||
|
||||
[rateLimit]
|
||||
enabled = false # 是否开启速率限制
|
||||
rateMrthod = "total" # "ip" or "total" 速率限制方式
|
||||
ratePerMinute = 180 # 每分钟限制请求数量
|
||||
burst = 5 # 突发请求数量
|
||||
```
|
||||
@@ -156,11 +159,11 @@ example.com {
|
||||
|
||||
### 前端页面
|
||||
|
||||

|
||||

|
||||
|
||||
结语
|
||||
---
|
||||
|
||||
本项目基于Go语言实现的GHProxy,使用Gin框架与req库
|
||||
本项目基于Go语言实现,使用Gin框架与req库
|
||||
Docker镜像基于[WJQSERVER-STUDIO/caddy](https://github.com/WJQSERVER-STUDIO/caddy)
|
||||
本项目使用WSL LICENSE Version1.2 (WJQSERVER STUDIO LICENSE Version1.2) 授权协议,请遵守相关条例。
|
||||
|
||||
@@ -3,8 +3,8 @@ package api
|
||||
import (
|
||||
"encoding/json"
|
||||
"ghproxy/config"
|
||||
"ghproxy/logger"
|
||||
|
||||
"github.com/WJQSERVER-STUDIO/go-utils/logger"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
|
||||
30
auth/auth-header.go
Normal file
30
auth/auth-header.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"ghproxy/config"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func AuthHeaderHandler(c *gin.Context, cfg *config.Config) (isValid bool, err string) {
|
||||
if !cfg.Auth.Enabled {
|
||||
return true, ""
|
||||
}
|
||||
// 获取"GH-Auth"的值
|
||||
authToken := c.GetHeader("GH-Auth")
|
||||
logInfo("%s %s %s %s %s AUTH_TOKEN: %s", c.Request.Method, c.Request.Host, c.Request.URL.Path, c.Request.Proto, c.Request.RemoteAddr, authToken)
|
||||
if authToken == "" {
|
||||
err := "Auth Header == nil"
|
||||
return false, err
|
||||
}
|
||||
|
||||
isValid = authToken == cfg.Auth.AuthToken
|
||||
if !isValid {
|
||||
err := fmt.Sprintf("Auth token incorrect: %s", authToken)
|
||||
return false, err
|
||||
}
|
||||
|
||||
logInfo("auth SUCCESS: %t", isValid)
|
||||
return isValid, ""
|
||||
}
|
||||
31
auth/auth-parameters.go
Normal file
31
auth/auth-parameters.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"ghproxy/config"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func AuthParametersHandler(c *gin.Context, cfg *config.Config) (isValid bool, err string) {
|
||||
if !cfg.Auth.Enabled {
|
||||
return true, ""
|
||||
}
|
||||
|
||||
authToken := c.Query("auth_token")
|
||||
logInfo("%s %s %s %s %s AUTH_TOKEN: %s", c.ClientIP(), c.Request.Method, c.Request.URL.Path, c.Request.UserAgent(), c.Request.Proto, authToken)
|
||||
|
||||
if authToken == "" {
|
||||
err := "Auth token == nil"
|
||||
return false, err
|
||||
}
|
||||
|
||||
isValid = authToken == cfg.Auth.AuthToken
|
||||
if !isValid {
|
||||
err := fmt.Sprintf("Auth token incorrect: %s", authToken)
|
||||
return false, err
|
||||
}
|
||||
|
||||
logInfo("auth SUCCESS: %t", isValid)
|
||||
return isValid, ""
|
||||
}
|
||||
32
auth/auth.go
32
auth/auth.go
@@ -1,10 +1,9 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"ghproxy/config"
|
||||
"ghproxy/logger"
|
||||
|
||||
"github.com/WJQSERVER-STUDIO/go-utils/logger"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
@@ -26,24 +25,17 @@ func Init(cfg *config.Config) {
|
||||
}
|
||||
|
||||
func AuthHandler(c *gin.Context, cfg *config.Config) (isValid bool, err string) {
|
||||
if !cfg.Auth.Enabled {
|
||||
if cfg.Auth.AuthMethod == "parameters" {
|
||||
isValid, err = AuthParametersHandler(c, cfg)
|
||||
return isValid, err
|
||||
} else if cfg.Auth.AuthMethod == "header" {
|
||||
isValid, err = AuthHeaderHandler(c, cfg)
|
||||
return isValid, err
|
||||
} else if cfg.Auth.AuthMethod == "" {
|
||||
logWarning("Auth method not set")
|
||||
return true, ""
|
||||
} else {
|
||||
logWarning("Auth method not supported")
|
||||
return false, "Auth method not supported"
|
||||
}
|
||||
|
||||
authToken := c.Query("auth_token")
|
||||
logInfo("%s %s %s %s %s AUTH_TOKEN: %s", c.ClientIP(), c.Request.Method, c.Request.URL.Path, c.Request.UserAgent(), c.Request.Proto, authToken)
|
||||
|
||||
if authToken == "" {
|
||||
err := "Auth token == nil"
|
||||
return false, err
|
||||
}
|
||||
|
||||
isValid = authToken == cfg.Auth.AuthToken
|
||||
if !isValid {
|
||||
err := fmt.Sprintf("Auth token incorrect: %s", authToken)
|
||||
return false, err
|
||||
}
|
||||
|
||||
logInfo("auth SUCCESS: %t", isValid)
|
||||
return isValid, ""
|
||||
}
|
||||
|
||||
@@ -13,6 +13,9 @@
|
||||
roll_keep 10
|
||||
}
|
||||
}
|
||||
servers :80 {
|
||||
protocols h1 h2c
|
||||
}
|
||||
}
|
||||
|
||||
(log) {
|
||||
@@ -68,8 +71,11 @@
|
||||
|
||||
:80 {
|
||||
reverse_proxy {
|
||||
to h2c://127.0.0.1:8080
|
||||
to 127.0.0.1:8080
|
||||
import header_realip
|
||||
transport http {
|
||||
versions 1.1 h2c
|
||||
}
|
||||
}
|
||||
import log ghproxy
|
||||
import cache 0s 300s
|
||||
|
||||
@@ -13,6 +13,9 @@
|
||||
roll_keep 10
|
||||
}
|
||||
}
|
||||
servers :80 {
|
||||
protocols h1 h2c
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -13,6 +13,9 @@
|
||||
roll_keep 10
|
||||
}
|
||||
}
|
||||
servers :80 {
|
||||
protocols h1 h2c
|
||||
}
|
||||
}
|
||||
|
||||
(log) {
|
||||
@@ -68,8 +71,11 @@
|
||||
|
||||
:80 {
|
||||
reverse_proxy {
|
||||
to h2c://127.0.0.1:8080
|
||||
to 127.0.0.1:8080
|
||||
import header_realip
|
||||
transport http {
|
||||
versions 1.1 h2c
|
||||
}
|
||||
}
|
||||
import log ghproxy
|
||||
import cache 0s 300s
|
||||
|
||||
@@ -19,6 +19,8 @@ type ServerConfig struct {
|
||||
Port int `toml:"port"`
|
||||
Host string `toml:"host"`
|
||||
SizeLimit int `toml:"sizeLimit"`
|
||||
EnableH2C string `toml:"enableH2C"`
|
||||
Debug bool `toml:"debug"`
|
||||
}
|
||||
|
||||
type PagesConfig struct {
|
||||
@@ -36,8 +38,10 @@ type CORSConfig struct {
|
||||
}
|
||||
|
||||
type AuthConfig struct {
|
||||
Enabled bool `toml:"enabled"`
|
||||
AuthToken string `toml:"authToken"`
|
||||
Enabled bool `toml:"enabled"`
|
||||
AuthMethod string `toml:"authMethod"`
|
||||
AuthToken string `toml:"authToken"`
|
||||
PassThrough bool `toml:"passThrough"`
|
||||
}
|
||||
|
||||
type BlacklistConfig struct {
|
||||
@@ -51,9 +55,10 @@ type WhitelistConfig struct {
|
||||
}
|
||||
|
||||
type RateLimitConfig struct {
|
||||
Enabled bool `toml:"enabled"`
|
||||
RatePerMinute int `toml:"ratePerMinute"`
|
||||
Burst int `toml:"burst"`
|
||||
Enabled bool `toml:"enabled"`
|
||||
RateMethod string `toml:"rateMethod"`
|
||||
RatePerMinute int `toml:"ratePerMinute"`
|
||||
Burst int `toml:"burst"`
|
||||
}
|
||||
|
||||
// LoadConfig 从 TOML 配置文件加载配置
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
host = "127.0.0.1"
|
||||
port = 8080
|
||||
sizeLimit = 125 # MB
|
||||
enableH2C = "on" # "on" or "off"
|
||||
debug = false
|
||||
|
||||
[pages]
|
||||
enabled = false
|
||||
@@ -15,8 +17,10 @@ maxLogSize = 5 # MB
|
||||
enabled = true
|
||||
|
||||
[auth]
|
||||
authMethod = "parameters" # "header" or "parameters"
|
||||
authToken = "token"
|
||||
enabled = false
|
||||
passThrough = false
|
||||
|
||||
[blacklist]
|
||||
blacklistFile = "/data/ghproxy/config/blacklist.json"
|
||||
@@ -28,5 +32,6 @@ whitelistFile = "/data/ghproxy/config/whitelist.json"
|
||||
|
||||
[rateLimit]
|
||||
enabled = false
|
||||
rateMethod = "total" # "ip" or "total"
|
||||
ratePerMinute = 180
|
||||
burst = 5
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
host = "127.0.0.1"
|
||||
port = 8080
|
||||
sizeLimit = 125 # MB
|
||||
enableH2C = false
|
||||
debug = false
|
||||
|
||||
[pages]
|
||||
enabled = true
|
||||
@@ -15,8 +17,10 @@ maxLogSize = 5 # MB
|
||||
enabled = true
|
||||
|
||||
[auth]
|
||||
authMethod = "parameters" # "header" or "parameters"
|
||||
authToken = "token"
|
||||
enabled = false
|
||||
passThrough = false
|
||||
|
||||
[blacklist]
|
||||
blacklistFile = "/usr/local/ghproxy/config/blacklist.json"
|
||||
@@ -28,5 +32,6 @@ whitelistFile = "/usr/local/ghproxy/config/whitelist.json"
|
||||
|
||||
[rateLimit]
|
||||
enabled = false
|
||||
rateMethod = "total" # "ip" or "total"
|
||||
ratePerMinute = 180
|
||||
burst = 5
|
||||
|
||||
@@ -30,25 +30,13 @@ install() {
|
||||
return 0
|
||||
}
|
||||
|
||||
make_systemd_service() {
|
||||
cat <<EOF > /etc/systemd/system/ghproxy.service
|
||||
[Unit]
|
||||
Description=Github Proxy Service
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
ExecStart=/bin/bash -c '$ghproxy_dir/ghproxy -cfg $ghproxy_dir/config/config.toml > $ghproxy_dir/log/run.log 2>&1'
|
||||
WorkingDirectory=$ghproxy_dir
|
||||
Restart=always
|
||||
User=root
|
||||
Group=root
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
||||
EOF
|
||||
|
||||
}
|
||||
# 检查是否为root用户
|
||||
if [ "$EUID" -ne 0 ]; then
|
||||
echo "请以root用户运行此脚本"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 安装依赖包
|
||||
install curl wget sed
|
||||
@@ -85,6 +73,26 @@ if [ -z "$ghproxy_dir" ]; then
|
||||
ghproxy_dir="/usr/local/ghproxy"
|
||||
fi
|
||||
|
||||
make_systemd_service() {
|
||||
cat <<EOF > /etc/systemd/system/ghproxy.service
|
||||
[Unit]
|
||||
Description=Github Proxy Service
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
ExecStart=/bin/bash -c '$ghproxy_dir/ghproxy -cfg $ghproxy_dir/config/config.toml > $ghproxy_dir/log/run.log 2>&1'
|
||||
WorkingDirectory=$ghproxy_dir
|
||||
Restart=always
|
||||
User=root
|
||||
Group=root
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
||||
EOF
|
||||
|
||||
}
|
||||
|
||||
# 创建目录
|
||||
mkdir -p ${ghproxy_dir}
|
||||
mkdir -p ${ghproxy_dir}/config
|
||||
@@ -127,7 +135,7 @@ sed -i "s|whitelistFile = \"/usr/local/ghproxy/config/whitelist.json\"|whitelist
|
||||
if [ "$ghproxy_dir" = "/usr/local/ghproxy" ]; then
|
||||
wget -q -O /etc/systemd/system/ghproxy.service https://raw.githubusercontent.com/WJQSERVER-STUDIO/ghproxy/main/deploy/ghproxy.service
|
||||
else
|
||||
make_systemd_service
|
||||
make_systemd_service()
|
||||
fi
|
||||
|
||||
# 启动ghproxy
|
||||
|
||||
@@ -30,25 +30,13 @@ install() {
|
||||
return 0
|
||||
}
|
||||
|
||||
make_systemd_service() {
|
||||
cat <<EOF > /etc/systemd/system/ghproxy.service
|
||||
[Unit]
|
||||
Description=Github Proxy Service
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
ExecStart=/bin/bash -c '$ghproxy_dir/ghproxy -cfg $ghproxy_dir/config/config.toml > $ghproxy_dir/log/run.log 2>&1'
|
||||
WorkingDirectory=$ghproxy_dir
|
||||
Restart=always
|
||||
User=root
|
||||
Group=root
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
||||
EOF
|
||||
|
||||
}
|
||||
# 检查是否为root用户
|
||||
if [ "$EUID" -ne 0 ]; then
|
||||
echo "请以root用户运行此脚本"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 安装依赖包
|
||||
install curl wget sed
|
||||
@@ -85,6 +73,26 @@ if [ -z "$ghproxy_dir" ]; then
|
||||
ghproxy_dir="/usr/local/ghproxy"
|
||||
fi
|
||||
|
||||
make_systemd_service() {
|
||||
cat <<EOF > /etc/systemd/system/ghproxy.service
|
||||
[Unit]
|
||||
Description=Github Proxy Service
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
ExecStart=/bin/bash -c '$ghproxy_dir/ghproxy -cfg $ghproxy_dir/config/config.toml > $ghproxy_dir/log/run.log 2>&1'
|
||||
WorkingDirectory=$ghproxy_dir
|
||||
Restart=always
|
||||
User=root
|
||||
Group=root
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
||||
EOF
|
||||
|
||||
}
|
||||
|
||||
# 创建目录
|
||||
mkdir -p ${ghproxy_dir}
|
||||
mkdir -p ${ghproxy_dir}/config
|
||||
@@ -96,7 +104,7 @@ VERSION=$(curl -s https://raw.githubusercontent.com/WJQSERVER-STUDIO/ghproxy/mai
|
||||
wget -q -O ${ghproxy_dir}/VERSION https://raw.githubusercontent.com/WJQSERVER-STUDIO/ghproxy/main/VERSION
|
||||
|
||||
# 下载ghproxy
|
||||
wget -q -O ${ghproxy_dir}/ghproxy https://github.com/WJQSERVER-STUDIO/ghproxy/releases/download/$VERSION/ghproxy-linux-$ARCH.tar.gz
|
||||
wget -q -O ${ghproxy_dir}/ghproxy https://github.com/WJQSERVER-STUDIO/ghproxy/releases/download/${VERSION}/ghproxy-linux-${ARCH}.tar.gz
|
||||
install tar
|
||||
tar -zxvf ${ghproxy_dir}/ghproxy-linux-$ARCH.tar.gz -C ${ghproxy_dir}
|
||||
chmod +x ${ghproxy_dir}/ghproxy
|
||||
@@ -127,7 +135,7 @@ sed -i "s|whitelistFile = \"/usr/local/ghproxy/config/whitelist.json\"|whitelist
|
||||
if [ "$ghproxy_dir" = "/usr/local/ghproxy" ]; then
|
||||
wget -q -O /etc/systemd/system/ghproxy.service https://raw.githubusercontent.com/WJQSERVER-STUDIO/ghproxy/main/deploy/ghproxy.service
|
||||
else
|
||||
make_systemd_service
|
||||
make_systemd_service()
|
||||
fi
|
||||
|
||||
# 启动ghproxy
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM wjqserver/caddy:2.9.0-rc4-alpine AS builder
|
||||
FROM wjqserver/caddy:2.9.0-alpine AS builder
|
||||
|
||||
ARG USER=WJQSERVER-STUDIO
|
||||
ARG REPO=ghproxy
|
||||
@@ -36,7 +36,7 @@ RUN wget -O /data/${APPLICATION}/whitelist.json https://raw.githubusercontent.co
|
||||
RUN chmod +x /data/${APPLICATION}/${APPLICATION}
|
||||
RUN chmod +x /usr/local/bin/init.sh
|
||||
|
||||
FROM wjqserver/caddy:2.9.0-rc4-alpine
|
||||
FROM wjqserver/caddy:2.9.0-alpine
|
||||
|
||||
RUN apk add --no-cache curl
|
||||
|
||||
|
||||
@@ -26,6 +26,6 @@ sleep 30
|
||||
|
||||
while [[ true ]]; do
|
||||
# Failure Circuit Breaker
|
||||
curl -f -max-time 5 -retry 3 http://localhost:8080/api/healthcheck || exit 1
|
||||
curl -f --max-time 5 -retry 3 http://127.0.0.1:8080/api/healthcheck || exit 1
|
||||
sleep 120
|
||||
done
|
||||
@@ -1,7 +1,9 @@
|
||||
[server]
|
||||
host = "0.0.0.0"
|
||||
port = 80
|
||||
port = 80 #修改此配置会导致容器异常
|
||||
sizeLimit = 125 # MB
|
||||
enableH2C = "off" # on / off
|
||||
debug = false
|
||||
|
||||
[pages]
|
||||
enabled = true
|
||||
@@ -15,8 +17,10 @@ maxLogSize = 5 # MB
|
||||
enabled = true
|
||||
|
||||
[auth]
|
||||
authMethod = "parameters" # "header" or "parameters"
|
||||
authToken = "token"
|
||||
enabled = false
|
||||
passThrough = false
|
||||
|
||||
[blacklist]
|
||||
blacklistFile = "/data/ghproxy/config/blacklist.json"
|
||||
@@ -28,5 +32,6 @@ whitelistFile = "/data/ghproxy/config/whitelist.json"
|
||||
|
||||
[rateLimit]
|
||||
enabled = false
|
||||
rateMethod = "total" # "ip" or "total"
|
||||
ratePerMinute = 180
|
||||
burst = 5
|
||||
|
||||
@@ -16,6 +16,10 @@ fi
|
||||
|
||||
/data/${APPLICATION}/${APPLICATION} -cfg /data/${APPLICATION}/config/config.toml > /data/${APPLICATION}/log/run.log 2>&1 &
|
||||
|
||||
while true; do
|
||||
sleep 1
|
||||
sleep 30
|
||||
|
||||
while [[ true ]]; do
|
||||
# Failure Circuit Breaker
|
||||
curl -f --max-time 5 -retry 3 http://127.0.0.1:80/api/healthcheck || exit 1
|
||||
sleep 120
|
||||
done
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM wjqserver/caddy:2.9.0-rc4-alpine AS builder
|
||||
FROM wjqserver/caddy:2.9.0-alpine AS builder
|
||||
|
||||
ARG USER=WJQSERVER-STUDIO
|
||||
ARG REPO=ghproxy
|
||||
@@ -36,7 +36,7 @@ RUN wget -O /data/${APPLICATION}/whitelist.json https://raw.githubusercontent.co
|
||||
RUN chmod +x /data/${APPLICATION}/${APPLICATION}
|
||||
RUN chmod +x /usr/local/bin/init.sh
|
||||
|
||||
FROM wjqserver/caddy:2.9.0-rc4-alpine
|
||||
FROM wjqserver/caddy:2.9.0-alpine
|
||||
|
||||
RUN apk add --no-cache curl
|
||||
|
||||
|
||||
@@ -26,6 +26,6 @@ sleep 30
|
||||
|
||||
while [[ true ]]; do
|
||||
# Failure Circuit Breaker
|
||||
curl -f -max-time 5 -retry 3 http://localhost:8080/api/healthcheck || exit 1
|
||||
curl -f --max-time 5 -retry 3 http://127.0.0.1:8080/api/healthcheck || exit 1
|
||||
sleep 120
|
||||
done
|
||||
45
go.mod
45
go.mod
@@ -1,55 +1,56 @@
|
||||
module ghproxy
|
||||
|
||||
go 1.23.3
|
||||
go 1.23.4
|
||||
|
||||
require (
|
||||
github.com/BurntSushi/toml v1.4.0
|
||||
github.com/WJQSERVER-STUDIO/go-utils/logger v1.1.0
|
||||
github.com/gin-gonic/gin v1.10.0
|
||||
github.com/imroc/req/v3 v3.48.0
|
||||
golang.org/x/time v0.7.0
|
||||
github.com/imroc/req/v3 v3.49.1
|
||||
golang.org/x/time v0.9.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/andybalholm/brotli v1.1.1 // indirect
|
||||
github.com/bytedance/sonic v1.12.4 // indirect
|
||||
github.com/bytedance/sonic v1.12.6 // indirect
|
||||
github.com/bytedance/sonic/loader v0.2.1 // indirect
|
||||
github.com/cloudflare/circl v1.5.0 // indirect
|
||||
github.com/cloudwego/base64x v0.1.4 // indirect
|
||||
github.com/cloudwego/iasm v0.2.0 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.6 // indirect
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
|
||||
github.com/gin-contrib/sse v1.0.0 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.22.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.23.0 // indirect
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
|
||||
github.com/goccy/go-json v0.10.3 // indirect
|
||||
github.com/google/pprof v0.0.0-20241101162523-b92577c0c142 // indirect
|
||||
github.com/goccy/go-json v0.10.4 // indirect
|
||||
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/compress v1.17.11 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.8 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.9 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.21.0 // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.22.2 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
|
||||
github.com/quic-go/qpack v0.5.1 // indirect
|
||||
github.com/quic-go/quic-go v0.48.1 // indirect
|
||||
github.com/quic-go/quic-go v0.48.2 // indirect
|
||||
github.com/refraction-networking/utls v1.6.7 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.2.12 // indirect
|
||||
go.uber.org/mock v0.5.0 // indirect
|
||||
golang.org/x/arch v0.11.0 // indirect
|
||||
golang.org/x/crypto v0.28.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c // indirect
|
||||
golang.org/x/mod v0.21.0 // indirect
|
||||
golang.org/x/net v0.30.0 // indirect
|
||||
golang.org/x/sync v0.8.0 // indirect
|
||||
golang.org/x/sys v0.26.0 // indirect
|
||||
golang.org/x/text v0.19.0 // indirect
|
||||
golang.org/x/tools v0.26.0 // indirect
|
||||
google.golang.org/protobuf v1.35.1 // indirect
|
||||
golang.org/x/arch v0.13.0 // indirect
|
||||
golang.org/x/crypto v0.31.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20250103183323-7d7fa50e5329 // indirect
|
||||
golang.org/x/mod v0.22.0 // indirect
|
||||
golang.org/x/net v0.33.0 // indirect
|
||||
golang.org/x/sync v0.10.0 // indirect
|
||||
golang.org/x/sys v0.29.0 // indirect
|
||||
golang.org/x/text v0.21.0 // indirect
|
||||
golang.org/x/tools v0.28.0 // indirect
|
||||
google.golang.org/protobuf v1.36.1 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
123
go.sum
123
go.sum
@@ -1,14 +1,12 @@
|
||||
github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
|
||||
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||
github.com/WJQSERVER-STUDIO/go-utils/logger v1.1.0 h1:OUrAOWb8xK0kxpWextJYUasmol+5KKqG2az52X2ae64=
|
||||
github.com/WJQSERVER-STUDIO/go-utils/logger v1.1.0/go.mod h1:sAqHVYSucoUnycyHMAZc1fMH5dS2bQwgwo8NUiAIcyk=
|
||||
github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA=
|
||||
github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA=
|
||||
github.com/bytedance/sonic v1.12.3 h1:W2MGa7RCU1QTeYRTPE3+88mVC0yXmsRQRChiyVocVjU=
|
||||
github.com/bytedance/sonic v1.12.3/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk=
|
||||
github.com/bytedance/sonic v1.12.4 h1:9Csb3c9ZJhfUWeMtpCDCq6BUoH5ogfDFLUgQ/jG+R0k=
|
||||
github.com/bytedance/sonic v1.12.4/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk=
|
||||
github.com/bytedance/sonic v1.12.6 h1:/isNmCUF2x3Sh8RAp/4mh4ZGkcFAX/hLrzrK3AvpRzk=
|
||||
github.com/bytedance/sonic v1.12.6/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk=
|
||||
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
||||
github.com/bytedance/sonic/loader v0.2.0 h1:zNprn+lsIP06C/IqCHs3gPQIvnvpKbbxyXQP1iU4kWM=
|
||||
github.com/bytedance/sonic/loader v0.2.0/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
||||
github.com/bytedance/sonic/loader v0.2.1 h1:1GgorWTqf12TA8mma4DDSbaQigE2wOgQo7iCjjJv3+E=
|
||||
github.com/bytedance/sonic/loader v0.2.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
||||
github.com/cloudflare/circl v1.5.0 h1:hxIWksrX6XN5a1L2TI/h53AGPhNHoUBo+TD1ms9+pys=
|
||||
@@ -20,12 +18,10 @@ github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQ
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/gabriel-vasile/mimetype v1.4.5 h1:J7wGKdGu33ocBOhGy0z653k/lFKLFDPJMG8Gql0kxn4=
|
||||
github.com/gabriel-vasile/mimetype v1.4.5/go.mod h1:ibHel+/kbxn9x2407k1izTA1S81ku1z/DlgOW2QE0M4=
|
||||
github.com/gabriel-vasile/mimetype v1.4.6 h1:3+PzJTKLkvgjeTbts6msPJt4DixhT4YtFNf1gtGe3zc=
|
||||
github.com/gabriel-vasile/mimetype v1.4.6/go.mod h1:JX1qVKqZd40hUPpAfiNTe0Sne7hdfKSbOqqmkq8GCXc=
|
||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM=
|
||||
github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8=
|
||||
github.com/gin-contrib/sse v1.0.0 h1:y3bT1mUWUxDpW4JLQg/HnTqV4rozuW4tC9eFKTxYI9E=
|
||||
github.com/gin-contrib/sse v1.0.0/go.mod h1:zNuFdwarAygJBht0NTKiSi3jRf6RbqeILZ9Sp6Slhe0=
|
||||
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
|
||||
github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
|
||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
@@ -36,40 +32,31 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o
|
||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.22.1 h1:40JcKH+bBNGFczGuoBYgX4I6m/i27HYW8P9FDk5PbgA=
|
||||
github.com/go-playground/validator/v10 v10.22.1/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
|
||||
github.com/go-playground/validator/v10 v10.23.0 h1:/PwmTwZhS0dPkav3cdK9kV1FsAmrL8sThn8IHr/sO+o=
|
||||
github.com/go-playground/validator/v10 v10.23.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
||||
github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
|
||||
github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||
github.com/goccy/go-json v0.10.4 h1:JSwxQzIqKfmFX1swYPpUThQZp/Ka4wzJdK0LWVytLPM=
|
||||
github.com/goccy/go-json v0.10.4/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/pprof v0.0.0-20241009165004-a3522334989c h1:NDovD0SMpBYXlE1zJmS1q55vWB/fUQBcPAqAboZSccA=
|
||||
github.com/google/pprof v0.0.0-20241009165004-a3522334989c/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
|
||||
github.com/google/pprof v0.0.0-20241017200806-017d972448fc h1:NGyrhhFhwvRAZg02jnYVg3GBQy0qGBKmFQJwaPmpmxs=
|
||||
github.com/google/pprof v0.0.0-20241017200806-017d972448fc/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
|
||||
github.com/google/pprof v0.0.0-20241023014458-598669927662 h1:SKMkD83p7FwUqKmBsPdLHF5dNyxq3jOWwu9w9UyH5vA=
|
||||
github.com/google/pprof v0.0.0-20241023014458-598669927662/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
|
||||
github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo=
|
||||
github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
|
||||
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-20241210010833-40e02aabc2ad h1:a6HEuzUHeKH6hwfN/ZoQgRgVIWFJljSWa/zetS2WTvg=
|
||||
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
|
||||
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/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||
github.com/imroc/req/v3 v3.48.0 h1:IYuMGetuwLzOOTzDCquDqs912WNwpsPK0TBXWPIvoqg=
|
||||
github.com/imroc/req/v3 v3.48.0/go.mod h1:weam9gmyb00QnOtu6HXSnk44dNFkIUQb5QdMx13FeUU=
|
||||
github.com/imroc/req/v3 v3.49.1 h1:Nvwo02riiPEzh74ozFHeEJrtjakFxnoWNR3YZYuQm9U=
|
||||
github.com/imroc/req/v3 v3.49.1/go.mod h1:tsOk8K7zI6cU4xu/VWCZVtq9Djw9IWm4MslKzme5woU=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
|
||||
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM=
|
||||
github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||
github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY=
|
||||
github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8=
|
||||
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||
@@ -80,73 +67,63 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/onsi/ginkgo/v2 v2.20.2 h1:7NVCeyIWROIAheY21RLS+3j2bb52W0W82tkberYytp4=
|
||||
github.com/onsi/ginkgo/v2 v2.20.2/go.mod h1:K9gyxPIlb+aIvnZ8bd9Ak+YP18w3APlR+5coaZoE2ag=
|
||||
github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM=
|
||||
github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo=
|
||||
github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
|
||||
github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
|
||||
github.com/onsi/gomega v1.34.2 h1:pNCwDkzrsv7MS9kpaQvVb1aVLahQXyJ/Tv5oAZMI3i8=
|
||||
github.com/onsi/ginkgo/v2 v2.22.2 h1:/3X8Panh8/WwhU/3Ssa6rCKqPLuAkVY2I0RoyDLySlU=
|
||||
github.com/onsi/ginkgo/v2 v2.22.2/go.mod h1:oeMosUL+8LtarXBHu/c0bx2D/K9zyQ6uX3cTyztHwsk=
|
||||
github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8=
|
||||
github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY=
|
||||
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
|
||||
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
|
||||
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
|
||||
github.com/quic-go/quic-go v0.47.0 h1:yXs3v7r2bm1wmPTYNLKAAJTHMYkPEsfYJmTazXrCZ7Y=
|
||||
github.com/quic-go/quic-go v0.47.0/go.mod h1:3bCapYsJvXGZcipOHuu7plYtaV6tnF+z7wIFsU0WK9E=
|
||||
github.com/quic-go/quic-go v0.48.0 h1:2TCyvBrMu1Z25rvIAlnp2dPT4lgh/uTqLqiXVpp5AeU=
|
||||
github.com/quic-go/quic-go v0.48.0/go.mod h1:yBgs3rWBOADpga7F+jJsb6Ybg1LSYiQvwWlLX+/6HMs=
|
||||
github.com/quic-go/quic-go v0.48.1 h1:y/8xmfWI9qmGTc+lBr4jKRUWLGSlSigv847ULJ4hYXA=
|
||||
github.com/quic-go/quic-go v0.48.1/go.mod h1:yBgs3rWBOADpga7F+jJsb6Ybg1LSYiQvwWlLX+/6HMs=
|
||||
github.com/quic-go/quic-go v0.48.2 h1:wsKXZPeGWpMpCGSWqOcqpW2wZYic/8T3aqiOID0/KWE=
|
||||
github.com/quic-go/quic-go v0.48.2/go.mod h1:yBgs3rWBOADpga7F+jJsb6Ybg1LSYiQvwWlLX+/6HMs=
|
||||
github.com/refraction-networking/utls v1.6.7 h1:zVJ7sP1dJx/WtVuITug3qYUq034cDq9B2MR1K67ULZM=
|
||||
github.com/refraction-networking/utls v1.6.7/go.mod h1:BC3O4vQzye5hqpmDTWUqi4P5DDhzJfkV1tdqtawQIH0=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
|
||||
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
|
||||
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
|
||||
go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
|
||||
go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
|
||||
go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU=
|
||||
go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=
|
||||
golang.org/x/arch v0.11.0 h1:KXV8WWKCXm6tRpLirl2szsO5j/oOODwZf4hATmGVNs4=
|
||||
golang.org/x/arch v0.11.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||
golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
|
||||
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
|
||||
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY=
|
||||
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8=
|
||||
golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
|
||||
golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||
golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
|
||||
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
|
||||
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/arch v0.13.0 h1:KCkqVVV1kGg0X87TFysjCJ8MxtZEIU4Ja/yXGeoECdA=
|
||||
golang.org/x/arch v0.13.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
|
||||
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||
golang.org/x/exp v0.0.0-20250103183323-7d7fa50e5329 h1:9kj3STMvgqy3YA4VQXBrN7925ICMxD5wzMRcgA30588=
|
||||
golang.org/x/exp v0.0.0-20250103183323-7d7fa50e5329/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c=
|
||||
golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
|
||||
golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
|
||||
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
||||
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
|
||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
|
||||
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
|
||||
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
|
||||
golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ=
|
||||
golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=
|
||||
google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
|
||||
google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
|
||||
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
|
||||
golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8=
|
||||
golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw=
|
||||
google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk=
|
||||
google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
||||
1
init.sh
1
init.sh
@@ -25,7 +25,6 @@ fi
|
||||
sleep 30
|
||||
|
||||
while [[ true ]]; do
|
||||
# 健康检查
|
||||
curl -f http://localhost:8080/api/healthcheck || exit 1
|
||||
sleep 120
|
||||
done
|
||||
|
||||
175
logger/logger.go
175
logger/logger.go
@@ -1,175 +0,0 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"compress/gzip"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
logw = Logw
|
||||
logFile *os.File
|
||||
logger *log.Logger
|
||||
logChannel = make(chan string, 100)
|
||||
quitChannel = make(chan struct{})
|
||||
logFileMutex sync.Mutex
|
||||
logFilePath = "/data/ghproxy/log/ghproxy.log"
|
||||
)
|
||||
|
||||
// 初始化
|
||||
func Init(logFilePath_input string, maxLogsize int) error {
|
||||
logFileMutex.Lock()
|
||||
defer logFileMutex.Unlock()
|
||||
|
||||
var err error
|
||||
logFilePath = logFilePath_input
|
||||
logFile, err = os.OpenFile(logFilePath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logger = log.New(logFile, "", 0)
|
||||
|
||||
go logWorker()
|
||||
go monitorLogSize(logFilePath, maxLogsize)
|
||||
return nil
|
||||
}
|
||||
|
||||
func logWorker() {
|
||||
for {
|
||||
select {
|
||||
case msg := <-logChannel:
|
||||
timestamp := time.Now().Format("02/Jan/2006:15:04:05 -0700")
|
||||
logger.Println(timestamp + " - " + msg)
|
||||
case <-quitChannel:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Log(customMessage string) {
|
||||
logChannel <- customMessage
|
||||
}
|
||||
|
||||
func Logw(format string, args ...interface{}) {
|
||||
message := fmt.Sprintf(format, args...)
|
||||
Log(message)
|
||||
}
|
||||
|
||||
// INFO
|
||||
func LogInfo(format string, args ...interface{}) {
|
||||
message := fmt.Sprintf(format, args...)
|
||||
output := fmt.Sprintf("[INFO] %s", message)
|
||||
Log(output)
|
||||
}
|
||||
|
||||
// WARNING
|
||||
func LogWarning(format string, args ...interface{}) {
|
||||
message := fmt.Sprintf(format, args...)
|
||||
output := fmt.Sprintf("[WARNING] %s", message)
|
||||
Log(output)
|
||||
}
|
||||
|
||||
// ERROR
|
||||
func LogError(format string, args ...interface{}) {
|
||||
message := fmt.Sprintf(format, args...)
|
||||
Log(message)
|
||||
}
|
||||
|
||||
func Close() {
|
||||
logFileMutex.Lock()
|
||||
defer logFileMutex.Unlock()
|
||||
|
||||
if logFile != nil {
|
||||
quitChannel <- struct{}{}
|
||||
if err := logFile.Close(); err != nil {
|
||||
fmt.Printf("Error closing log file: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func monitorLogSize(logFilePath string, maxLogsize int) {
|
||||
var maxLogsizeBytes int64 = int64(maxLogsize) * 1024 * 1024
|
||||
for {
|
||||
time.Sleep(120 * time.Minute) // 每120分钟检查一次日志文件大小
|
||||
logFileMutex.Lock()
|
||||
info, err := logFile.Stat()
|
||||
logFileMutex.Unlock()
|
||||
|
||||
if err == nil && info.Size() > maxLogsizeBytes {
|
||||
if err := rotateLogFile(logFilePath); err != nil {
|
||||
logw("Log Rotation Failed: %s", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func rotateLogFile(logFilePath string) error {
|
||||
logFileMutex.Lock()
|
||||
defer logFileMutex.Unlock()
|
||||
|
||||
if logFile != nil {
|
||||
if err := logFile.Close(); err != nil {
|
||||
logw("Error closing log file for rotation: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
logFile, err := os.Open(logFilePath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open log file: %s, error: %w", logFilePath, err)
|
||||
}
|
||||
defer logFile.Close()
|
||||
|
||||
newLogFilePath := logFilePath + "-" + time.Now().Format("20060102-150405") + ".tar.gz"
|
||||
outFile, err := os.Create(newLogFilePath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create gz file: %s, error: %w", newLogFilePath, err)
|
||||
}
|
||||
defer outFile.Close()
|
||||
|
||||
gzWriter, err := gzip.NewWriterLevel(outFile, gzip.BestCompression)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create gz writer: %w", err)
|
||||
}
|
||||
defer gzWriter.Close()
|
||||
|
||||
tarWriter := tar.NewWriter(gzWriter)
|
||||
defer tarWriter.Close()
|
||||
|
||||
logFileStat, err := logFile.Stat()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to stat log file: %s, error: %w", logFilePath, err)
|
||||
}
|
||||
|
||||
logFileHeader := &tar.Header{
|
||||
Name: filepath.Base(logFilePath),
|
||||
Size: logFileStat.Size(),
|
||||
Mode: 0644,
|
||||
ModTime: logFileStat.ModTime(),
|
||||
}
|
||||
|
||||
if err := tarWriter.WriteHeader(logFileHeader); err != nil {
|
||||
return fmt.Errorf("failed to write log file header: %s, error: %w", logFilePath, err)
|
||||
}
|
||||
|
||||
if _, err := io.Copy(tarWriter, logFile); err != nil {
|
||||
return fmt.Errorf("failed to copy log file: %s, error: %w", logFilePath, err)
|
||||
}
|
||||
|
||||
if err := os.Truncate(logFilePath, 0); err != nil {
|
||||
return fmt.Errorf("failed to truncate log file: %s, error: %w", logFilePath, err)
|
||||
}
|
||||
|
||||
logFile, err = os.OpenFile(logFilePath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to reopen log file: %s, error: %w", logFilePath, err)
|
||||
}
|
||||
logger.SetOutput(logFile)
|
||||
|
||||
return nil
|
||||
}
|
||||
55
main.go
55
main.go
@@ -1,8 +1,10 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"log"
|
||||
"net/http"
|
||||
"time"
|
||||
@@ -10,10 +12,11 @@ import (
|
||||
"ghproxy/api"
|
||||
"ghproxy/auth"
|
||||
"ghproxy/config"
|
||||
"ghproxy/logger"
|
||||
"ghproxy/proxy"
|
||||
"ghproxy/rate"
|
||||
|
||||
"github.com/WJQSERVER-STUDIO/go-utils/logger"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
@@ -22,8 +25,16 @@ var (
|
||||
router *gin.Engine
|
||||
configfile = "/data/ghproxy/config/config.toml"
|
||||
cfgfile string
|
||||
limiter *rate.RateLimiter
|
||||
version string
|
||||
dev string
|
||||
runMode string
|
||||
limiter *rate.RateLimiter
|
||||
iplimiter *rate.IPRateLimiter
|
||||
)
|
||||
|
||||
var (
|
||||
//go:embed pages/*
|
||||
pagesFS embed.FS
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -68,7 +79,13 @@ func setupApi(cfg *config.Config, router *gin.Engine, version string) {
|
||||
|
||||
func setupRateLimit(cfg *config.Config) {
|
||||
if cfg.RateLimit.Enabled {
|
||||
limiter = rate.New(cfg.RateLimit.RatePerMinute, cfg.RateLimit.Burst, 1*time.Minute)
|
||||
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)
|
||||
} else {
|
||||
logError("Invalid RateLimit Method: %s", cfg.RateLimit.RateMethod)
|
||||
}
|
||||
logInfo("Rate Limit Loaded")
|
||||
}
|
||||
}
|
||||
@@ -81,10 +98,26 @@ func init() {
|
||||
loadlist(cfg)
|
||||
setupRateLimit(cfg)
|
||||
|
||||
gin.SetMode(gin.ReleaseMode)
|
||||
if cfg.Server.Debug {
|
||||
dev = "true"
|
||||
}
|
||||
if dev == "true" {
|
||||
gin.SetMode(gin.DebugMode)
|
||||
runMode = "dev"
|
||||
} else {
|
||||
gin.SetMode(gin.ReleaseMode)
|
||||
runMode = "release"
|
||||
}
|
||||
|
||||
router = gin.Default()
|
||||
router.UseH2C = true
|
||||
//H2C默认值为true,而后遵循cfg.Server.EnableH2C的设置
|
||||
if cfg.Server.EnableH2C == "on" {
|
||||
router.UseH2C = true
|
||||
} else if cfg.Server.EnableH2C == "" {
|
||||
router.UseH2C = true
|
||||
} else {
|
||||
router.UseH2C = false
|
||||
}
|
||||
|
||||
setupApi(cfg, router, version)
|
||||
|
||||
@@ -97,14 +130,16 @@ func init() {
|
||||
})
|
||||
router.StaticFile("/favicon.ico", faviconPath)
|
||||
} else if !cfg.Pages.Enabled {
|
||||
router.GET("/", func(c *gin.Context) {
|
||||
c.String(http.StatusForbidden, "403 Forbidden Access")
|
||||
logWarning("403 > Path:/ IP:%s UA:%s METHOD:%s HTTPv:%s", c.ClientIP(), c.Request.UserAgent(), c.Request.Method, c.Request.Proto)
|
||||
})
|
||||
pages, err := fs.Sub(pagesFS, "pages")
|
||||
if err != nil {
|
||||
log.Fatalf("Failed when processing pages: %s", err)
|
||||
}
|
||||
router.GET("/", gin.WrapH(http.FileServer(http.FS(pages))))
|
||||
router.GET("/favicon.ico", gin.WrapH(http.FileServer(http.FS(pages))))
|
||||
}
|
||||
|
||||
router.NoRoute(func(c *gin.Context) {
|
||||
proxy.NoRouteHandler(cfg, limiter)(c)
|
||||
proxy.NoRouteHandler(cfg, limiter, iplimiter, runMode)(c)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -463,7 +463,7 @@
|
||||
|
||||
<footer>
|
||||
<p>
|
||||
Copyright © 2024 WJQSERVER-STUDIO<br>
|
||||
Copyright © 2024-2025 WJQSERVER-STUDIO<br>
|
||||
GitHub仓库地址:<a href="https://github.com/WJQSERVER-STUDIO/ghproxy">WJQSERVER-STUDIO/ghproxy</a>
|
||||
<br><a href="https://t.me/ghproxy_go">Telegram交流群</a>
|
||||
</p>
|
||||
@@ -472,7 +472,7 @@
|
||||
<p>当前位置: <span id="visitor-country"></span> <img id="visitor-flag" src="" alt="" width="24" height="16"></p>
|
||||
</div>
|
||||
<script>
|
||||
fetch('https://ip.1888866.xyz/ip-lookup')
|
||||
fetch('https://ip.1888866.xyz/api/ip-lookup')
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error('网络响应失败');
|
||||
|
||||
@@ -10,9 +10,9 @@ import (
|
||||
|
||||
"ghproxy/auth"
|
||||
"ghproxy/config"
|
||||
"ghproxy/logger"
|
||||
"ghproxy/rate"
|
||||
|
||||
"github.com/WJQSERVER-STUDIO/go-utils/logger"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/imroc/req/v3"
|
||||
)
|
||||
@@ -31,14 +31,27 @@ var exps = []*regexp.Regexp{
|
||||
regexp.MustCompile(`^(?:https?://)?github\.com/([^/]+)/([^/]+)/(?:info|git-).*`),
|
||||
regexp.MustCompile(`^(?:https?://)?raw\.github(?:usercontent|)\.com/([^/]+)/([^/]+)/.+?/.+`),
|
||||
regexp.MustCompile(`^(?:https?://)?gist\.github(?:usercontent|)\.com/([^/]+)/.+?/.+`),
|
||||
regexp.MustCompile(`^(?:https?://)?api\.github\.com/repos/([^/]+)/([^/]+)/.*`),
|
||||
}
|
||||
|
||||
func NoRouteHandler(cfg *config.Config, limiter *rate.RateLimiter) gin.HandlerFunc {
|
||||
func NoRouteHandler(cfg *config.Config, limiter *rate.RateLimiter, iplimiter *rate.IPRateLimiter, runMode string) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
// 限制访问频率
|
||||
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"})
|
||||
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
|
||||
@@ -94,6 +107,16 @@ func NoRouteHandler(cfg *config.Config, limiter *rate.RateLimiter) gin.HandlerFu
|
||||
return
|
||||
}
|
||||
|
||||
// 若匹配api.github.com/repos/用户名/仓库名/路径, 则检查是否开启HeaderAuth
|
||||
if exps[5].MatchString(rawPath) {
|
||||
if cfg.Auth.AuthMethod != "header" || !cfg.Auth.Enabled {
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": "HeaderAuth is not enabled."})
|
||||
logWarning("%s %s %s %s %s HeaderAuth-Error: HeaderAuth is not enabled.", c.ClientIP(), c.Request.Method, rawPath, c.Request.Header.Get("User-Agent"), c.Request.Proto)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 处理blob/raw路径
|
||||
if exps[1].MatchString(rawPath) {
|
||||
rawPath = strings.Replace(rawPath, "/blob/", "/raw/", 1)
|
||||
}
|
||||
@@ -111,9 +134,9 @@ func NoRouteHandler(cfg *config.Config, limiter *rate.RateLimiter) gin.HandlerFu
|
||||
|
||||
switch {
|
||||
case exps[0].MatchString(rawPath), exps[1].MatchString(rawPath), exps[3].MatchString(rawPath), exps[4].MatchString(rawPath):
|
||||
ProxyRequest(c, rawPath, cfg, "chrome")
|
||||
ProxyRequest(c, rawPath, cfg, "chrome", runMode)
|
||||
case exps[2].MatchString(rawPath):
|
||||
ProxyRequest(c, rawPath, cfg, "git")
|
||||
ProxyRequest(c, rawPath, cfg, "git", runMode)
|
||||
default:
|
||||
c.String(http.StatusForbidden, "Invalid input.")
|
||||
fmt.Println("Invalid input.")
|
||||
@@ -144,15 +167,19 @@ func MatchUserRepo(rawPath string, cfg *config.Config, c *gin.Context, matches [
|
||||
return "", ""
|
||||
}
|
||||
|
||||
func ProxyRequest(c *gin.Context, u string, cfg *config.Config, mode string) {
|
||||
func ProxyRequest(c *gin.Context, u string, cfg *config.Config, mode string, runMode string) {
|
||||
method := c.Request.Method
|
||||
logInfo("%s %s %s %s %s", c.ClientIP(), method, u, c.Request.Header.Get("User-Agent"), c.Request.Proto)
|
||||
|
||||
client := createHTTPClient(mode)
|
||||
if runMode == "dev" {
|
||||
client.DevMode()
|
||||
}
|
||||
|
||||
// 发送HEAD请求, 预获取Content-Length
|
||||
headReq := client.R()
|
||||
setRequestHeaders(c, headReq)
|
||||
authPassThrough(c, cfg, headReq)
|
||||
|
||||
headResp, err := headReq.Head(u)
|
||||
if err != nil {
|
||||
@@ -174,6 +201,7 @@ func ProxyRequest(c *gin.Context, u string, cfg *config.Config, mode string) {
|
||||
|
||||
req := client.R().SetBody(body)
|
||||
setRequestHeaders(c, req)
|
||||
authPassThrough(c, cfg, req)
|
||||
|
||||
resp, err := SendRequest(c, req, method, u)
|
||||
if err != nil {
|
||||
@@ -227,6 +255,57 @@ func setRequestHeaders(c *gin.Context, req *req.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
func authPassThrough(c *gin.Context, cfg *config.Config, req *req.Request) {
|
||||
if cfg.Auth.PassThrough && cfg.Auth.AuthMethod == "parameters" && !cfg.Auth.Enabled {
|
||||
// only mode
|
||||
token := c.Query("token")
|
||||
req.SetHeader("Authorization", "token "+token)
|
||||
} else if cfg.Auth.PassThrough && cfg.Auth.AuthMethod == "header" && cfg.Auth.Enabled {
|
||||
// mix mode
|
||||
token := c.Query("token")
|
||||
req.SetHeader("Authorization", "token "+token)
|
||||
} else if cfg.Auth.PassThrough && cfg.Auth.AuthMethod == "parameters" && cfg.Auth.Enabled {
|
||||
// conflict
|
||||
logWarning("%s %s %s %s %s Auth-Error: Conflict Auth Method", c.ClientIP(), c.Request.Method, c.Request.URL.String(), c.Request.Header.Get("User-Agent"), c.Request.Proto)
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": "Conflict Auth Method"})
|
||||
return
|
||||
} else if cfg.Auth.PassThrough && cfg.Auth.AuthMethod == "header" && !cfg.Auth.Enabled {
|
||||
// only mode
|
||||
token := c.Query("token")
|
||||
req.SetHeader("Authorization", "token "+token)
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
func authPassThrough(c *gin.Context, cfg *config.Config, req *req.Request) {
|
||||
if cfg.Auth.PassThrough {
|
||||
token := c.Query("token")
|
||||
if token != "" {
|
||||
switch cfg.Auth.AuthMethod {
|
||||
case "parameters":
|
||||
if !cfg.Auth.Enabled {
|
||||
req.SetHeader("Authorization", "token "+token)
|
||||
} else {
|
||||
logWarning("%s %s %s %s %s Auth-Error: Conflict Auth Method", c.ClientIP(), c.Request.Method, c.Request.URL.String(), c.Request.Header.Get("User-Agent"), c.Request.Proto)
|
||||
// 500 Internal Server Error
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Conflict Auth Method"})
|
||||
return
|
||||
}
|
||||
case "header":
|
||||
if cfg.Auth.Enabled {
|
||||
req.SetHeader("Authorization", "token "+token)
|
||||
}
|
||||
default:
|
||||
logWarning("%s %s %s %s %s Invalid Auth Method / Auth Method is not be set", c.ClientIP(), c.Request.Method, c.Request.URL.String(), c.Request.Header.Get("User-Agent"), c.Request.Proto)
|
||||
// 500 Internal Server Error
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Invalid Auth Method / Auth Method is not be set"})
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 复制响应体
|
||||
func copyResponseBody(c *gin.Context, respBody io.Reader) error {
|
||||
_, err := io.Copy(c.Writer, respBody)
|
||||
|
||||
42
rate/rate.go
42
rate/rate.go
@@ -3,13 +3,31 @@ package rate
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/WJQSERVER-STUDIO/go-utils/logger"
|
||||
"golang.org/x/time/rate"
|
||||
)
|
||||
|
||||
// 日志输出
|
||||
var (
|
||||
logw = logger.Logw
|
||||
logInfo = logger.LogInfo
|
||||
logWarning = logger.LogWarning
|
||||
logError = logger.LogError
|
||||
)
|
||||
|
||||
// 总体限流器
|
||||
type RateLimiter struct {
|
||||
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 {
|
||||
return &RateLimiter{
|
||||
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 {
|
||||
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()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user