Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
09163ed4df |
32
.github/workflows/build-dev.yml
vendored
32
.github/workflows/build-dev.yml
vendored
@@ -9,11 +9,40 @@ on:
|
|||||||
- 'DEV-VERSION'
|
- 'DEV-VERSION'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
prepare:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: 加载版本号
|
||||||
|
run: |
|
||||||
|
if [ -f DEV-VERSION ]; then
|
||||||
|
echo "VERSION=$(cat DEV-VERSION)" >> $GITHUB_ENV
|
||||||
|
else
|
||||||
|
echo "DEV-VERSION file not found!" && exit 1
|
||||||
|
fi
|
||||||
|
- name: 输出版本号
|
||||||
|
run: |
|
||||||
|
echo "Version: ${{ env.VERSION }}"
|
||||||
|
- name: 预先创建Pre-release
|
||||||
|
id: create_release
|
||||||
|
uses: ncipollo/release-action@v1
|
||||||
|
with:
|
||||||
|
name: ${{ env.VERSION }}
|
||||||
|
artifacts: ./DEV-VERSION
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
tag: ${{ env.VERSION }}
|
||||||
|
allowUpdates: true
|
||||||
|
prerelease: true
|
||||||
|
body: ${{ env.VERSION }}
|
||||||
|
env:
|
||||||
|
export PATH: $PATH:/usr/local/go/bin
|
||||||
|
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
needs: prepare
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
goos: [linux]
|
goos: [linux, darwin, freebsd]
|
||||||
goarch: [amd64, arm64]
|
goarch: [amd64, arm64]
|
||||||
env:
|
env:
|
||||||
OUTPUT_BINARY: ghproxy
|
OUTPUT_BINARY: ghproxy
|
||||||
@@ -64,6 +93,7 @@ jobs:
|
|||||||
tag: ${{ env.VERSION }}
|
tag: ${{ env.VERSION }}
|
||||||
allowUpdates: true
|
allowUpdates: true
|
||||||
prerelease: true
|
prerelease: true
|
||||||
|
body: ${{ env.VERSION }}
|
||||||
env:
|
env:
|
||||||
export PATH: $PATH:/usr/local/go/bin
|
export PATH: $PATH:/usr/local/go/bin
|
||||||
|
|
||||||
|
|||||||
31
.github/workflows/build.yml
vendored
31
.github/workflows/build.yml
vendored
@@ -9,11 +9,39 @@ on:
|
|||||||
- 'VERSION'
|
- 'VERSION'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
prepare:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: 加载版本号
|
||||||
|
run: |
|
||||||
|
if [ -f VERSION ]; then
|
||||||
|
echo "VERSION=$(cat VERSION)" >> $GITHUB_ENV
|
||||||
|
else
|
||||||
|
echo "VERSION file not found!" && exit 1
|
||||||
|
fi
|
||||||
|
- name: 输出版本号
|
||||||
|
run: |
|
||||||
|
echo "Version: ${{ env.VERSION }}"
|
||||||
|
- name: 预先创建release
|
||||||
|
id: create_release
|
||||||
|
uses: ncipollo/release-action@v1
|
||||||
|
with:
|
||||||
|
name: ${{ env.VERSION }}
|
||||||
|
artifacts: ./VERSION
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
tag: ${{ env.VERSION }}
|
||||||
|
allowUpdates: true
|
||||||
|
body: ${{ env.VERSION }}
|
||||||
|
env:
|
||||||
|
export PATH: $PATH:/usr/local/go/bin
|
||||||
|
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
needs: prepare # 确保这个作业在 prepare 作业完成后运行
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
goos: [linux]
|
goos: [linux, darwin, freebsd]
|
||||||
goarch: [amd64, arm64]
|
goarch: [amd64, arm64]
|
||||||
env:
|
env:
|
||||||
OUTPUT_BINARY: ghproxy
|
OUTPUT_BINARY: ghproxy
|
||||||
@@ -60,6 +88,7 @@ jobs:
|
|||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
tag: ${{ env.VERSION }}
|
tag: ${{ env.VERSION }}
|
||||||
allowUpdates: true
|
allowUpdates: true
|
||||||
|
body: ${{ env.VERSION }}
|
||||||
env:
|
env:
|
||||||
export PATH: $PATH:/usr/local/go/bin
|
export PATH: $PATH:/usr/local/go/bin
|
||||||
|
|
||||||
|
|||||||
35
CHANGELOG.md
35
CHANGELOG.md
@@ -1,5 +1,40 @@
|
|||||||
# 更新日志
|
# 更新日志
|
||||||
|
|
||||||
|
2.1.0
|
||||||
|
---
|
||||||
|
- RELEASE: v2.1.0正式版发布;
|
||||||
|
- CHANGE: 加入`FreeBSD`与`Darwin`系统支持
|
||||||
|
- CHANGE: 更新安全政策, v1和24w版本序列生命周期正式结束
|
||||||
|
- ADD: 加入`timing`中间件记录响应时间
|
||||||
|
- ADD: 加入`loggin`中间件包装日志输出
|
||||||
|
- CHANGE: 更新logger版本至v1.3.0
|
||||||
|
- CHANGE: 改进日志相关
|
||||||
|
- ADD: 加入日志等级配置项
|
||||||
|
|
||||||
|
25w12d
|
||||||
|
---
|
||||||
|
- PRE-RELEASE: 此版本是v2.1.0的预发布版本,请勿在生产环境中使用;
|
||||||
|
- CHANGE: 处理类型断言相关问题
|
||||||
|
|
||||||
|
25w12c
|
||||||
|
---
|
||||||
|
- PRE-RELEASE: 此版本是v2.1.0的预发布版本,请勿在生产环境中使用;
|
||||||
|
- CHANGE: 加入`FreeBSD`与`Darwin`系统支持
|
||||||
|
|
||||||
|
25w12b
|
||||||
|
---
|
||||||
|
- PRE-RELEASE: 此版本是v2.0.8/v2.1.0的预发布版本,请勿在生产环境中使用;
|
||||||
|
- ADD: 加入`timing`中间件记录响应时间
|
||||||
|
- ADD: 加入`loggin`中间件包装日志输出
|
||||||
|
- CHANGE: 更新安全政策, v1和24w版本序列生命周期正式结束
|
||||||
|
|
||||||
|
25w12a
|
||||||
|
---
|
||||||
|
- PRE-RELEASE: 此版本是v2.0.8/v2.1.0的预发布版本,请勿在生产环境中使用;
|
||||||
|
- CHANGE: 更新logger版本至v1.3.0
|
||||||
|
- CHANGE: 改进日志相关
|
||||||
|
- ADD: 加入日志等级配置项
|
||||||
|
|
||||||
2.0.7
|
2.0.7
|
||||||
---
|
---
|
||||||
- RELEASE: v2.0.7正式版发布;
|
- RELEASE: v2.0.7正式版发布;
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
25w11a
|
25w12d
|
||||||
@@ -92,6 +92,7 @@ staticPath = "/data/www" # 静态页面文件路径
|
|||||||
[log]
|
[log]
|
||||||
logFilePath = "/data/ghproxy/log/ghproxy.log" # 日志文件路径
|
logFilePath = "/data/ghproxy/log/ghproxy.log" # 日志文件路径
|
||||||
maxLogSize = 5 # MB 日志文件最大大小
|
maxLogSize = 5 # MB 日志文件最大大小
|
||||||
|
level = "info" # 日志级别 dump, debug, info, warn, error, none
|
||||||
|
|
||||||
[cors]
|
[cors]
|
||||||
enabled = true # 是否开启跨域
|
enabled = true # 是否开启跨域
|
||||||
|
|||||||
@@ -6,8 +6,10 @@
|
|||||||
|
|
||||||
| 版本 | 是否支持 |
|
| 版本 | 是否支持 |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| v1.x.x | :white_check_mark: |
|
| v2.x.x | :white_check_mark: 当前最新版本序列, 受支持 |
|
||||||
| 24w*a/b/c... | :warning: 此为PRE-RELEASE版本,用于开发与测试,可能存在未知的问题 |
|
| v1.x.x | :x: 这些版本已结束生命周期,不再受支持 |
|
||||||
|
| 25w*a/b/c... | :warning: 此为PRE-RELEASE版本,用于开发与测试,可能存在未知的问题 |
|
||||||
|
| 24w*a/b/c... | :warning: 此为PRE-RELEASE版本,用于开发与测试,可能存在未知的问题 生命周期已完全结束 |
|
||||||
| v0.x.x | :x: 这些版本不再受支持 |
|
| v0.x.x | :x: 这些版本不再受支持 |
|
||||||
|
|
||||||
### 用户须知
|
### 用户须知
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ var (
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
logw = logger.Logw
|
logw = logger.Logw
|
||||||
|
LogDump = logger.LogDump
|
||||||
|
logDebug = logger.LogDebug
|
||||||
logInfo = logger.LogInfo
|
logInfo = logger.LogInfo
|
||||||
logWarning = logger.LogWarning
|
logWarning = logger.LogWarning
|
||||||
logError = logger.LogError
|
logError = logger.LogError
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ func AuthHeaderHandler(c *gin.Context, cfg *config.Config) (isValid bool, err st
|
|||||||
}
|
}
|
||||||
// 获取"GH-Auth"的值
|
// 获取"GH-Auth"的值
|
||||||
authToken := c.GetHeader("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)
|
logDebug("%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 == "" {
|
if authToken == "" {
|
||||||
err := "Auth Header == nil"
|
err := "Auth Header == nil"
|
||||||
return false, err
|
return false, err
|
||||||
@@ -25,6 +25,5 @@ func AuthHeaderHandler(c *gin.Context, cfg *config.Config) (isValid bool, err st
|
|||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
logInfo("auth SUCCESS: %t", isValid)
|
|
||||||
return isValid, ""
|
return isValid, ""
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ func AuthParametersHandler(c *gin.Context, cfg *config.Config) (isValid bool, er
|
|||||||
}
|
}
|
||||||
|
|
||||||
authToken := c.Query("auth_token")
|
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)
|
logDebug("%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 == "" {
|
if authToken == "" {
|
||||||
err := "Auth token == nil"
|
err := "Auth token == nil"
|
||||||
@@ -26,6 +26,5 @@ func AuthParametersHandler(c *gin.Context, cfg *config.Config) (isValid bool, er
|
|||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
logInfo("auth SUCCESS: %t", isValid)
|
|
||||||
return isValid, ""
|
return isValid, ""
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ import (
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
logw = logger.Logw
|
logw = logger.Logw
|
||||||
|
LogDump = logger.LogDump
|
||||||
|
logDebug = logger.LogDebug
|
||||||
logInfo = logger.LogInfo
|
logInfo = logger.LogInfo
|
||||||
logWarning = logger.LogWarning
|
logWarning = logger.LogWarning
|
||||||
logError = logger.LogError
|
logError = logger.LogError
|
||||||
@@ -21,7 +23,7 @@ func Init(cfg *config.Config) {
|
|||||||
if cfg.Whitelist.Enabled {
|
if cfg.Whitelist.Enabled {
|
||||||
LoadWhitelist(cfg)
|
LoadWhitelist(cfg)
|
||||||
}
|
}
|
||||||
logInfo("Auth Init")
|
logDebug("Auth Init")
|
||||||
}
|
}
|
||||||
|
|
||||||
func AuthHandler(c *gin.Context, cfg *config.Config) (isValid bool, err string) {
|
func AuthHandler(c *gin.Context, cfg *config.Config) (isValid bool, err string) {
|
||||||
@@ -32,10 +34,10 @@ func AuthHandler(c *gin.Context, cfg *config.Config) (isValid bool, err string)
|
|||||||
isValid, err = AuthHeaderHandler(c, cfg)
|
isValid, err = AuthHeaderHandler(c, cfg)
|
||||||
return isValid, err
|
return isValid, err
|
||||||
} else if cfg.Auth.AuthMethod == "" {
|
} else if cfg.Auth.AuthMethod == "" {
|
||||||
logWarning("Auth method not set")
|
logError("Auth method not set")
|
||||||
return true, ""
|
return true, ""
|
||||||
} else {
|
} else {
|
||||||
logWarning("Auth method not supported")
|
logError("Auth method not supported")
|
||||||
return false, "Auth method not supported"
|
return false, "Auth method not supported"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ type PagesConfig struct {
|
|||||||
type LogConfig struct {
|
type LogConfig struct {
|
||||||
LogFilePath string `toml:"logFilePath"`
|
LogFilePath string `toml:"logFilePath"`
|
||||||
MaxLogSize int `toml:"maxLogSize"`
|
MaxLogSize int `toml:"maxLogSize"`
|
||||||
|
Level string `toml:"level"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type CORSConfig struct {
|
type CORSConfig struct {
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ staticDir = "/data/www"
|
|||||||
[log]
|
[log]
|
||||||
logFilePath = "/data/ghproxy/log/ghproxy.log"
|
logFilePath = "/data/ghproxy/log/ghproxy.log"
|
||||||
maxLogSize = 5 # MB
|
maxLogSize = 5 # MB
|
||||||
|
level = "info" # dump, debug, info, warn, error, none
|
||||||
|
|
||||||
[cors]
|
[cors]
|
||||||
enabled = true
|
enabled = true
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ debug = false
|
|||||||
[pages]
|
[pages]
|
||||||
enabled = false
|
enabled = false
|
||||||
staticDir = "/usr/local/ghproxy/pages"
|
staticDir = "/usr/local/ghproxy/pages"
|
||||||
|
level = "info"
|
||||||
|
|
||||||
[log]
|
[log]
|
||||||
logFilePath = "/usr/local/ghproxy/log/ghproxy.log"
|
logFilePath = "/usr/local/ghproxy/log/ghproxy.log"
|
||||||
|
|||||||
6
go.mod
6
go.mod
@@ -4,7 +4,7 @@ go 1.23.6
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/BurntSushi/toml v1.4.0
|
github.com/BurntSushi/toml v1.4.0
|
||||||
github.com/WJQSERVER-STUDIO/go-utils/logger v1.2.0
|
github.com/WJQSERVER-STUDIO/go-utils/logger v1.3.0
|
||||||
github.com/gin-gonic/gin v1.10.0
|
github.com/gin-gonic/gin v1.10.0
|
||||||
golang.org/x/time v0.10.0
|
golang.org/x/time v0.10.0
|
||||||
)
|
)
|
||||||
@@ -30,10 +30,10 @@ require (
|
|||||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||||
github.com/ugorji/go/codec v1.2.12 // indirect
|
github.com/ugorji/go/codec v1.2.12 // indirect
|
||||||
golang.org/x/arch v0.14.0 // indirect
|
golang.org/x/arch v0.14.0 // indirect
|
||||||
golang.org/x/crypto v0.32.0 // indirect
|
golang.org/x/crypto v0.33.0 // indirect
|
||||||
golang.org/x/net v0.34.0 // indirect
|
golang.org/x/net v0.34.0 // indirect
|
||||||
golang.org/x/sys v0.30.0 // indirect
|
golang.org/x/sys v0.30.0 // indirect
|
||||||
golang.org/x/text v0.22.0 // indirect
|
golang.org/x/text v0.22.0 // indirect
|
||||||
google.golang.org/protobuf v1.36.4 // indirect
|
google.golang.org/protobuf v1.36.5 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
12
go.sum
12
go.sum
@@ -1,7 +1,7 @@
|
|||||||
github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
|
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/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||||
github.com/WJQSERVER-STUDIO/go-utils/logger v1.2.0 h1:6JkiVGRasfdBxEV8Qnv5x00Bq4qX1Tg6f58Ar/D2YX0=
|
github.com/WJQSERVER-STUDIO/go-utils/logger v1.3.0 h1:rOvutC4zYfvtSGN2CNZrycjtq8dLpfu7ypy7tTEErPY=
|
||||||
github.com/WJQSERVER-STUDIO/go-utils/logger v1.2.0/go.mod h1:oW884JCCPDU6c906LI0uKXndWLiRvjb9LkGYC2cqRO8=
|
github.com/WJQSERVER-STUDIO/go-utils/logger v1.3.0/go.mod h1:oW884JCCPDU6c906LI0uKXndWLiRvjb9LkGYC2cqRO8=
|
||||||
github.com/bytedance/sonic v1.12.8 h1:4xYRVRlXIgvSZ4e8iVTlMF5szgpXd4AfvuWgA8I8lgs=
|
github.com/bytedance/sonic v1.12.8 h1:4xYRVRlXIgvSZ4e8iVTlMF5szgpXd4AfvuWgA8I8lgs=
|
||||||
github.com/bytedance/sonic v1.12.8/go.mod h1:uVvFidNmlt9+wa31S1urfwwthTWteBgG0hWuoKAXTx8=
|
github.com/bytedance/sonic v1.12.8/go.mod h1:uVvFidNmlt9+wa31S1urfwwthTWteBgG0hWuoKAXTx8=
|
||||||
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
||||||
@@ -69,8 +69,8 @@ github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65E
|
|||||||
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||||
golang.org/x/arch v0.14.0 h1:z9JUEZWr8x4rR0OU6c4/4t6E6jOZ8/QBS2bBYBm4tx4=
|
golang.org/x/arch v0.14.0 h1:z9JUEZWr8x4rR0OU6c4/4t6E6jOZ8/QBS2bBYBm4tx4=
|
||||||
golang.org/x/arch v0.14.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
golang.org/x/arch v0.14.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||||
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
|
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
|
||||||
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
|
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
|
||||||
golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
|
golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
|
||||||
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
|
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
@@ -80,8 +80,8 @@ golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
|
|||||||
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
|
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
|
||||||
golang.org/x/time v0.10.0 h1:3usCWA8tQn0L8+hFJQNgzpWbd89begxN66o1Ojdn5L4=
|
golang.org/x/time v0.10.0 h1:3usCWA8tQn0L8+hFJQNgzpWbd89begxN66o1Ojdn5L4=
|
||||||
golang.org/x/time v0.10.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
golang.org/x/time v0.10.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||||
google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM=
|
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
|
||||||
google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
google.golang.org/protobuf v1.36.5/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 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
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=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
|||||||
34
loggin/loggin.go
Normal file
34
loggin/loggin.go
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
package loggin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"ghproxy/timing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/WJQSERVER-STUDIO/go-utils/logger"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
logw = logger.Logw
|
||||||
|
LogDump = logger.LogDump
|
||||||
|
logDebug = logger.LogDebug
|
||||||
|
logInfo = logger.LogInfo
|
||||||
|
logWarning = logger.LogWarning
|
||||||
|
logError = logger.LogError
|
||||||
|
)
|
||||||
|
|
||||||
|
// 日志中间件
|
||||||
|
func Middleware() gin.HandlerFunc {
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
// 处理请求
|
||||||
|
c.Next()
|
||||||
|
|
||||||
|
var timingResults time.Duration
|
||||||
|
|
||||||
|
// 获取计时结果
|
||||||
|
timingResults, _ = timing.Get(c)
|
||||||
|
|
||||||
|
// 记录日志 IP METHOD URL USERAGENT PROTOCOL STATUS TIMING
|
||||||
|
logInfo("%s %s %s %s %d %s ", c.ClientIP(), c.Request.Method, c.Request.URL.Path, c.Request.UserAgent(), c.Writer.Status(), timingResults)
|
||||||
|
}
|
||||||
|
}
|
||||||
40
main.go
40
main.go
@@ -6,15 +6,16 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"log"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"ghproxy/api"
|
"ghproxy/api"
|
||||||
"ghproxy/auth"
|
"ghproxy/auth"
|
||||||
"ghproxy/config"
|
"ghproxy/config"
|
||||||
|
"ghproxy/loggin"
|
||||||
"ghproxy/proxy"
|
"ghproxy/proxy"
|
||||||
"ghproxy/rate"
|
"ghproxy/rate"
|
||||||
|
"ghproxy/timing"
|
||||||
|
|
||||||
"github.com/WJQSERVER-STUDIO/go-utils/logger"
|
"github.com/WJQSERVER-STUDIO/go-utils/logger"
|
||||||
|
|
||||||
@@ -40,6 +41,8 @@ var (
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
logw = logger.Logw
|
logw = logger.Logw
|
||||||
|
LogDump = logger.LogDump
|
||||||
|
logDebug = logger.LogDebug
|
||||||
logInfo = logger.LogInfo
|
logInfo = logger.LogInfo
|
||||||
logWarning = logger.LogWarning
|
logWarning = logger.LogWarning
|
||||||
logError = logger.LogError
|
logError = logger.LogError
|
||||||
@@ -53,20 +56,27 @@ func loadConfig() {
|
|||||||
var err error
|
var err error
|
||||||
cfg, err = config.LoadConfig(cfgfile)
|
cfg, err = config.LoadConfig(cfgfile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Failed to load config: %v", err)
|
fmt.Printf("Failed to load config: %v\n", err)
|
||||||
|
}
|
||||||
|
if cfg.Server.Debug {
|
||||||
|
fmt.Println("Config File Path: ", cfgfile)
|
||||||
|
fmt.Printf("Loaded config: %v\n", cfg)
|
||||||
}
|
}
|
||||||
fmt.Println("Config File Path: ", cfgfile)
|
|
||||||
fmt.Printf("Loaded config: %v\n", cfg)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func setupLogger(cfg *config.Config) {
|
func setupLogger(cfg *config.Config) {
|
||||||
var err error
|
var err error
|
||||||
err = logger.Init(cfg.Log.LogFilePath, cfg.Log.MaxLogSize)
|
err = logger.Init(cfg.Log.LogFilePath, cfg.Log.MaxLogSize)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Failed to initialize logger: %v", err)
|
fmt.Printf("Failed to initialize logger: %v\n", err)
|
||||||
}
|
}
|
||||||
logInfo("Config File Path: ", cfgfile)
|
err = logger.SetLogLevel(cfg.Log.Level)
|
||||||
logInfo("Loaded config: %v\n", cfg)
|
if err != nil {
|
||||||
|
fmt.Printf("Logger Level Error: %v\n", err)
|
||||||
|
}
|
||||||
|
fmt.Printf("Log Level: %s\n", cfg.Log.Level)
|
||||||
|
logDebug("Config File Path: ", cfgfile)
|
||||||
|
logDebug("Loaded config: %v\n", cfg)
|
||||||
logInfo("Init Completed")
|
logInfo("Init Completed")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,7 +97,6 @@ func setupRateLimit(cfg *config.Config) {
|
|||||||
} else {
|
} else {
|
||||||
logError("Invalid RateLimit Method: %s", cfg.RateLimit.RateMethod)
|
logError("Invalid RateLimit Method: %s", cfg.RateLimit.RateMethod)
|
||||||
}
|
}
|
||||||
logInfo("Rate Limit Loaded")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,9 +125,20 @@ func init() {
|
|||||||
runMode = "release"
|
runMode = "release"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logDebug("Run Mode: %s", runMode)
|
||||||
|
|
||||||
gin.LoggerWithWriter(io.Discard)
|
gin.LoggerWithWriter(io.Discard)
|
||||||
router = gin.New()
|
router = gin.New()
|
||||||
|
|
||||||
|
// 添加recovery中间件
|
||||||
router.Use(gin.Recovery())
|
router.Use(gin.Recovery())
|
||||||
|
|
||||||
|
// 添加log中间件
|
||||||
|
router.Use(loggin.Middleware())
|
||||||
|
|
||||||
|
// 添加计时中间件
|
||||||
|
router.Use(timing.Middleware())
|
||||||
|
|
||||||
//H2C默认值为true,而后遵循cfg.Server.EnableH2C的设置
|
//H2C默认值为true,而后遵循cfg.Server.EnableH2C的设置
|
||||||
if cfg.Server.EnableH2C == "on" {
|
if cfg.Server.EnableH2C == "on" {
|
||||||
router.UseH2C = true
|
router.UseH2C = true
|
||||||
@@ -141,7 +161,7 @@ func init() {
|
|||||||
} else if !cfg.Pages.Enabled {
|
} else if !cfg.Pages.Enabled {
|
||||||
pages, err := fs.Sub(pagesFS, "pages")
|
pages, err := fs.Sub(pagesFS, "pages")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Failed when processing pages: %s", err)
|
logError("Failed when processing pages: %s", err)
|
||||||
}
|
}
|
||||||
router.GET("/", gin.WrapH(http.FileServer(http.FS(pages))))
|
router.GET("/", gin.WrapH(http.FileServer(http.FS(pages))))
|
||||||
router.GET("/favicon.ico", gin.WrapH(http.FileServer(http.FS(pages))))
|
router.GET("/favicon.ico", gin.WrapH(http.FileServer(http.FS(pages))))
|
||||||
@@ -152,6 +172,8 @@ func init() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
fmt.Printf("GHProxy Version: %s\n", version)
|
fmt.Printf("GHProxy Version: %s\n", version)
|
||||||
|
fmt.Printf("A Go Based High-Performance Github Proxy \n")
|
||||||
|
fmt.Printf("Made by WJQSERVER-STUDIO\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ func AuthPassThrough(c *gin.Context, cfg *config.Config, req *http.Request) {
|
|||||||
if cfg.Auth.PassThrough {
|
if cfg.Auth.PassThrough {
|
||||||
token := c.Query("token")
|
token := c.Query("token")
|
||||||
if token != "" {
|
if token != "" {
|
||||||
|
logDebug("%s %s %s %s %s Auth-PassThrough: token %s", c.ClientIP(), c.Request.Method, c.Request.URL.String(), c.Request.Header.Get("User-Agent"), c.Request.Proto, token)
|
||||||
switch cfg.Auth.AuthMethod {
|
switch cfg.Auth.AuthMethod {
|
||||||
case "parameters":
|
case "parameters":
|
||||||
if !cfg.Auth.Enabled {
|
if !cfg.Auth.Enabled {
|
||||||
|
|||||||
@@ -54,12 +54,11 @@ func initChunkedHTTPClient() {
|
|||||||
|
|
||||||
func ChunkedProxyRequest(c *gin.Context, u string, cfg *config.Config, mode string, runMode string) {
|
func ChunkedProxyRequest(c *gin.Context, u string, cfg *config.Config, mode string, runMode string) {
|
||||||
method := c.Request.Method
|
method := c.Request.Method
|
||||||
logInfo("%s %s %s %s %s", c.ClientIP(), method, u, c.Request.Header.Get("User-Agent"), c.Request.Proto)
|
|
||||||
|
|
||||||
// 发送HEAD请求, 预获取Content-Length
|
// 发送HEAD请求, 预获取Content-Length
|
||||||
headReq, err := http.NewRequest("HEAD", u, nil)
|
headReq, err := http.NewRequest("HEAD", u, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
HandleError(c, fmt.Sprintf("创建HEAD请求失败: %v", err))
|
HandleError(c, fmt.Sprintf("Failed to create request: %v", err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
setRequestHeaders(c, headReq)
|
setRequestHeaders(c, headReq)
|
||||||
@@ -108,7 +107,7 @@ func ChunkedProxyRequest(c *gin.Context, u string, cfg *config.Config, mode stri
|
|||||||
// 创建请求
|
// 创建请求
|
||||||
req, err := http.NewRequest(method, u, bodyReader)
|
req, err := http.NewRequest(method, u, bodyReader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
HandleError(c, fmt.Sprintf("创建请求失败: %v", err))
|
HandleError(c, fmt.Sprintf("Failed to create request: %v", err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,7 +117,7 @@ func ChunkedProxyRequest(c *gin.Context, u string, cfg *config.Config, mode stri
|
|||||||
|
|
||||||
resp, err := cclient.Do(req)
|
resp, err := cclient.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
HandleError(c, fmt.Sprintf("发送请求失败: %v", err))
|
HandleError(c, fmt.Sprintf("Failed to send request: %v", err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
@@ -171,7 +170,7 @@ func ChunkedProxyRequest(c *gin.Context, u string, cfg *config.Config, mode stri
|
|||||||
|
|
||||||
_, err = io.CopyBuffer(c.Writer, resp.Body, buffer)
|
_, err = io.CopyBuffer(c.Writer, resp.Body, buffer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logError("%s %s %s %s %s 响应复制错误: %v", c.ClientIP(), method, u, c.Request.Header.Get("User-Agent"), c.Request.Proto, err)
|
logError("%s %s %s %s %s Failed to copy response body: %v", c.ClientIP(), method, u, c.Request.Header.Get("User-Agent"), c.Request.Proto, err)
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
c.Writer.Flush() // 确保刷入
|
c.Writer.Flush() // 确保刷入
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ func GitReq(c *gin.Context, u string, cfg *config.Config, mode string, runMode s
|
|||||||
// 发送HEAD请求, 预获取Content-Length
|
// 发送HEAD请求, 预获取Content-Length
|
||||||
headReq, err := http.NewRequest("HEAD", u, nil)
|
headReq, err := http.NewRequest("HEAD", u, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
HandleError(c, fmt.Sprintf("创建HEAD请求失败: %v", err))
|
HandleError(c, fmt.Sprintf("Failed to create request: %v", err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
setRequestHeaders(c, headReq)
|
setRequestHeaders(c, headReq)
|
||||||
@@ -80,7 +80,7 @@ func GitReq(c *gin.Context, u string, cfg *config.Config, mode string, runMode s
|
|||||||
// 创建请求
|
// 创建请求
|
||||||
req, err := http.NewRequest(method, u, bodyReader)
|
req, err := http.NewRequest(method, u, bodyReader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
HandleError(c, fmt.Sprintf("创建请求失败: %v", err))
|
HandleError(c, fmt.Sprintf("Failed to create request: %v", err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
setRequestHeaders(c, req)
|
setRequestHeaders(c, req)
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import (
|
|||||||
|
|
||||||
func NoRouteHandler(cfg *config.Config, limiter *rate.RateLimiter, iplimiter *rate.IPRateLimiter, runMode string) gin.HandlerFunc {
|
func NoRouteHandler(cfg *config.Config, limiter *rate.RateLimiter, iplimiter *rate.IPRateLimiter, runMode string) gin.HandlerFunc {
|
||||||
return func(c *gin.Context) {
|
return func(c *gin.Context) {
|
||||||
|
|
||||||
// 限制访问频率
|
// 限制访问频率
|
||||||
if cfg.RateLimit.Enabled {
|
if cfg.RateLimit.Enabled {
|
||||||
|
|
||||||
@@ -54,6 +55,8 @@ func NoRouteHandler(cfg *config.Config, limiter *rate.RateLimiter, iplimiter *ra
|
|||||||
username, repo := MatchUserRepo(rawPath, cfg, c, matches) // 匹配用户名和仓库名
|
username, repo := MatchUserRepo(rawPath, cfg, c, matches) // 匹配用户名和仓库名
|
||||||
|
|
||||||
logInfo("%s %s %s %s %s Matched-Username: %s, Matched-Repo: %s", c.ClientIP(), c.Request.Method, rawPath, c.Request.Header.Get("User-Agent"), c.Request.Proto, username, repo)
|
logInfo("%s %s %s %s %s Matched-Username: %s, Matched-Repo: %s", c.ClientIP(), c.Request.Method, rawPath, c.Request.Header.Get("User-Agent"), c.Request.Proto, username, repo)
|
||||||
|
// dump log 记录详细信息 c.ClientIP(), c.Request.Method, rawPath, c.Request.Header.Get("User-Agent"), c.Request.Proto, full Header
|
||||||
|
LogDump("%s %s %s %s %s %s", c.ClientIP(), c.Request.Method, rawPath, c.Request.Header.Get("User-Agent"), c.Request.Proto, c.Request.Header)
|
||||||
repouser := fmt.Sprintf("%s/%s", username, repo)
|
repouser := fmt.Sprintf("%s/%s", username, repo)
|
||||||
|
|
||||||
// 白名单检查
|
// 白名单检查
|
||||||
@@ -83,7 +86,7 @@ func NoRouteHandler(cfg *config.Config, limiter *rate.RateLimiter, iplimiter *ra
|
|||||||
matches = CheckURL(rawPath, c)
|
matches = CheckURL(rawPath, c)
|
||||||
if matches == nil {
|
if matches == nil {
|
||||||
c.AbortWithStatus(http.StatusNotFound)
|
c.AbortWithStatus(http.StatusNotFound)
|
||||||
logError("%s %s %s %s %s 404-NOMATCH", c.ClientIP(), c.Request.Method, rawPath, c.Request.Header.Get("User-Agent"), c.Request.Proto)
|
logWarning("%s %s %s %s %s 404-NOMATCH", c.ClientIP(), c.Request.Method, rawPath, c.Request.Header.Get("User-Agent"), c.Request.Proto)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,7 +94,7 @@ func NoRouteHandler(cfg *config.Config, limiter *rate.RateLimiter, iplimiter *ra
|
|||||||
if exps[5].MatchString(rawPath) {
|
if exps[5].MatchString(rawPath) {
|
||||||
if cfg.Auth.AuthMethod != "header" || !cfg.Auth.Enabled {
|
if cfg.Auth.AuthMethod != "header" || !cfg.Auth.Enabled {
|
||||||
c.JSON(http.StatusForbidden, gin.H{"error": "HeaderAuth is not 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)
|
logError("%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
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -110,7 +113,7 @@ func NoRouteHandler(cfg *config.Config, limiter *rate.RateLimiter, iplimiter *ra
|
|||||||
}
|
}
|
||||||
|
|
||||||
// IP METHOD URL USERAGENT PROTO MATCHES
|
// IP METHOD URL USERAGENT PROTO MATCHES
|
||||||
logInfo("%s %s %s %s %s Matches: %v", c.ClientIP(), c.Request.Method, rawPath, c.Request.Header.Get("User-Agent"), c.Request.Proto, matches)
|
logDebug("%s %s %s %s %s Matches: %v", c.ClientIP(), c.Request.Method, rawPath, c.Request.Header.Get("User-Agent"), c.Request.Proto, matches)
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case exps[0].MatchString(rawPath), exps[1].MatchString(rawPath), exps[3].MatchString(rawPath), exps[4].MatchString(rawPath):
|
case exps[0].MatchString(rawPath), exps[1].MatchString(rawPath), exps[3].MatchString(rawPath), exps[4].MatchString(rawPath):
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ var (
|
|||||||
// 提取用户名和仓库名
|
// 提取用户名和仓库名
|
||||||
func MatchUserRepo(rawPath string, cfg *config.Config, c *gin.Context, matches []string) (string, string) {
|
func MatchUserRepo(rawPath string, cfg *config.Config, c *gin.Context, matches []string) (string, string) {
|
||||||
if gistMatches := gistRegex.FindStringSubmatch(rawPath); len(gistMatches) == 3 {
|
if gistMatches := gistRegex.FindStringSubmatch(rawPath); len(gistMatches) == 3 {
|
||||||
logInfo("%s %s %s %s %s Matched-Username: %s", c.ClientIP(), c.Request.Method, rawPath, c.Request.Header.Get("User-Agent"), c.Request.Proto, gistMatches[1])
|
LogDump("%s %s %s %s %s Matched-Username: %s", c.ClientIP(), c.Request.Method, rawPath, c.Request.Header.Get("User-Agent"), c.Request.Proto, gistMatches[1])
|
||||||
return gistMatches[1], ""
|
return gistMatches[1], ""
|
||||||
}
|
}
|
||||||
// 定义路径
|
// 定义路径
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ import (
|
|||||||
// 日志模块
|
// 日志模块
|
||||||
var (
|
var (
|
||||||
logw = logger.Logw
|
logw = logger.Logw
|
||||||
|
LogDump = logger.LogDump
|
||||||
|
logDebug = logger.LogDebug
|
||||||
logInfo = logger.LogInfo
|
logInfo = logger.LogInfo
|
||||||
logWarning = logger.LogWarning
|
logWarning = logger.LogWarning
|
||||||
logError = logger.LogError
|
logError = logger.LogError
|
||||||
@@ -31,6 +33,7 @@ var exps = []*regexp.Regexp{
|
|||||||
func readRequestBody(c *gin.Context) ([]byte, error) {
|
func readRequestBody(c *gin.Context) ([]byte, error) {
|
||||||
body, err := io.ReadAll(c.Request.Body)
|
body, err := io.ReadAll(c.Request.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
logError("failed to read request body: %v", err)
|
||||||
return nil, fmt.Errorf("failed to read request body: %v", err)
|
return nil, fmt.Errorf("failed to read request body: %v", err)
|
||||||
}
|
}
|
||||||
defer c.Request.Body.Close()
|
defer c.Request.Body.Close()
|
||||||
@@ -59,7 +62,7 @@ func SendRequest(c *gin.Context, req *req.Request, method, url string) (*req.Res
|
|||||||
|
|
||||||
func HandleError(c *gin.Context, message string) {
|
func HandleError(c *gin.Context, message string) {
|
||||||
c.String(http.StatusInternalServerError, fmt.Sprintf("server error %v", message))
|
c.String(http.StatusInternalServerError, fmt.Sprintf("server error %v", message))
|
||||||
logWarning(message)
|
logError(message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func CheckURL(u string, c *gin.Context) []string {
|
func CheckURL(u string, c *gin.Context) []string {
|
||||||
@@ -69,7 +72,7 @@ func CheckURL(u string, c *gin.Context) []string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
errMsg := fmt.Sprintf("%s %s %s %s %s Invalid URL", c.ClientIP(), c.Request.Method, u, c.Request.Header.Get("User-Agent"), c.Request.Proto)
|
errMsg := fmt.Sprintf("%s %s %s %s %s Invalid URL", c.ClientIP(), c.Request.Method, u, c.Request.Header.Get("User-Agent"), c.Request.Proto)
|
||||||
logWarning(errMsg)
|
logError(errMsg)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ import (
|
|||||||
// 日志输出
|
// 日志输出
|
||||||
var (
|
var (
|
||||||
logw = logger.Logw
|
logw = logger.Logw
|
||||||
|
LogDump = logger.LogDump
|
||||||
|
logDebug = logger.LogDebug
|
||||||
logInfo = logger.LogInfo
|
logInfo = logger.LogInfo
|
||||||
logWarning = logger.LogWarning
|
logWarning = logger.LogWarning
|
||||||
logError = logger.LogError
|
logError = logger.LogError
|
||||||
|
|||||||
86
timing/timing.go
Normal file
86
timing/timing.go
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
package timing
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 阶段计时结构(固定数组优化)
|
||||||
|
type timingData struct {
|
||||||
|
phases [8]struct { // 预分配8个阶段存储
|
||||||
|
name string
|
||||||
|
dur time.Duration
|
||||||
|
}
|
||||||
|
count int
|
||||||
|
start time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
// 对象池(内存重用优化)
|
||||||
|
var pool = sync.Pool{
|
||||||
|
New: func() interface{} {
|
||||||
|
return new(timingData)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// 中间件入口
|
||||||
|
func Middleware() gin.HandlerFunc {
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
// 从池中获取计时器
|
||||||
|
td := pool.Get().(*timingData)
|
||||||
|
td.start = time.Now()
|
||||||
|
td.count = 0
|
||||||
|
|
||||||
|
// 存储到上下文
|
||||||
|
c.Set("timing", td)
|
||||||
|
|
||||||
|
// 请求完成后回收对象
|
||||||
|
defer func() {
|
||||||
|
pool.Put(td)
|
||||||
|
}()
|
||||||
|
|
||||||
|
c.Next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 记录阶段耗时
|
||||||
|
func Record(c *gin.Context, name string) {
|
||||||
|
if val, exists := c.Get("timing"); exists {
|
||||||
|
//td := val.(*timingData)
|
||||||
|
td, ok := val.(*timingData)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if td.count < len(td.phases) {
|
||||||
|
td.phases[td.count].name = name
|
||||||
|
td.phases[td.count].dur = time.Since(td.start) // 直接记录当前时间
|
||||||
|
td.count++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取计时结果(日志输出用)
|
||||||
|
func Get(c *gin.Context) (total time.Duration, phases []struct {
|
||||||
|
Name string
|
||||||
|
Dur time.Duration
|
||||||
|
}) {
|
||||||
|
if val, exists := c.Get("timing"); exists {
|
||||||
|
//td := val.(*timingData)
|
||||||
|
td, ok := val.(*timingData)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for i := 0; i < td.count; i++ {
|
||||||
|
phases = append(phases, struct {
|
||||||
|
Name string
|
||||||
|
Dur time.Duration
|
||||||
|
}{
|
||||||
|
Name: td.phases[i].name,
|
||||||
|
Dur: td.phases[i].dur,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
total = time.Since(td.start)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user