Compare commits

...

1 Commits

Author SHA1 Message Date
WJQSERVER
09163ed4df 2.1.0 (#47)
2.1.0
---
- RELEASE: v2.1.0正式版发布;
- CHANGE: 加入`FreeBSD`与`Darwin`系统支持
- CHANGE: 更新安全政策, v1和24w版本序列生命周期正式结束
- ADD: 加入`timing`中间件记录响应时间
- ADD: 加入`loggin`中间件包装日志输出
- CHANGE: 更新logger版本至v1.3.0
- CHANGE: 改进日志相关
- ADD: 加入日志等级配置项
2025-02-09 23:13:57 +08:00
26 changed files with 296 additions and 44 deletions

View File

@@ -9,11 +9,40 @@ on:
- 'DEV-VERSION'
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:
runs-on: ubuntu-latest
needs: prepare
strategy:
matrix:
goos: [linux]
goos: [linux, darwin, freebsd]
goarch: [amd64, arm64]
env:
OUTPUT_BINARY: ghproxy
@@ -64,6 +93,7 @@ jobs:
tag: ${{ env.VERSION }}
allowUpdates: true
prerelease: true
body: ${{ env.VERSION }}
env:
export PATH: $PATH:/usr/local/go/bin

View File

@@ -9,11 +9,39 @@ on:
- 'VERSION'
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:
runs-on: ubuntu-latest
needs: prepare # 确保这个作业在 prepare 作业完成后运行
strategy:
matrix:
goos: [linux]
goos: [linux, darwin, freebsd]
goarch: [amd64, arm64]
env:
OUTPUT_BINARY: ghproxy
@@ -60,6 +88,7 @@ jobs:
token: ${{ secrets.GITHUB_TOKEN }}
tag: ${{ env.VERSION }}
allowUpdates: true
body: ${{ env.VERSION }}
env:
export PATH: $PATH:/usr/local/go/bin

View File

@@ -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
---
- RELEASE: v2.0.7正式版发布;

View File

@@ -1 +1 @@
25w11a
25w12d

View File

@@ -92,6 +92,7 @@ staticPath = "/data/www" # 静态页面文件路径
[log]
logFilePath = "/data/ghproxy/log/ghproxy.log" # 日志文件路径
maxLogSize = 5 # MB 日志文件最大大小
level = "info" # 日志级别 dump, debug, info, warn, error, none
[cors]
enabled = true # 是否开启跨域

View File

@@ -6,8 +6,10 @@
| 版本 | 是否支持 |
| --- | --- |
| v1.x.x | :white_check_mark: |
| 24w*a/b/c... | :warning: 此为PRE-RELEASE版本,用于开发与测试,可能存在未知的问题 |
| v2.x.x | :white_check_mark: 当前最新版本序列, 受支持 |
| v1.x.x | :x: 这些版本已结束生命周期,不再受支持 |
| 25w*a/b/c... | :warning: 此为PRE-RELEASE版本,用于开发与测试,可能存在未知的问题 |
| 24w*a/b/c... | :warning: 此为PRE-RELEASE版本,用于开发与测试,可能存在未知的问题 生命周期已完全结束 |
| v0.x.x | :x: 这些版本不再受支持 |
### 用户须知

View File

@@ -1 +1 @@
2.0.7
2.1.0

View File

@@ -15,6 +15,8 @@ var (
var (
logw = logger.Logw
LogDump = logger.LogDump
logDebug = logger.LogDebug
logInfo = logger.LogInfo
logWarning = logger.LogWarning
logError = logger.LogError

View File

@@ -13,7 +13,7 @@ func AuthHeaderHandler(c *gin.Context, cfg *config.Config) (isValid bool, err st
}
// 获取"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 == "" {
err := "Auth Header == nil"
return false, err
@@ -25,6 +25,5 @@ func AuthHeaderHandler(c *gin.Context, cfg *config.Config) (isValid bool, err st
return false, err
}
logInfo("auth SUCCESS: %t", isValid)
return isValid, ""
}

View File

@@ -13,7 +13,7 @@ func AuthParametersHandler(c *gin.Context, cfg *config.Config) (isValid bool, er
}
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 == "" {
err := "Auth token == nil"
@@ -26,6 +26,5 @@ func AuthParametersHandler(c *gin.Context, cfg *config.Config) (isValid bool, er
return false, err
}
logInfo("auth SUCCESS: %t", isValid)
return isValid, ""
}

View File

@@ -9,6 +9,8 @@ import (
var (
logw = logger.Logw
LogDump = logger.LogDump
logDebug = logger.LogDebug
logInfo = logger.LogInfo
logWarning = logger.LogWarning
logError = logger.LogError
@@ -21,7 +23,7 @@ func Init(cfg *config.Config) {
if cfg.Whitelist.Enabled {
LoadWhitelist(cfg)
}
logInfo("Auth Init")
logDebug("Auth Init")
}
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)
return isValid, err
} else if cfg.Auth.AuthMethod == "" {
logWarning("Auth method not set")
logError("Auth method not set")
return true, ""
} else {
logWarning("Auth method not supported")
logError("Auth method not supported")
return false, "Auth method not supported"
}
}

View File

@@ -31,6 +31,7 @@ type PagesConfig struct {
type LogConfig struct {
LogFilePath string `toml:"logFilePath"`
MaxLogSize int `toml:"maxLogSize"`
Level string `toml:"level"`
}
type CORSConfig struct {

View File

@@ -12,6 +12,7 @@ staticDir = "/data/www"
[log]
logFilePath = "/data/ghproxy/log/ghproxy.log"
maxLogSize = 5 # MB
level = "info" # dump, debug, info, warn, error, none
[cors]
enabled = true

View File

@@ -8,6 +8,7 @@ debug = false
[pages]
enabled = false
staticDir = "/usr/local/ghproxy/pages"
level = "info"
[log]
logFilePath = "/usr/local/ghproxy/log/ghproxy.log"

6
go.mod
View File

@@ -4,7 +4,7 @@ go 1.23.6
require (
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
golang.org/x/time v0.10.0
)
@@ -30,10 +30,10 @@ require (
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.12 // 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/sys v0.30.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
)

12
go.sum
View File

@@ -1,7 +1,7 @@
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.2.0 h1:6JkiVGRasfdBxEV8Qnv5x00Bq4qX1Tg6f58Ar/D2YX0=
github.com/WJQSERVER-STUDIO/go-utils/logger v1.2.0/go.mod h1:oW884JCCPDU6c906LI0uKXndWLiRvjb9LkGYC2cqRO8=
github.com/WJQSERVER-STUDIO/go-utils/logger v1.3.0 h1:rOvutC4zYfvtSGN2CNZrycjtq8dLpfu7ypy7tTEErPY=
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/go.mod h1:uVvFidNmlt9+wa31S1urfwwthTWteBgG0hWuoKAXTx8=
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=
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/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
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/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
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/time v0.10.0 h1:3usCWA8tQn0L8+hFJQNgzpWbd89begxN66o1Ojdn5L4=
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.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
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/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

34
loggin/loggin.go Normal file
View 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
View File

@@ -6,15 +6,16 @@ import (
"fmt"
"io"
"io/fs"
"log"
"net/http"
"time"
"ghproxy/api"
"ghproxy/auth"
"ghproxy/config"
"ghproxy/loggin"
"ghproxy/proxy"
"ghproxy/rate"
"ghproxy/timing"
"github.com/WJQSERVER-STUDIO/go-utils/logger"
@@ -40,6 +41,8 @@ var (
var (
logw = logger.Logw
LogDump = logger.LogDump
logDebug = logger.LogDebug
logInfo = logger.LogInfo
logWarning = logger.LogWarning
logError = logger.LogError
@@ -53,20 +56,27 @@ func loadConfig() {
var err error
cfg, err = config.LoadConfig(cfgfile)
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) {
var err error
err = logger.Init(cfg.Log.LogFilePath, cfg.Log.MaxLogSize)
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)
logInfo("Loaded config: %v\n", cfg)
err = logger.SetLogLevel(cfg.Log.Level)
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")
}
@@ -87,7 +97,6 @@ func setupRateLimit(cfg *config.Config) {
} else {
logError("Invalid RateLimit Method: %s", cfg.RateLimit.RateMethod)
}
logInfo("Rate Limit Loaded")
}
}
@@ -116,9 +125,20 @@ func init() {
runMode = "release"
}
logDebug("Run Mode: %s", runMode)
gin.LoggerWithWriter(io.Discard)
router = gin.New()
// 添加recovery中间件
router.Use(gin.Recovery())
// 添加log中间件
router.Use(loggin.Middleware())
// 添加计时中间件
router.Use(timing.Middleware())
//H2C默认值为true而后遵循cfg.Server.EnableH2C的设置
if cfg.Server.EnableH2C == "on" {
router.UseH2C = true
@@ -141,7 +161,7 @@ func init() {
} else if !cfg.Pages.Enabled {
pages, err := fs.Sub(pagesFS, "pages")
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("/favicon.ico", gin.WrapH(http.FileServer(http.FS(pages))))
@@ -152,6 +172,8 @@ func init() {
})
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() {

View File

@@ -11,6 +11,7 @@ func AuthPassThrough(c *gin.Context, cfg *config.Config, req *http.Request) {
if cfg.Auth.PassThrough {
token := c.Query("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 {
case "parameters":
if !cfg.Auth.Enabled {

View File

@@ -54,12 +54,11 @@ func initChunkedHTTPClient() {
func ChunkedProxyRequest(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)
// 发送HEAD请求, 预获取Content-Length
headReq, err := http.NewRequest("HEAD", u, nil)
if err != nil {
HandleError(c, fmt.Sprintf("创建HEAD请求失败: %v", err))
HandleError(c, fmt.Sprintf("Failed to create request: %v", err))
return
}
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)
if err != nil {
HandleError(c, fmt.Sprintf("创建请求失败: %v", err))
HandleError(c, fmt.Sprintf("Failed to create request: %v", err))
return
}
@@ -118,7 +117,7 @@ func ChunkedProxyRequest(c *gin.Context, u string, cfg *config.Config, mode stri
resp, err := cclient.Do(req)
if err != nil {
HandleError(c, fmt.Sprintf("发送请求失败: %v", err))
HandleError(c, fmt.Sprintf("Failed to send request: %v", err))
return
}
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)
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
} else {
c.Writer.Flush() // 确保刷入

View File

@@ -38,7 +38,7 @@ func GitReq(c *gin.Context, u string, cfg *config.Config, mode string, runMode s
// 发送HEAD请求, 预获取Content-Length
headReq, err := http.NewRequest("HEAD", u, nil)
if err != nil {
HandleError(c, fmt.Sprintf("创建HEAD请求失败: %v", err))
HandleError(c, fmt.Sprintf("Failed to create request: %v", err))
return
}
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)
if err != nil {
HandleError(c, fmt.Sprintf("创建请求失败: %v", err))
HandleError(c, fmt.Sprintf("Failed to create request: %v", err))
return
}
setRequestHeaders(c, req)

View File

@@ -14,6 +14,7 @@ import (
func NoRouteHandler(cfg *config.Config, limiter *rate.RateLimiter, iplimiter *rate.IPRateLimiter, runMode string) gin.HandlerFunc {
return func(c *gin.Context) {
// 限制访问频率
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) // 匹配用户名和仓库名
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)
// 白名单检查
@@ -83,7 +86,7 @@ func NoRouteHandler(cfg *config.Config, limiter *rate.RateLimiter, iplimiter *ra
matches = CheckURL(rawPath, c)
if matches == nil {
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
}
@@ -91,7 +94,7 @@ func NoRouteHandler(cfg *config.Config, limiter *rate.RateLimiter, iplimiter *ra
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)
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
}
}
@@ -110,7 +113,7 @@ func NoRouteHandler(cfg *config.Config, limiter *rate.RateLimiter, iplimiter *ra
}
// 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 {
case exps[0].MatchString(rawPath), exps[1].MatchString(rawPath), exps[3].MatchString(rawPath), exps[4].MatchString(rawPath):

View File

@@ -18,7 +18,7 @@ var (
// 提取用户名和仓库名
func MatchUserRepo(rawPath string, cfg *config.Config, c *gin.Context, matches []string) (string, string) {
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], ""
}
// 定义路径

View File

@@ -13,6 +13,8 @@ import (
// 日志模块
var (
logw = logger.Logw
LogDump = logger.LogDump
logDebug = logger.LogDebug
logInfo = logger.LogInfo
logWarning = logger.LogWarning
logError = logger.LogError
@@ -31,6 +33,7 @@ var exps = []*regexp.Regexp{
func readRequestBody(c *gin.Context) ([]byte, error) {
body, err := io.ReadAll(c.Request.Body)
if err != nil {
logError("failed to read request body: %v", err)
return nil, fmt.Errorf("failed to read request body: %v", err)
}
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) {
c.String(http.StatusInternalServerError, fmt.Sprintf("server error %v", message))
logWarning(message)
logError(message)
}
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)
logWarning(errMsg)
logError(errMsg)
return nil
}

View File

@@ -10,6 +10,8 @@ import (
// 日志输出
var (
logw = logger.Logw
LogDump = logger.LogDump
logDebug = logger.LogDebug
logInfo = logger.LogInfo
logWarning = logger.LogWarning
logError = logger.LogError

86
timing/timing.go Normal file
View 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
}