This commit is contained in:
WJQSERVER 2024-10-24 21:50:38 +08:00
parent c31e887ad3
commit 575e36ef90
7 changed files with 252 additions and 23 deletions

51
.github/workflows/build-nocache.yml vendored Normal file
View file

@ -0,0 +1,51 @@
name: Build NoCache Docker Image
on:
workflow_dispatch:
push:
branches:
- 'main'
paths:
- 'VERSION'
jobs:
docker:
runs-on: ubuntu-latest
env:
IMAGE_NAME: wjqserver/ghproxy # 定义镜像名称变量
DOCKERFILE: docker/dockerfile/nocache/Dockerfile # 定义 Dockerfile 路径变量
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Load VERSION
run: |
if [ -f VERSION ]; then
echo "VERSION=$(cat VERSION)" >> $GITHUB_ENV
else
echo "VERSION file not found!" && exit 1
fi
- name: Wait for Compile
run: sleep 300s
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: 构建镜像
uses: docker/build-push-action@v6
with:
file: ./${{ env.DOCKERFILE }}
platforms: linux/amd64,linux/arm64
push: true
tags: |
${{ env.IMAGE_NAME }}:${{ env.VERSION }}-nocache
${{ env.IMAGE_NAME }}:nocache

View file

@ -1,5 +1,12 @@
# 更新日志
24w19d
---
- PRE-RELEASE: 此版本是v1.6.1的预发布版本,请勿在生产环境中使用
- ADD: 新增nocache版本,供由用户自行优化缓存策略
- CHANGE: 优化`Proxy`核心模块内部结构,提升性能
- REMOVE: 移除`Proxy`模块内部分无用`logInfo`
24w19c
---
- PRE-RELEASE: 此版本是v1.6.1的预发布版本,请勿在生产环境中使用

View file

@ -0,0 +1,99 @@
{
debug
http_port 80
https_port 443
order cache before rewrite
cache {
cache_name GhProxyCache
}
log {
level INFO
output file /data/caddy/log/caddy.log {
roll_size 5MB
roll_keep 10
}
}
server :80 {
protocols h1 h2 h2c
}
}
(log) {
log {
format transform `{request>headers>X-Forwarded-For>[0]:request>remote_ip} - {user_id} [{ts}] "{request>method} {request>uri} {request>proto}" {status} {size} "{request>headers>Referer>[0]}" "{request>headers>User-Agent>[0]}"` {
time_format "02/Jan/2006:15:04:05 -0700"
}
output file /data/caddy/log/{args[0]}/access.log {
roll_size 5MB
roll_keep 10
roll_keep_for 24h
}
}
}
(error_page) {
handle_errors {
rewrite * /{err.status_code}.html
root * /data/caddy/pages/errors
file_server
}
}
(encode) {
encode {
zstd best
br 5 v2
gzip 5
minimum_length 256
}
}
(cache) {
cache {
allowed_http_verbs GET
stale {args[0]}
ttl {args[1]}
}
}
(header_realip) {
header_up X-Real-IP {remote_host}
header_up X-Real-IP {http.request.header.CF-Connecting-IP}
header_up X-Forwarded-For {http.request.header.CF-Connecting-IP}
header_up X-Forwarded-Proto {http.request.header.CF-Visitor}
}
(rate_limit) {
route /* {
rate_limit {remote.ip} {args[0]}r/m 10000 429
}
}
:80 {
reverse_proxy {
to h2c://127.0.0.1:8080
import header_realip
}
import log ghproxy
import error_page
import encode
import rate_limit 60
route / {
root /data/www
file_server
import cache 300s
}
route /favicon.ico {
root /data/www
file_server
import cache 300s
}
route /api* {
rate_limit {remote.ip} 15r/m 10000 429
import cache 300s
}
}
import /data/caddy/config.d/*

View file

@ -0,0 +1,49 @@
FROM wjqserver/caddy:2.9.0-rc-alpine AS builder
ARG USER=WJQSERVER-STUDIO
ARG REPO=ghproxy
ARG APPLICATION=ghproxy
ARG TARGETOS
ARG TARGETARCH
ARG TARGETPLATFORM
# 创建文件夹
RUN mkdir -p /data/www
RUN mkdir -p /data/${APPLICATION}/config
RUN mkdir -p /data/${APPLICATION}/log
# 安装依赖
RUN apk add --no-cache curl wget
# 前端
RUN wget -O /data/www/index.html https://raw.githubusercontent.com/${USER}/${REPO}/main/pages/index.html
RUN wget -O /data/www/favicon.ico https://raw.githubusercontent.com/${USER}/${REPO}/main/pages/favicon.ico
# 后端
RUN VERSION=$(curl -s https://raw.githubusercontent.com/${USER}/${REPO}/main/VERSION) && \
wget -O /data/${APPLICATION}/${APPLICATION} https://github.com/${USER}/${REPO}/releases/download/$VERSION/${APPLICATION}-${TARGETOS}-${TARGETARCH}
RUN wget -O /usr/local/bin/init.sh https://raw.githubusercontent.com/${USER}/${REPO}/main/docker/dockerfile/nocache/init.sh
# 拉取配置
RUN wget -O /data/caddy/Caddyfile https://raw.githubusercontent.com/${USER}/${REPO}/main/caddyfile/nocache/Caddyfile
RUN wget -O /data/${APPLICATION}/config.toml https://raw.githubusercontent.com/${USER}/${REPO}/main/config/config.toml
RUN wget -O /data/${APPLICATION}/blacklist.json https://raw.githubusercontent.com/${USER}/${REPO}/main/config/blacklist.json
RUN wget -O /data/${APPLICATION}/whitelist.json https://raw.githubusercontent.com/${USER}/${REPO}/main/config/whitelist.json
# 权限
RUN chmod +x /data/${APPLICATION}/${APPLICATION}
RUN chmod +x /usr/local/bin/init.sh
FROM wjqserver/caddy:2.9.0-rc-alpine
COPY --from=builder /data/www /data/www
COPY --from=builder /data/caddy /data/caddy
COPY --from=builder /data/${APPLICATION} /data/${APPLICATION}
COPY --from=builder /usr/local/bin/init.sh /usr/local/bin/init.sh
# 权限
RUN chmod +x /data/${APPLICATION}/${APPLICATION}
RUN chmod +x /usr/local/bin/init.sh
CMD ["/usr/local/bin/init.sh"]

View file

@ -0,0 +1,27 @@
#!/bin/sh
APPLICATION=ghproxy
if [ ! -f /data/caddy/config/Caddyfile ]; then
cp /data/caddy/Caddyfile /data/caddy/config/Caddyfile
fi
if [ ! -f /data/${APPLICATION}/config/blacklist.json ]; then
cp /data/${APPLICATION}/blacklist.json /data/${APPLICATION}/config/blacklist.json
fi
if [ ! -f /data/${APPLICATION}/config/whitelist.json ]; then
cp /data/${APPLICATION}/whitelist.json /data/${APPLICATION}/config/whitelist.json
fi
if [ ! -f /data/${APPLICATION}/config/config.toml ]; then
cp /data/${APPLICATION}/config.toml /data/${APPLICATION}/config/config.toml
fi
/data/caddy/caddy run --config /data/caddy/config/Caddyfile > /data/${APPLICATION}/log/caddy.log 2>&1 &
/data/${APPLICATION}/${APPLICATION} -cfg /data/${APPLICATION}/config/config.toml > /data/${APPLICATION}/log/run.log 2>&1 &
while true; do
sleep 1
done

View file

@ -22,7 +22,7 @@ var (
logFilePath = "/data/ghproxy/log/ghproxy.log"
)
// 初始化,接受日志文件路径作为参数
// 初始化
func Init(logFilePath_input string, maxLogsize int) error {
logFileMutex.Lock()
defer logFileMutex.Unlock()
@ -121,7 +121,6 @@ func rotateLogFile(logFilePath string) error {
}
}
// 打开当前日志文件
logFile, err := os.Open(logFilePath)
if err != nil {
return fmt.Errorf("failed to open log file: %s, error: %w", logFilePath, err)
@ -168,7 +167,6 @@ func rotateLogFile(logFilePath string) error {
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)

View file

@ -94,10 +94,8 @@ func NoRouteHandler(cfg *config.Config) gin.HandlerFunc {
switch {
case exps[0].MatchString(rawPath), exps[1].MatchString(rawPath), exps[3].MatchString(rawPath), exps[4].MatchString(rawPath):
logInfo("%s Matched - USE proxy-chrome", rawPath)
ProxyRequest(c, rawPath, cfg, "chrome")
case exps[2].MatchString(rawPath):
logInfo("%s Matched - USE proxy-git", rawPath)
ProxyRequest(c, rawPath, cfg, "git")
default:
c.String(http.StatusForbidden, "Invalid input.")
@ -106,7 +104,7 @@ func NoRouteHandler(cfg *config.Config) gin.HandlerFunc {
}
}
// 提取用户名和仓库名,格式为 handle/<username>/<repo>/*
// 提取用户名和仓库名
func MatchUserRepo(rawPath string, cfg *config.Config, c *gin.Context, matches []string) (string, string) {
var gistregex = regexp.MustCompile(`^(?:https?://)?gist\.github(?:usercontent|)\.com/([^/]+)/([^/]+)/.*`)
var gistmatches []string
@ -115,16 +113,16 @@ func MatchUserRepo(rawPath string, cfg *config.Config, c *gin.Context, matches [
logInfo("Gist Matched > Username: %s, URL: %s", gistmatches[1], rawPath)
return gistmatches[1], ""
}
pathmatches := regexp.MustCompile(`^([^/]+)/([^/]+)/([^/]+)/.*`)
pathParts := pathmatches.FindStringSubmatch(matches[2])
if len(pathParts) < 4 {
logWarning("Invalid path: %s", rawPath)
c.String(http.StatusForbidden, "Invalid path; expected username/repo.")
return "", ""
} else {
return pathParts[2], pathParts[3]
// 定义路径匹配的正则表达式
pathRegex := regexp.MustCompile(`^([^/]+)/([^/]+)/([^/]+)/.*`)
if pathMatches := pathRegex.FindStringSubmatch(matches[2]); len(pathMatches) >= 4 {
return pathMatches[2], pathMatches[3]
}
// 返回错误信息
logWarning("Invalid path: %s", rawPath)
c.String(http.StatusForbidden, "Invalid path; expected username/repo.")
return "", ""
}
func ProxyRequest(c *gin.Context, u string, cfg *config.Config, mode string) {
@ -166,7 +164,7 @@ func createHTTPClient(mode string) *req.Client {
client := req.C()
switch mode {
case "chrome":
client.SetUserAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36").
client.SetUserAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36").
SetTLSFingerprintChrome().
ImpersonateChrome()
case "git":
@ -175,7 +173,7 @@ func createHTTPClient(mode string) *req.Client {
return client
}
// readRequestBody 读取请求体
// 读取请求体
func readRequestBody(c *gin.Context) ([]byte, error) {
body, err := io.ReadAll(c.Request.Body)
if err != nil {
@ -185,7 +183,7 @@ func readRequestBody(c *gin.Context) ([]byte, error) {
return body, nil
}
// setRequestHeaders 设置请求头
// 设置请求头
func setRequestHeaders(c *gin.Context, req *req.Request) {
for key, values := range c.Request.Header {
for _, value := range values {
@ -194,7 +192,7 @@ func setRequestHeaders(c *gin.Context, req *req.Request) {
}
}
// copyResponseBody 复制响应体到客户端
// 复制响应体
func copyResponseBody(c *gin.Context, respBody io.Reader) error {
_, err := io.Copy(c.Writer, respBody)
return err
@ -242,7 +240,7 @@ func CopyResponseHeaders(resp *req.Response, c *gin.Context, cfg *config.Config)
setDefaultHeaders(c)
}
// removeHeaders 移除指定响应头
// 移除指定响应头
func removeHeaders(resp *req.Response) {
headersToRemove := map[string]struct{}{
"Content-Security-Policy": {},
@ -255,7 +253,7 @@ func removeHeaders(resp *req.Response) {
}
}
// copyHeaders 复制响应头到 Gin 上下文
// 复制响应头
func copyHeaders(resp *req.Response, c *gin.Context) {
for key, values := range resp.Header {
for _, value := range values {
@ -264,7 +262,7 @@ func copyHeaders(resp *req.Response, c *gin.Context) {
}
}
// setCORSHeaders 设置 CORS 相关的响应头
// CORS配置
func setCORSHeaders(c *gin.Context, cfg *config.Config) {
if cfg.CORS.Enabled {
c.Header("Access-Control-Allow-Origin", "*")
@ -273,7 +271,7 @@ func setCORSHeaders(c *gin.Context, cfg *config.Config) {
}
}
// setDefaultHeaders 设置默认响应
// 默认响应
func setDefaultHeaders(c *gin.Context) {
c.Header("Age", "10")
c.Header("Cache-Control", "max-age=300")