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' - '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

View File

@@ -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

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

View File

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

View File

@@ -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 # 是否开启跨域

View File

@@ -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: 这些版本不再受支持 |
### 用户须知 ### 用户须知

View File

@@ -1 +1 @@
2.0.7 2.1.0

View File

@@ -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

View File

@@ -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, ""
} }

View File

@@ -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, ""
} }

View File

@@ -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"
} }
} }

View File

@@ -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 {

View File

@@ -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

View File

@@ -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
View File

@@ -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
View File

@@ -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
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" "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() {

View File

@@ -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 {

View File

@@ -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() // 确保刷入

View File

@@ -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)

View File

@@ -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):

View File

@@ -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], ""
} }
// 定义路径 // 定义路径

View File

@@ -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
} }

View File

@@ -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
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
}