Compare commits
137 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d7949f625a | ||
|
|
ddd12729a4 | ||
|
|
91b7cfe533 | ||
|
|
aee81ba4fd | ||
|
|
e5d941414e | ||
|
|
c50f23c399 | ||
|
|
47d062a1c4 | ||
|
|
57ba06e01e | ||
|
|
52fdaf5f81 | ||
|
|
d6b8f2b812 | ||
|
|
00d2113904 | ||
|
|
670bca31ca | ||
|
|
f77de0d37a | ||
|
|
1c18ccc363 | ||
|
|
a386304d42 | ||
|
|
a1fdd0f477 | ||
|
|
e94a98668e | ||
|
|
22273f166c | ||
|
|
2187514da7 | ||
|
|
ac859c39a6 | ||
|
|
518bbf2600 | ||
|
|
d383c2d2a6 | ||
|
|
6d055e19e3 | ||
|
|
18f78385f3 | ||
|
|
2edb6f6dd7 | ||
|
|
d42ed119d4 | ||
|
|
072f73fdce | ||
|
|
02b4857334 | ||
|
|
a156f1b2e7 | ||
|
|
1d8c3914d2 | ||
|
|
b05dd37500 | ||
|
|
7fabd3c3e2 | ||
|
|
6dc20398ae | ||
|
|
03118a24a3 | ||
|
|
0cb7c9f948 | ||
|
|
824656f9d0 | ||
|
|
e3d56ae9b7 | ||
|
|
794ba22232 | ||
|
|
f71c95e381 | ||
|
|
997860f3ef | ||
|
|
81c35030e6 | ||
|
|
80d2bc0068 | ||
|
|
4173617e72 | ||
|
|
6a1a1b3c8b | ||
|
|
a21d5659ac | ||
|
|
a0de2f627d | ||
|
|
b47638d066 | ||
|
|
69d4d53a51 | ||
|
|
6864925dbe | ||
|
|
2bdc2d0ace | ||
|
|
1e496fb09e | ||
|
|
23bb463490 | ||
|
|
8eb6c09562 | ||
|
|
1277af445c | ||
|
|
62ee8ede1a | ||
|
|
87b62e0342 | ||
|
|
245040a497 | ||
|
|
96fba543a2 | ||
|
|
38430a3a68 | ||
|
|
71c5f25d1d | ||
|
|
2a53ac1b72 | ||
|
|
57146483ec | ||
|
|
03c33449db | ||
|
|
6b9b864a4f | ||
|
|
db7f8f4bcb | ||
|
|
bfcb835608 | ||
|
|
1eeeb56e9b | ||
|
|
d0b20a8828 | ||
|
|
b57aa84bda | ||
|
|
4eb9d1a899 | ||
|
|
d3d9f78820 | ||
|
|
f3a49b83f2 | ||
|
|
fd7491aaa7 | ||
|
|
b11a864495 | ||
|
|
564364db74 | ||
|
|
a1f0eaafa6 | ||
|
|
2bbb539dda | ||
|
|
7ba2b94a00 | ||
|
|
5e764a6cd7 | ||
|
|
abeb6ee8eb | ||
|
|
df93a90081 | ||
|
|
05032158d4 | ||
|
|
b5bfc809a2 | ||
|
|
dc8f4a28e9 | ||
|
|
f5d7d0994e | ||
|
|
e3cffe70d9 | ||
|
|
822b6c8cea | ||
|
|
5ac08bba56 | ||
|
|
61e741c9b3 | ||
|
|
815b86c6c6 | ||
|
|
999016be2b | ||
|
|
af6c6cfe64 | ||
|
|
88ee8abb5c | ||
|
|
2d9ce57e90 | ||
|
|
d33377087f | ||
|
|
c78d114767 | ||
|
|
b87a8de3c4 | ||
|
|
5dbf137116 | ||
|
|
57f67278a3 | ||
|
|
b9a7f30705 | ||
|
|
c0a9ed11e3 | ||
|
|
eec50879e9 | ||
|
|
862b92a1c5 | ||
|
|
1d7780a890 | ||
|
|
ee215eff36 | ||
|
|
20b9c7827d | ||
|
|
89e6be7709 | ||
|
|
822c08d4c0 | ||
|
|
8588d66a6c | ||
|
|
e4713d0f50 | ||
|
|
51d84f0fc3 | ||
|
|
912b6c0279 | ||
|
|
c4ecf74d71 | ||
|
|
9f801bbac8 | ||
|
|
a1fd8e59f0 | ||
|
|
df05c6a275 | ||
|
|
8ae6a9e090 | ||
|
|
00283fa4d2 | ||
|
|
b68593cbd0 | ||
|
|
c11fd11647 | ||
|
|
058610536b | ||
|
|
7f321bd5f4 | ||
|
|
3665bc9cfd | ||
|
|
fea70c01d8 | ||
|
|
8412231b72 | ||
|
|
178d5f14c2 | ||
|
|
bb4e2d8ae7 | ||
|
|
7d6d9d0da5 | ||
|
|
a8d524739b | ||
|
|
93c3175206 | ||
|
|
58b9fcd1c9 | ||
|
|
af0c364662 | ||
|
|
45246e124e | ||
|
|
0899397d2e | ||
|
|
efeb940676 | ||
|
|
19e2909699 | ||
|
|
48c2ea8f97 |
11
.github/dependabot.yml
vendored
Normal file
11
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# To get started with Dependabot version updates, you'll need to specify which
|
||||||
|
# package ecosystems to update and where the package manifests are located.
|
||||||
|
# Please see the documentation for all configuration options:
|
||||||
|
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
|
||||||
|
|
||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: "gomod" # See documentation for possible values
|
||||||
|
directory: "/" # Location of package manifests
|
||||||
|
schedule:
|
||||||
|
interval: "weekly"
|
||||||
19
.github/workflows/auto-assign.yml
vendored
Normal file
19
.github/workflows/auto-assign.yml
vendored
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
name: Auto Assign
|
||||||
|
on:
|
||||||
|
issues:
|
||||||
|
types: [opened]
|
||||||
|
pull_request:
|
||||||
|
types: [opened]
|
||||||
|
jobs:
|
||||||
|
run:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
issues: write
|
||||||
|
pull-requests: write
|
||||||
|
steps:
|
||||||
|
- name: 'Auto-assign issue'
|
||||||
|
uses: pozil/auto-assign-issue@v1
|
||||||
|
with:
|
||||||
|
repo-token: ${{ secrets.AUTO_ASSIGN }}
|
||||||
|
assignees: WJQSERVER, satomitoka
|
||||||
|
numOfAssignee: 2
|
||||||
31
.github/workflows/build-dev.yml
vendored
31
.github/workflows/build-dev.yml
vendored
@@ -11,10 +11,13 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
goos: [linux]
|
||||||
|
goarch: [amd64, arm64]
|
||||||
env:
|
env:
|
||||||
OUTPUT_BINARY: ghproxy
|
OUTPUT_BINARY: ghproxy
|
||||||
OUTPUT_ARCHIVE: ghproxy.tar.gz
|
GO_VERSION: 1.23.2
|
||||||
GO_VERSION: 1.23.1
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
@@ -29,25 +32,32 @@ jobs:
|
|||||||
uses: actions/setup-go@v3
|
uses: actions/setup-go@v3
|
||||||
with:
|
with:
|
||||||
go-version: ${{ env.GO_VERSION }}
|
go-version: ${{ env.GO_VERSION }}
|
||||||
- name: Build
|
- name: Install UPX
|
||||||
run: |
|
run: |
|
||||||
go build -o ${{ env.OUTPUT_BINARY }} ./main.go
|
sudo apt update
|
||||||
|
sudo apt install upx -y
|
||||||
|
- name: Build
|
||||||
|
env:
|
||||||
|
GOOS: ${{ matrix.goos }}
|
||||||
|
GOARCH: ${{ matrix.goarch }}
|
||||||
|
run: |
|
||||||
|
CGO_ENABLED=0 go build -o ${{ env.OUTPUT_BINARY }}-${{matrix.goos}}-${{matrix.goarch}} ./main.go
|
||||||
|
upx -9 ${{ env.OUTPUT_BINARY }}-${{matrix.goos}}-${{matrix.goarch}}
|
||||||
- name: Package
|
- name: Package
|
||||||
run: |
|
run: |
|
||||||
tar -czvf ${{ env.OUTPUT_ARCHIVE }} ./${{ env.OUTPUT_BINARY }}
|
tar -czvf ${{ env.OUTPUT_BINARY }}-${{matrix.goos}}-${{matrix.goarch}}.tar.gz ./${{ env.OUTPUT_BINARY }}-${{matrix.goos}}-${{matrix.goarch}}
|
||||||
- name: Upload to GitHub Artifacts
|
- name: Upload to GitHub Artifacts
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: ${{ env.OUTPUT_BINARY }}
|
name: ${{ env.OUTPUT_BINARY }}
|
||||||
path: |
|
path: |
|
||||||
./${{ env.OUTPUT_ARCHIVE }}
|
./${{ env.OUTPUT_BINARY }}*
|
||||||
./${{ env.OUTPUT_BINARY }}
|
|
||||||
- name: 上传至Release
|
- name: 上传至Release
|
||||||
id: create_release
|
id: create_release
|
||||||
uses: ncipollo/release-action@v1
|
uses: ncipollo/release-action@v1
|
||||||
with:
|
with:
|
||||||
name: ${{ env.VERSION }}
|
name: ${{ env.VERSION }}
|
||||||
artifacts: ./${{ env.OUTPUT_ARCHIVE }}, ./${{ env.OUTPUT_BINARY }}
|
artifacts: ./${{ env.OUTPUT_BINARY }}*
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
tag: ${{ env.VERSION }}
|
tag: ${{ env.VERSION }}
|
||||||
allowUpdates: true
|
allowUpdates: true
|
||||||
@@ -61,6 +71,7 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
IMAGE_NAME: wjqserver/ghproxy
|
IMAGE_NAME: wjqserver/ghproxy
|
||||||
DOCKERFILE: docker/dockerfile/dev/Dockerfile
|
DOCKERFILE: docker/dockerfile/dev/Dockerfile
|
||||||
|
DOCKERFILE_PATH: docker/dockerfile/dev
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
@@ -86,10 +97,10 @@ jobs:
|
|||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
|
||||||
- name: 构建镜像
|
- name: 构建镜像
|
||||||
uses: docker/build-push-action@v5
|
uses: docker/build-push-action@v6
|
||||||
with:
|
with:
|
||||||
file: ./${{ env.DOCKERFILE }}
|
file: ./${{ env.DOCKERFILE }}
|
||||||
platforms: linux/amd64
|
platforms: linux/amd64,linux/arm64
|
||||||
push: true
|
push: true
|
||||||
tags: |
|
tags: |
|
||||||
${{ env.IMAGE_NAME }}:${{ env.VERSION }}
|
${{ env.IMAGE_NAME }}:${{ env.VERSION }}
|
||||||
|
|||||||
42
.github/workflows/build.yml
vendored
42
.github/workflows/build.yml
vendored
@@ -11,10 +11,13 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
goos: [linux]
|
||||||
|
goarch: [amd64, arm64]
|
||||||
env:
|
env:
|
||||||
OUTPUT_BINARY: ghproxy
|
OUTPUT_BINARY: ghproxy
|
||||||
OUTPUT_ARCHIVE: ghproxy.tar.gz
|
GO_VERSION: 1.23.2
|
||||||
GO_VERSION: 1.23.1
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
@@ -29,49 +32,54 @@ jobs:
|
|||||||
uses: actions/setup-go@v3
|
uses: actions/setup-go@v3
|
||||||
with:
|
with:
|
||||||
go-version: ${{ env.GO_VERSION }}
|
go-version: ${{ env.GO_VERSION }}
|
||||||
- name: Build
|
- name: Install UPX
|
||||||
run: |
|
run: |
|
||||||
go build -o ${{ env.OUTPUT_BINARY }} ./main.go
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y upx
|
||||||
|
- name: Build
|
||||||
|
env:
|
||||||
|
GOOS: ${{ matrix.goos }}
|
||||||
|
GOARCH: ${{ matrix.goarch }}
|
||||||
|
run: |
|
||||||
|
CGO_ENABLED=0 go build -ldflags="-s -w" -o ${{ env.OUTPUT_BINARY }}-${{matrix.goos}}-${{matrix.goarch}} ./main.go
|
||||||
|
upx -9 ${{ env.OUTPUT_BINARY }}-${{matrix.goos}}-${{matrix.goarch}}
|
||||||
- name: Package
|
- name: Package
|
||||||
run: |
|
run: |
|
||||||
tar -czvf ${{ env.OUTPUT_ARCHIVE }} ./${{ env.OUTPUT_BINARY }}
|
tar -czvf ${{ env.OUTPUT_BINARY }}-${{matrix.goos}}-${{matrix.goarch}}.tar.gz ./${{ env.OUTPUT_BINARY }}-${{matrix.goos}}-${{matrix.goarch}}
|
||||||
- name: Upload to GitHub Artifacts
|
- name: Upload to GitHub Artifacts
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: ${{ env.OUTPUT_BINARY }}
|
name: ${{ env.OUTPUT_BINARY }}
|
||||||
path: |
|
path: |
|
||||||
./${{ env.OUTPUT_ARCHIVE }}
|
./${{ env.OUTPUT_BINARY }}*
|
||||||
./${{ env.OUTPUT_BINARY }}
|
|
||||||
- name: 上传至Release
|
- name: 上传至Release
|
||||||
id: create_release
|
id: create_release
|
||||||
uses: ncipollo/release-action@v1
|
uses: ncipollo/release-action@v1
|
||||||
with:
|
with:
|
||||||
name: ${{ env.VERSION }}
|
name: ${{ env.VERSION }}
|
||||||
artifacts: ./${{ env.OUTPUT_ARCHIVE }}, ./${{ env.OUTPUT_BINARY }}
|
artifacts: ./${{ env.OUTPUT_BINARY }}*
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
tag: ${{ env.VERSION }}
|
tag: ${{ env.VERSION }}
|
||||||
allowUpdates: true
|
allowUpdates: true
|
||||||
env:
|
env:
|
||||||
export PATH: $PATH:/usr/local/go/bin
|
export PATH: $PATH:/usr/local/go/bin
|
||||||
|
|
||||||
docker:
|
docker:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: build # 确保这个作业在 build 作业完成后运行
|
needs: build # 确保这个作业在 build 作业完成后运行
|
||||||
env:
|
env:
|
||||||
IMAGE_NAME: wjqserver/ghproxy # 定义镜像名称变量
|
IMAGE_NAME: wjqserver/ghproxy # 定义镜像名称变量
|
||||||
DOCKERFILE: docker/dockerfile/release/Dockerfile
|
DOCKERFILE: docker/dockerfile/release/Dockerfile # 定义 Dockerfile 路径变量
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
- name: Load VERSION
|
- name: Load VERSION
|
||||||
run: |
|
run: |
|
||||||
if [ -f DEV-VERSION ]; then
|
if [ -f VERSION ]; then
|
||||||
echo "VERSION=$(cat DEV-VERSION)" >> $GITHUB_ENV
|
echo "VERSION=$(cat VERSION)" >> $GITHUB_ENV
|
||||||
else
|
else
|
||||||
echo "DEV-VERSION file not found!" && exit 1
|
echo "VERSION file not found!" && exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v3
|
uses: docker/setup-qemu-action@v3
|
||||||
@@ -86,10 +94,10 @@ jobs:
|
|||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
|
||||||
- name: 构建镜像
|
- name: 构建镜像
|
||||||
uses: docker/build-push-action@v5
|
uses: docker/build-push-action@v6
|
||||||
with:
|
with:
|
||||||
file: ./${{ env.DOCKERFILE }}
|
file: ./${{ env.DOCKERFILE }}
|
||||||
platforms: linux/amd64
|
platforms: linux/amd64,linux/arm64
|
||||||
push: true
|
push: true
|
||||||
tags: |
|
tags: |
|
||||||
${{ env.IMAGE_NAME }}:${{ env.VERSION }}
|
${{ env.IMAGE_NAME }}:${{ env.VERSION }}
|
||||||
|
|||||||
327
CHANGELOG.md
327
CHANGELOG.md
@@ -1,7 +1,317 @@
|
|||||||
# 更新日志
|
# 更新日志
|
||||||
|
|
||||||
|
v1.6.0
|
||||||
|
---
|
||||||
|
- CHANGE: 优化代码结构,提升性能
|
||||||
|
- CHANGE: 引入H2C支持,支持无加密HTTP/2请求,一定程度上提升传输性能
|
||||||
|
- ADD: 在核心程序内加入静态页面支持,支持不通过caddy等web server提供前端页面
|
||||||
|
- CHANGE: 优化日志记录,带来更多的可观测性
|
||||||
|
- CHANGE: 改进前端界面,优化用户体验; 对原有Alert提示进行优化,改为ShowToast提示
|
||||||
|
- CHANGE: 规范化部分函数命名,提升可读性; 同时对config.toml内的参数命名进行规范化(部分参数名称已过时,请注意更新)
|
||||||
|
- CHANGE: 修改日志检查周期,降低检查频率,避免不必要的资源浪费
|
||||||
|
- ADD: 增加CORS状态API
|
||||||
|
|
||||||
|
24w18f
|
||||||
|
---
|
||||||
|
- PRE-RELEASE: 此版本是v1.6.0的预发布版本,请勿在生产环境中使用
|
||||||
|
- CHANGE: 修正前端页面的部分样式问题
|
||||||
|
- FIX: 修正部分问题
|
||||||
|
|
||||||
|
24w18e
|
||||||
|
---
|
||||||
|
- PRE-RELEASE: 此版本是预发布版本,请勿在生产环境中使用
|
||||||
|
- CHANGE: 引入H2C协议支持,支持无加密HTTP/2请求
|
||||||
|
- ADD: 尝试在核心程序内加入静态页面支持
|
||||||
|
- CHANGE: 优化日志记录
|
||||||
|
- CHANGE: 去除部分无用/重复配置
|
||||||
|
- CHANGE: 规范化部分函数命名
|
||||||
|
|
||||||
|
24w18d
|
||||||
|
---
|
||||||
|
- PRE-RELEASE: 此版本是预发布版本,请勿在生产环境中使用
|
||||||
|
- CHANGE: 更新相关依赖库
|
||||||
|
- ADD: 增加CORS状态API
|
||||||
|
- CHANGE: 优化部分函数执行顺序
|
||||||
|
- CHANGE: 优化前端界面
|
||||||
|
|
||||||
|
24w18c
|
||||||
|
---
|
||||||
|
- PRE-RELEASE: 此版本是预发布版本,请勿在生产环境中使用
|
||||||
|
- CHANGE: 修正配置命名,改为驼峰式命名
|
||||||
|
- CHANGE: 修正函数命名
|
||||||
|
|
||||||
|
24w18b
|
||||||
|
---
|
||||||
|
- PRE-RELEASE: 此版本是预发布版本,请勿在生产环境中使用
|
||||||
|
- CHANGE: 经团队考量,移除 Docker 代理功能,若造成了不便敬请谅解
|
||||||
|
- CHANGE: 修改日志检查周期
|
||||||
|
|
||||||
|
24w18a
|
||||||
|
---
|
||||||
|
- PRE-RELEASE: 此版本是预发布版本,请勿在生产环境中使用
|
||||||
|
- CHANGE: 改进Docker 代理
|
||||||
|
- CHANGE: 改进前端页面的copy提示,弃用alert提示
|
||||||
|
|
||||||
|
v1.5.2
|
||||||
|
---
|
||||||
|
- FIX: 修正flag传入问题
|
||||||
|
- CHANGE: 去除/路径重定向,改为返回403,并记录对应请求日志
|
||||||
|
- CHANGE: 优化Proxy模块的日志记录,记录请求详细信息
|
||||||
|
|
||||||
|
24w17b
|
||||||
|
---
|
||||||
|
- PRE-RELEASE: 此版本是v1.5.2的预发布版本,请勿在生产环境中使用
|
||||||
|
- FIX: 修正flag传入问题
|
||||||
|
- CHANGE: 去除/路径重定向,改为返回403,并记录对应请求日志
|
||||||
|
- CHANGE: 优化Proxy模块的日志记录,记录请求详细信息
|
||||||
|
|
||||||
|
24w17a
|
||||||
|
---
|
||||||
|
- PRE-RELEASE: 此版本是v1.5.2的预发布版本,请勿在生产环境中使用
|
||||||
|
- FIX: 初步修正flag传入问题,但仍有可能存在其他问题
|
||||||
|
|
||||||
|
v1.5.1
|
||||||
|
---
|
||||||
|
- CHANGE: 优化代码结构,提升性能
|
||||||
|
- CHANGE: Bump github.com/imroc/req/v3 from 3.48.0 to 3.49.0 by @dependabot in https://github.com/WJQSERVER-STUDIO/ghproxy/pull/7
|
||||||
|
- ADD: 新增一键部署脚本,简化二进制文件部署流程
|
||||||
|
|
||||||
|
24w16a
|
||||||
|
---
|
||||||
|
- PRE-RELEASE: 此版本是v1.5.1的预发布版本,请勿在生产环境中使用
|
||||||
|
- CHANGE: 优化代码结构,提升性能
|
||||||
|
- CHANGE: Bump github.com/imroc/req/v3 from 3.47.0 to 3.48.0 by @dependabot in https://github.com/WJQSERVER-STUDIO/ghproxy/pull/6
|
||||||
|
- ADD: 新增一键部署脚本,简化二进制文件部署流程
|
||||||
|
|
||||||
|
v1.5.0
|
||||||
|
---
|
||||||
|
- CHANGE: 优化代码结构,提升性能
|
||||||
|
- CHANGE: 改进核心部分,即proxy模块的转发部分,对请求体处理与响应体处理进行优化
|
||||||
|
- CHANGE: 配置文件格式由yaml切换至toml,使其具备更好的可读性
|
||||||
|
- ADD: 黑白名单引入通配符支持,支持完全屏蔽或放行某个用户,例如`onwer/*`表示匹配`owner`的所有仓库
|
||||||
|
- ADD: 新增API模块,新增配置开关状态接口,以在前端指示功能状态
|
||||||
|
- CHANGE: 由于API变动,对前端进行相应调整
|
||||||
|
- ADD: 日志模块引入日志级别,排障更加直观
|
||||||
|
- CHANGE: 改进黑白名单机制,若禁用相关功能,则不对相关模块进行初始化
|
||||||
|
|
||||||
|
24w15d
|
||||||
|
---
|
||||||
|
- PRE-RELEASE: 此版本是v1.5.0的预发布版本,请勿在生产环境中使用
|
||||||
|
- CHANGE: 优化代码结构,提升性能
|
||||||
|
- ADD: 新增API模块,新增配置开关状态接口,以在前端指示功能状态
|
||||||
|
- CHANGE: 由于API变动,对前端进行相应调整
|
||||||
|
|
||||||
|
24w15c
|
||||||
|
---
|
||||||
|
- PRE-RELEASE: 此版本是v1.5.0的预发布版本,请勿在生产环境中使用
|
||||||
|
- CHANGE: 优化代码结构,提升性能
|
||||||
|
- CHANGE: 改进核心部分,即proxy模块的转发部分,对请求体处理与响应体处理进行优化
|
||||||
|
- CHANGE: 改进黑白名单机制,若禁用相关功能,则不对对应模块进行初始化
|
||||||
|
- ADD: 黑白名单引入通配符支持,支持完全屏蔽或放行某个用户,例如`onwer/*`表示匹配`owner`的所有仓库
|
||||||
|
- ADD: 日志模块引入日志级别,排障更加直观
|
||||||
|
|
||||||
|
24w15b
|
||||||
|
---
|
||||||
|
- PRE-RELEASE: 此版本是v1.5.0的预发布版本,请勿在生产环境中使用
|
||||||
|
- CHANGE: 优化代码结构,提升性能
|
||||||
|
- FIX: 修正24w15a版本的部分问题
|
||||||
|
|
||||||
|
24w15a
|
||||||
|
---
|
||||||
|
- PRE-RELEASE: 此版本是v1.5.0的预发布版本,请勿在生产环境中使用
|
||||||
|
- CHANGE: 优化代码结构,提升性能
|
||||||
|
- CHANGE: 将配置文件由yaml切换至toml
|
||||||
|
|
||||||
|
v1.4.3
|
||||||
|
---
|
||||||
|
- CHANGE: 优化代码结构,提升性能
|
||||||
|
- ADD: 新增命令行参数 `-cfg string` 用于指定配置文件路径
|
||||||
|
- CHANGE: 对二进制文件大小进行改进
|
||||||
|
|
||||||
|
24w14a
|
||||||
|
---
|
||||||
|
- PRE-RELEASE: 此版本是v1.4.3的预发布版本,请勿在生产环境中使用
|
||||||
|
- CHANGE: 优化代码结构,提升性能
|
||||||
|
- ADD: 新增命令行参数 `-cfg string` 用于指定配置文件路径
|
||||||
|
|
||||||
|
v1.4.2
|
||||||
|
---
|
||||||
|
- CHANGE: 优化代码结构,提升性能
|
||||||
|
- CHANGE: 初步引入ARM64架构支持
|
||||||
|
- CHANGE: 对Docker镜像构建进行优化,大幅减少镜像体积,从v1.4.0的`111 MB`,到v1.4.1的`58 MB`,再到v1.4.2的`28 MB`
|
||||||
|
- CHANGE: 切换至wjqserver/caddy:2.9.0-rc-alpine作为基础镜像
|
||||||
|
|
||||||
|
24w13c
|
||||||
|
---
|
||||||
|
- PRE-RELEASE: 此版本是v1.4.2的预发布版本,请勿在生产环境中使用
|
||||||
|
- CHANGE: 优化代码结构,提升性能
|
||||||
|
- CHANGE: 修正交叉编译问题
|
||||||
|
|
||||||
|
24w13b
|
||||||
|
---
|
||||||
|
- PRE-RELEASE: 此版本是v1.4.2的预发布版本,请勿在生产环境中使用
|
||||||
|
- CHANGE: 优化代码结构,提升性能
|
||||||
|
- CHANGE: 初步引入ARM64支持,但仍处于测试阶段
|
||||||
|
- CHANGE: 对Dockerfile进行优化,大幅减少镜像体积
|
||||||
|
|
||||||
|
24w13a
|
||||||
|
---
|
||||||
|
- PRE-RELEASE: 此版本是v1.4.2的预发布版本,请勿在生产环境中使用
|
||||||
|
- CHANGE: 优化代码结构,提升性能
|
||||||
|
- CHANGE: 更新相关依赖库
|
||||||
|
|
||||||
|
v1.4.1
|
||||||
|
---
|
||||||
|
- CHANGE: 优化代码结构,提升性能
|
||||||
|
- CHANGE: 引入Alpine Linux作为基础镜像,大幅减少Docker镜像体积
|
||||||
|
- FIX: 修正部分参数错误
|
||||||
|
- CHANGE: CGO_ENABLED=0
|
||||||
|
|
||||||
|
24w12c
|
||||||
|
---
|
||||||
|
- PRE-RELEASE: 此版本是v1.4.1的预发布版本,请勿在生产环境中使用
|
||||||
|
- CHANGE: 优化代码结构,提升性能
|
||||||
|
- CHANGE: 尝试在DEV版本引入Alpine Linux作为基础镜像,减少镜像体积
|
||||||
|
|
||||||
|
24w12b
|
||||||
|
---
|
||||||
|
- PRE-RELEASE: 此版本是v1.4.1的预发布版本,请勿在生产环境中使用
|
||||||
|
- CHANGE: 优化代码结构,提升性能
|
||||||
|
- CHANGE: 尝试引入Alpine Linux作为基础镜像,减少镜像体积
|
||||||
|
|
||||||
|
24w12a
|
||||||
|
---
|
||||||
|
- PRE-RELEASE: 此版本是v1.4.1的预发布版本,请勿在生产环境中使用
|
||||||
|
- CHANGE: 优化代码结构,提升性能
|
||||||
|
- FIX: 修正部分参数错误
|
||||||
|
- CHANGE: CGO_ENABLED=0
|
||||||
|
|
||||||
|
v1.4.0
|
||||||
|
---
|
||||||
|
- CHANGE: 优化代码结构,提升性能
|
||||||
|
- ADD: 新增auth子模块whitelist.go,支持白名单功能
|
||||||
|
- ADD: 新增whitelist.json文件,用于配置白名单
|
||||||
|
- CHANGE&ADD: 在config.yaml文件中新增白名单配置块
|
||||||
|
- FIX: 由于临时加入且未在原开发路线上计划的白名单功能,导致函数命名冲突,在此修复blacklist.go的函数命名问题
|
||||||
|
- FIX: 修复黑/白名单是否生效相关问题
|
||||||
|
|
||||||
|
24w11b
|
||||||
|
---
|
||||||
|
- PRE-RELEASE: 此版本是v1.4.0的预发布版本,请勿在生产环境中使用
|
||||||
|
- FIX: 修复黑/白名单是否生效相关问题
|
||||||
|
|
||||||
|
24w11a
|
||||||
|
---
|
||||||
|
- PRE-RELEASE: 此版本是v1.4.0的预发布版本,请勿在生产环境中使用
|
||||||
|
- **ANNOUNCE**: 自此版本起,DEV版本号格式进行修改,小版本号不再仅限于a/b,而是采用字母表顺序进行排列,此修改将带来一个重要改变,正式版前的预发布版本的数字版本号将会统一,以便于版本管理与发布管理
|
||||||
|
- CHANGE: 优化代码结构,提升性能
|
||||||
|
- ADD: 新增auth子模块whitelist.go,支持白名单功能
|
||||||
|
- ADD: 新增whitelist.json文件,用于配置白名单
|
||||||
|
- FIX: 由于临时加入且未在原开发路线上计划的白名单功能,导致函数命名冲突,在此修复blacklist.go的函数命名问题
|
||||||
|
|
||||||
|
v1.3.1
|
||||||
|
---
|
||||||
|
- CHANGE: 优化代码结构,提升性能
|
||||||
|
- CHANGE: 剃刀计划,减少多余日志输出
|
||||||
|
- CHANGE: 调整缓存参数
|
||||||
|
|
||||||
|
24w10a
|
||||||
|
---
|
||||||
|
- PRE-RELEASE: 此版本是v1.3.1的预发布版本,请勿在生产环境中使用
|
||||||
|
- CHANGE: 优化代码结构,提升性能
|
||||||
|
- CHANGE: 剃刀计划,减少多余日志输出
|
||||||
|
- CHANGE: 调整缓存参数
|
||||||
|
|
||||||
|
v1.3.0
|
||||||
|
---
|
||||||
|
- CHANGE: 优化代码结构,提升性能
|
||||||
|
- CHANGE: 优化黑名单功能,提升稳定性
|
||||||
|
- CHANGE: 剃刀计划,减少多余日志输出
|
||||||
|
- ADD: 新增auth子模块blacklist.go,支持黑名单功能
|
||||||
|
- ADD: 新增blacklist.json文件,用于配置黑名单
|
||||||
|
- CHANGE: config.yaml文件格式修改,使其具备更好的可读性
|
||||||
|
- WARNING: 此版本为大版本更新,配置文件重构,此版本不再向前兼容,请注意备份文件并重新部署
|
||||||
|
|
||||||
|
24w09b
|
||||||
|
---
|
||||||
|
- PRE-RELEASE: 此版本是v1.3.0的预发布版本,请勿在生产环境中使用
|
||||||
|
- CHANGE: 优化代码结构,提升性能
|
||||||
|
- CHANGE: 修正配置,提升稳定性
|
||||||
|
- WARNING: 此版本配置文件重构,此版本不再向前兼容,请注意备份文件并重新部署
|
||||||
|
|
||||||
|
24w09a
|
||||||
|
---
|
||||||
|
- PRE-RELEASE: 此版本是v1.3.0的预发布版本,请勿在生产环境中使用
|
||||||
|
- CHANGE: 优化代码结构,提升性能
|
||||||
|
- CHANGE: 优化黑名单功能,提升稳定性
|
||||||
|
- CHANGE&ADD: 新增auth子模块blacklist.go
|
||||||
|
- CHANGE: 黑名单转为使用json文件存储,便于程序处理
|
||||||
|
- WARNING: 此版本配置文件重构,此版本不再向前兼容,请注意备份文件并重新部署
|
||||||
|
|
||||||
|
24w08b
|
||||||
|
---
|
||||||
|
- PRE-RELEASE: 此版本是v1.3.0的预发布版本,请勿在生产环境中使用
|
||||||
|
- CHANGE: 优化代码结构,提升性能
|
||||||
|
- ADD & CHANGE: 新增仓库黑名单功能,改进Auth模块
|
||||||
|
- ADD: 新增blacklist.yaml文件,用于配置仓库黑名单
|
||||||
|
- CHANGE: 大幅度修改Config包,使其更加模块化
|
||||||
|
- CHANGE: 与Config包同步修改config.yaml文件(不向前兼容)
|
||||||
|
- CHANGE: 修改config.yaml文件的格式,使其具备更好的可读性
|
||||||
|
- WARNING: 此版本配置文件重构,此版本不再向前兼容,请注意备份文件并重新部署
|
||||||
|
|
||||||
|
v1.2.0
|
||||||
|
---
|
||||||
|
- CHANGE: 优化代码结构,提升性能
|
||||||
|
- CHANGE: 同步更新logger模块,与golang-temp项目定义的开发规范保持一致
|
||||||
|
- ADD: 新增日志翻转功能
|
||||||
|
|
||||||
|
24w08a
|
||||||
|
---
|
||||||
|
- PRE-RELEASE: 此版本是v1.2.0的预发布版本,请勿在生产环境中使用
|
||||||
|
- CHANGE: 同步更新logger模块,与golang-temp项目定义的开发规范保持一致
|
||||||
|
- ADD: 新增日志翻转功能
|
||||||
|
|
||||||
|
v1.1.1
|
||||||
|
---
|
||||||
|
- CHANGE: 修改部分代码,与golang-temp项目定义的开发规范保持一致
|
||||||
|
- CHANGE: 更新Go版本至v1.23.2
|
||||||
|
- CHANGE: 跟随Caddy更新,修改Caddyfile配置
|
||||||
|
|
||||||
|
24w07b
|
||||||
|
---
|
||||||
|
- PRE-RELEASE: 此版本是v1.1.1的预发布版本,请勿在生产环境中使用
|
||||||
|
- CHANGE: 修改部分代码,与golang-temp项目定义的开发规范保持一致
|
||||||
|
- CHANGE: 更新Go版本至v1.23.2
|
||||||
|
- CHANGE: 跟随Caddy更新,修改Caddyfile配置
|
||||||
|
|
||||||
|
24w07a
|
||||||
|
---
|
||||||
|
- PRE-RELEASE: 此版本是v1.1.1的预发布版本,请勿在生产环境中使用
|
||||||
|
- CHANGE: 修改部分代码,与golang-temp项目定义的开发规范保持一致
|
||||||
|
- CHANGE: 更新Go版本至v1.23.2
|
||||||
|
|
||||||
|
v1.1.0
|
||||||
|
---
|
||||||
|
- CHANGE: 优化代码结构,对main函数进行模块化,提升可读性
|
||||||
|
- CHANGE: Docker代理功能移至DEV版本内,保证稳定性
|
||||||
|
- ADD&CHANGE: 增加Auth(用户鉴权)模块,并改进其的错误处理与日志记录
|
||||||
|
- CHANGE: 日志模块引入goroutine协程,提升性能
|
||||||
|
- ADD: 将主要实现分离,作为Proxy模块,并优化代码结构
|
||||||
|
- ADD: 新增安全政策
|
||||||
|
|
||||||
|
24w06b
|
||||||
|
---
|
||||||
|
- PRE-RELEASE: 此版本是v1.1.0的预发布版本,请勿在生产环境中使用
|
||||||
|
- CHANGE: 优化代码结构,对main函数进行模块化,提升可读性
|
||||||
|
- CHANGE: Docker代理功能移至DEV版本内,保证稳定性
|
||||||
|
- ADD&CHANGE: 增加Auth(用户鉴权)模块,并改进其的错误处理与日志记录
|
||||||
|
- CHANGE: 日志模块引入goroutine协程,提升性能 (实验性功能)
|
||||||
|
- ADD: 将主要实现分离,作为Proxy模块,并优化代码结构
|
||||||
|
- ADD: 新增安全政策
|
||||||
|
|
||||||
v1.0.0
|
v1.0.0
|
||||||
---
|
---
|
||||||
|
- **ANNOUNCE**: 项目正式发布, 并迁移至[WJQSERVER-STUDIO/ghproxy](https://github.com/WJQSERVER-STUDIO/ghproxy),由Apache License Version 2.0转为WJQserver Studio License 请注意相关条例变更
|
||||||
- CHANGE: 项目正式发布, 并迁移至[WJQSERVER-STUDIO/ghproxy](https://github.com/WJQSERVER-STUDIO/ghproxy)
|
- CHANGE: 项目正式发布, 并迁移至[WJQSERVER-STUDIO/ghproxy](https://github.com/WJQSERVER-STUDIO/ghproxy)
|
||||||
- CHANGE: 再次重构代码,优化性能,提升稳定性
|
- CHANGE: 再次重构代码,优化性能,提升稳定性
|
||||||
- CHANGE: 使用golang-temp项目作为底层构建,标准化日志与配置模块
|
- CHANGE: 使用golang-temp项目作为底层构建,标准化日志与配置模块
|
||||||
@@ -9,6 +319,7 @@ v1.0.0
|
|||||||
|
|
||||||
24w06a
|
24w06a
|
||||||
---
|
---
|
||||||
|
- PRE-RELEASE: 此版本是v1.0.0的预发布版本,请勿在生产环境中使用
|
||||||
- CHANGE: 与v1.0.0版本同步
|
- CHANGE: 与v1.0.0版本同步
|
||||||
|
|
||||||
v0.2.0
|
v0.2.0
|
||||||
@@ -20,21 +331,25 @@ v0.2.0
|
|||||||
|
|
||||||
24w05b
|
24w05b
|
||||||
---
|
---
|
||||||
|
- PRE-RELEASE: 此版本是v0.2.0的预发布版本,请勿在生产环境中使用
|
||||||
- CHANGE: 重命名proxychrome函数
|
- CHANGE: 重命名proxychrome函数
|
||||||
- ADD: 增加多处日志记录,便于审计与排障
|
- ADD: 增加多处日志记录,便于审计与排障
|
||||||
|
|
||||||
24w05a
|
24w05a
|
||||||
---
|
---
|
||||||
|
- PRE-RELEASE: 此版本是v0.2.0的预发布版本,请勿在生产环境中使用
|
||||||
- FIX: 修正上一版本的req请求未继承请求方式的问题
|
- FIX: 修正上一版本的req请求未继承请求方式的问题
|
||||||
- CHANGE: 优化代码结构,进一步模块化,同时提升性能
|
- CHANGE: 优化代码结构,进一步模块化,同时提升性能
|
||||||
|
|
||||||
24w04b
|
24w04b
|
||||||
---
|
---
|
||||||
|
- PRE-RELEASE: 此版本是v0.2.0的预发布版本,请勿在生产环境中使用
|
||||||
- CHANGE: 更换Docker基础镜像为daily版本
|
- CHANGE: 更换Docker基础镜像为daily版本
|
||||||
- ADD: 新增使用req库实现代理请求,使用chrome TLS指纹发起请求
|
- ADD: 新增使用req库实现代理请求,使用chrome TLS指纹发起请求
|
||||||
|
|
||||||
24w04a
|
24w04a
|
||||||
---
|
---
|
||||||
|
- PRE-RELEASE: 此版本是v0.2.0的预发布版本,请勿在生产环境中使用
|
||||||
- CHANGE: 调整程序结构,使用init函数初始化配置,并优化代码结构
|
- CHANGE: 调整程序结构,使用init函数初始化配置,并优化代码结构
|
||||||
|
|
||||||
v0.1.7
|
v0.1.7
|
||||||
@@ -43,6 +358,7 @@ v0.1.7
|
|||||||
|
|
||||||
24w03b
|
24w03b
|
||||||
---
|
---
|
||||||
|
- PRE-RELEASE: 此版本是v0.1.7的预发布版本,请勿在生产环境中使用
|
||||||
- CHANGE: 合入上游(wjqserver/caddy:latest)安全更新, 增强镜像安全性
|
- CHANGE: 合入上游(wjqserver/caddy:latest)安全更新, 增强镜像安全性
|
||||||
|
|
||||||
v0.1.6
|
v0.1.6
|
||||||
@@ -52,6 +368,7 @@ v0.1.6
|
|||||||
|
|
||||||
24w03a
|
24w03a
|
||||||
---
|
---
|
||||||
|
- PRE-RELEASE: 此版本是v0.1.6的预发布版本,请勿在生产环境中使用
|
||||||
- CHANGE: 改进Docker代理相关Caddy配置
|
- CHANGE: 改进Docker代理相关Caddy配置
|
||||||
- ADD: 新增跨域配置选项
|
- ADD: 新增跨域配置选项
|
||||||
|
|
||||||
@@ -62,16 +379,17 @@ v0.1.5
|
|||||||
|
|
||||||
24w02b
|
24w02b
|
||||||
---
|
---
|
||||||
|
- PRE-RELEASE: 此版本是v0.1.5的预发布版本,请勿在生产环境中使用
|
||||||
- ADD: 新增Docker代理 (未并入正式版)
|
- ADD: 新增Docker代理 (未并入正式版)
|
||||||
|
|
||||||
24w02a
|
24w02a
|
||||||
---
|
---
|
||||||
|
- PRE-RELEASE: 此版本是v0.1.5的预发布版本,请勿在生产环境中使用
|
||||||
- CHANGE: 更新Go版本至v1.23.1
|
- CHANGE: 更新Go版本至v1.23.1
|
||||||
- CHANGE: 优化代码行为
|
- CHANGE: 优化代码行为
|
||||||
|
|
||||||
v0.1.4
|
v0.1.4
|
||||||
---
|
---
|
||||||
正式版24w01b内容更新
|
|
||||||
- ADD: 新增外部文件配置功能
|
- ADD: 新增外部文件配置功能
|
||||||
- ADD: 新增日志功能
|
- ADD: 新增日志功能
|
||||||
- CHANGE: 优化代码结构,提升性能
|
- CHANGE: 优化代码结构,提升性能
|
||||||
@@ -79,7 +397,7 @@ v0.1.4
|
|||||||
|
|
||||||
24w01b
|
24w01b
|
||||||
---
|
---
|
||||||
标志着项目正式进入自主开发阶段
|
- PRE-RELEASE: 此版本是v0.1.4的预发布版本,请勿在生产环境中使用
|
||||||
- ADD: 新增外部文件配置功能
|
- ADD: 新增外部文件配置功能
|
||||||
- ADD: 新增日志功能
|
- ADD: 新增日志功能
|
||||||
- CHANGE: 优化代码结构,提升性能
|
- CHANGE: 优化代码结构,提升性能
|
||||||
@@ -87,12 +405,13 @@ v0.1.4
|
|||||||
|
|
||||||
v0.1.3
|
v0.1.3
|
||||||
---
|
---
|
||||||
开始自行维护项目,脱离上游更新
|
- **ANNOUNCE**: 开始自行维护项目,脱离上游更新
|
||||||
- CHANGE: 改进已有实现,增强程序稳定性
|
- CHANGE: 改进已有实现,增强程序稳定性
|
||||||
|
|
||||||
24w01a
|
24w01a
|
||||||
---
|
---
|
||||||
首个DEV版本
|
- PRE-RELEASE: 此版本是v0.1.3的预发布版本,请勿在生产环境中使用
|
||||||
|
- **ANNOUNCE**: 首个DEV版本发布
|
||||||
- CHANGE: 同步更新
|
- CHANGE: 同步更新
|
||||||
|
|
||||||
v0.1.2
|
v0.1.2
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
24w06a
|
24w18f
|
||||||
100
README.md
100
README.md
@@ -5,6 +5,10 @@
|
|||||||
|
|
||||||
使用Go实现的GHProxy,用于加速部分地区Github仓库的拉取,支持速率限制,用户鉴权,支持Docker部署
|
使用Go实现的GHProxy,用于加速部分地区Github仓库的拉取,支持速率限制,用户鉴权,支持Docker部署
|
||||||
|
|
||||||
|
[DEMO](https://ghproxy.1888866.xyz)
|
||||||
|
|
||||||
|
[TG讨论群组](https://t.me/ghproxy_go)
|
||||||
|
|
||||||
## 项目说明
|
## 项目说明
|
||||||
|
|
||||||
### 项目特点
|
### 项目特点
|
||||||
@@ -14,6 +18,8 @@
|
|||||||
- 支持Docker部署
|
- 支持Docker部署
|
||||||
- 支持速率限制
|
- 支持速率限制
|
||||||
- 支持用户鉴权
|
- 支持用户鉴权
|
||||||
|
- 支持自定义黑名单/白名单
|
||||||
|
- 符合[RFC 7234](https://httpwg.org/specs/rfc7234.html)的HTTP Cache
|
||||||
- 使用Caddy作为Web Server
|
- 使用Caddy作为Web Server
|
||||||
- 基于[WJQSERVER-STUDIO/golang-temp](https://github.com/WJQSERVER-STUDIO/golang-temp)模板构建,具有标准化的日志记录与构建流程
|
- 基于[WJQSERVER-STUDIO/golang-temp](https://github.com/WJQSERVER-STUDIO/golang-temp)模板构建,具有标准化的日志记录与构建流程
|
||||||
|
|
||||||
@@ -47,31 +53,90 @@ git clone https://ghproxy.1888866.xyz/github.com/WJQSERVER-STUDIO/ghproxy.git
|
|||||||
- Docker-cli
|
- Docker-cli
|
||||||
|
|
||||||
```
|
```
|
||||||
docker run -p 7210:80 -v ./ghproxy/log/run:/data/ghproxy/log -v ./ghproxy/log/caddy:/data/caddy/log --restart always wjqserver/ghproxy
|
docker run -p 7210:80 -v ./ghproxy/log/run:/data/ghproxy/log -v ./ghproxy/log/caddy:/data/caddy/log -v ./ghproxy/config:/data/ghproxy/config --restart always wjqserver/ghproxy
|
||||||
```
|
```
|
||||||
|
|
||||||
- Docker-Compose
|
- Docker-Compose
|
||||||
|
|
||||||
参看[docker-compose.yml](https://github.com/WJQSERVER-STUDIO/ghproxy/blob/main/docker/compose/docker-compose.yml)
|
参看[docker-compose.yml](https://github.com/WJQSERVER-STUDIO/ghproxy/blob/main/docker/compose/docker-compose.yml)
|
||||||
|
|
||||||
|
### 二进制文件部署(不推荐)
|
||||||
|
|
||||||
|
一键部署脚本:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
wget -O install.sh https://raw.githubusercontent.com/WJQSERVER-STUDIO/ghproxy/main/deploy/install.sh && chmod +x install.sh &&./install.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
## 配置说明
|
||||||
|
|
||||||
### 外部配置文件
|
### 外部配置文件
|
||||||
|
|
||||||
本项目采用config.yaml作为外部配置,默认配置如下
|
本项目采用`config.toml`作为外部配置,默认配置如下
|
||||||
使用Docker部署时,慎重修改config.yaml,以免造成不必要的麻烦
|
使用Docker部署时,慎重修改`config.toml`,以免造成不必要的麻烦
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[server]
|
||||||
|
host = "127.0.0.1" # 监听地址
|
||||||
|
port = 8080 # 监听端口
|
||||||
|
sizeLimit = 131072000 # 125MB
|
||||||
|
|
||||||
|
[pages]
|
||||||
|
enabled = true # 是否开启内置静态页面
|
||||||
|
staticPath = "/data/www" # 静态页面文件路径
|
||||||
|
|
||||||
|
[log]
|
||||||
|
logFilePath = "/data/ghproxy/log/ghproxy.log" # 日志文件路径
|
||||||
|
maxLogSize = 5 # MB 日志文件最大大小
|
||||||
|
|
||||||
|
[cors]
|
||||||
|
enabled = true # 是否开启跨域
|
||||||
|
|
||||||
|
[auth]
|
||||||
|
authToken = "token" # 用户鉴权Token
|
||||||
|
enabled = false # 是否开启用户鉴权
|
||||||
|
|
||||||
|
[blacklist]
|
||||||
|
blacklistFile = "/data/ghproxy/config/blacklist.json" # 黑名单文件路径
|
||||||
|
enabled = false # 是否开启黑名单
|
||||||
|
|
||||||
|
[whitelist]
|
||||||
|
enabled = false # 是否开启白名单
|
||||||
|
whitelistFile = "/data/ghproxy/config/whitelist.json" # 白名单文件路径
|
||||||
|
|
||||||
```
|
```
|
||||||
port: 8080 # 监听端口
|
|
||||||
host: "127.0.0.1" # 监听地址
|
### 黑名单配置
|
||||||
sizelimit: 131072000 # 125MB
|
|
||||||
logfilepath: "/data/ghproxy/log/ghproxy-0rtt.log" # 日志文件路径
|
黑名单配置位于config/blacklist.json,格式如下:
|
||||||
CorsAllowOrigins: true # 是否允许跨域请求
|
|
||||||
auth: true # 是否开启鉴权
|
```json
|
||||||
authtoken: "test" # 鉴权token
|
{
|
||||||
|
"blacklist": [
|
||||||
|
"test/test1",
|
||||||
|
"example/repo2",
|
||||||
|
"another/*"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 白名单配置
|
||||||
|
|
||||||
|
白名单配置位于config/whitelist.json,格式如下:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"whitelist": [
|
||||||
|
"test/test1",
|
||||||
|
"example/repo2",
|
||||||
|
"another/*"
|
||||||
|
]
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Caddy反代配置
|
### Caddy反代配置
|
||||||
|
|
||||||
```
|
```Caddyfile
|
||||||
example.com {
|
example.com {
|
||||||
reverse_proxy {
|
reverse_proxy {
|
||||||
to 127.0.0.1:7210
|
to 127.0.0.1:7210
|
||||||
@@ -84,14 +149,17 @@ example.com {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## TODO & BETA
|
### 前端页面
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## TODO & DEV
|
||||||
|
|
||||||
### TODO
|
### TODO
|
||||||
|
|
||||||
- [x] 允许更多参数通过config结构传入
|
|
||||||
- [x] 改进程序效率
|
|
||||||
- [x] 用户鉴权
|
- [x] 用户鉴权
|
||||||
|
- [x] 仓库黑名单
|
||||||
|
- [x] 仓库白名单
|
||||||
|
|
||||||
### BETA
|
### DEV
|
||||||
|
|
||||||
- [x] Docker Pull 代理
|
- [x] Docker Pull 代理
|
||||||
|
|||||||
31
SECURITY.MD
Normal file
31
SECURITY.MD
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
# 安全政策
|
||||||
|
|
||||||
|
## 支持的版本
|
||||||
|
|
||||||
|
以下版本的项目目前正在接受安全更新:
|
||||||
|
|
||||||
|
| 版本 | 是否支持 |
|
||||||
|
| --- | --- |
|
||||||
|
| v1.x.x | :white_check_mark: |
|
||||||
|
| **w**a/b/c... | :warning: 此为PRE-RELEASE版本,用于开发与测试,可能存在未知的问题 |
|
||||||
|
| v0.x.x | :x: 这些版本不再受支持 |
|
||||||
|
|
||||||
|
### 用户须知
|
||||||
|
|
||||||
|
本项目为开源项目,开发者不对使用本项目造成的任何损失或问题承担责任。用户需自行评估并承担使用本项目的风险。
|
||||||
|
|
||||||
|
使用本项目,请遵循 **[WSL (WJQSERVER-STUDIO LICENSE)](https://wjqserver-studio.github.io/LICENSE/LICENSE.html)** 协议。
|
||||||
|
|
||||||
|
本项目所有文件均受到 WSL (WJQSERVER-STUDIO LICENSE) 协议保护,任何人不得在任何情况下以非 WSL (WJQSERVER-STUDIO LICENSE) 协议内规定的方式使用,复制,修改,编译,发布,分发,再许可,或者出售本项目的任何部分。
|
||||||
|
|
||||||
|
## 报告漏洞
|
||||||
|
|
||||||
|
如果您发现本项目存在安全漏洞,请通过发送ISSUES或尝试联系项目维护者来报告。请在您的报告中包含以下信息:
|
||||||
|
|
||||||
|
1. 漏洞的描述,包括重现步骤。
|
||||||
|
2. 受到漏洞影响的项目版本。
|
||||||
|
3. 任何缓解因素,如利用难度或漏洞影响。
|
||||||
|
|
||||||
|
项目维护者将审查您的报告,并尽快解决该漏洞。请注意,根据漏洞的复杂程度,可能需要一些时间来调查和修复问题。
|
||||||
|
|
||||||
|
感谢您帮助保护本项目的安全!
|
||||||
81
api/api.go
Normal file
81
api/api.go
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"ghproxy/config"
|
||||||
|
"ghproxy/logger"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
router *gin.Engine
|
||||||
|
cfg *config.Config
|
||||||
|
)
|
||||||
|
|
||||||
|
// 日志模块
|
||||||
|
var (
|
||||||
|
logw = logger.Logw
|
||||||
|
logInfo = logger.LogInfo
|
||||||
|
logWarning = logger.LogWarning
|
||||||
|
logError = logger.LogError
|
||||||
|
)
|
||||||
|
|
||||||
|
func InitHandleRouter(cfg *config.Config, router *gin.Engine) {
|
||||||
|
// 设置路由
|
||||||
|
apiRouter := router.Group("api")
|
||||||
|
{
|
||||||
|
apiRouter.GET("/size_limit", func(c *gin.Context) {
|
||||||
|
SizeLimitHandler(cfg, c)
|
||||||
|
})
|
||||||
|
apiRouter.GET("/whitelist/status", func(c *gin.Context) {
|
||||||
|
WhiteListStatusHandler(c, cfg)
|
||||||
|
})
|
||||||
|
apiRouter.GET("/blacklist/status", func(c *gin.Context) {
|
||||||
|
BlackListStatusHandler(c, cfg)
|
||||||
|
})
|
||||||
|
apiRouter.GET("/cors/status", func(c *gin.Context) {
|
||||||
|
CorsStatusHandler(c, cfg)
|
||||||
|
})
|
||||||
|
apiRouter.GET("/healthcheck", func(c *gin.Context) {
|
||||||
|
HealthcheckHandler(c)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
logInfo("API router Init success")
|
||||||
|
}
|
||||||
|
|
||||||
|
func SizeLimitHandler(cfg *config.Config, c *gin.Context) {
|
||||||
|
sizeLimit := cfg.Server.SizeLimit / 1024 / 1024
|
||||||
|
c.Writer.Header().Set("Content-Type", "application/json")
|
||||||
|
json.NewEncoder(c.Writer).Encode(map[string]interface{}{
|
||||||
|
"MaxResponseBodySize": sizeLimit,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func WhiteListStatusHandler(c *gin.Context, cfg *config.Config) {
|
||||||
|
c.Writer.Header().Set("Content-Type", "application/json")
|
||||||
|
json.NewEncoder(c.Writer).Encode(map[string]interface{}{
|
||||||
|
"Whitelist": cfg.Whitelist.Enabled,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func BlackListStatusHandler(c *gin.Context, cfg *config.Config) {
|
||||||
|
c.Writer.Header().Set("Content-Type", "application/json")
|
||||||
|
json.NewEncoder(c.Writer).Encode(map[string]interface{}{
|
||||||
|
"Blacklist": cfg.Blacklist.Enabled,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func CorsStatusHandler(c *gin.Context, cfg *config.Config) {
|
||||||
|
c.Writer.Header().Set("Content-Type", "application/json")
|
||||||
|
json.NewEncoder(c.Writer).Encode(map[string]interface{}{
|
||||||
|
"Cors": cfg.CORS.Enabled,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func HealthcheckHandler(c *gin.Context) {
|
||||||
|
c.Writer.Header().Set("Content-Type", "application/json")
|
||||||
|
json.NewEncoder(c.Writer).Encode(map[string]interface{}{
|
||||||
|
"Status": "OK",
|
||||||
|
})
|
||||||
|
}
|
||||||
52
auth/auth.go
Normal file
52
auth/auth.go
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
package auth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"ghproxy/config"
|
||||||
|
"ghproxy/logger"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 日志模块
|
||||||
|
var (
|
||||||
|
logw = logger.Logw
|
||||||
|
logInfo = logger.LogInfo
|
||||||
|
logWarning = logger.LogWarning
|
||||||
|
logError = logger.LogError
|
||||||
|
)
|
||||||
|
|
||||||
|
// Auth Init
|
||||||
|
func Init(cfg *config.Config) {
|
||||||
|
if cfg.Blacklist.Enabled {
|
||||||
|
LoadBlacklist(cfg)
|
||||||
|
}
|
||||||
|
if cfg.Whitelist.Enabled {
|
||||||
|
LoadWhitelist(cfg)
|
||||||
|
}
|
||||||
|
logInfo("Auth Init")
|
||||||
|
}
|
||||||
|
|
||||||
|
func AuthHandler(c *gin.Context, cfg *config.Config) bool {
|
||||||
|
// 如果身份验证未启用,直接返回 true
|
||||||
|
if !cfg.Auth.Enabled {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取 auth_token 参数
|
||||||
|
authToken := c.Query("auth_token")
|
||||||
|
logInfo("auth_token received: %s", authToken)
|
||||||
|
|
||||||
|
// 验证 token
|
||||||
|
if authToken == "" {
|
||||||
|
logWarning("auth FAILED: no auth_token provided")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
isValid := authToken == cfg.Auth.AuthToken
|
||||||
|
if !isValid {
|
||||||
|
logWarning("auth FAILED: invalid auth_token: %s", authToken)
|
||||||
|
}
|
||||||
|
|
||||||
|
logInfo("auth SUCCESS: %t", isValid)
|
||||||
|
return isValid
|
||||||
|
}
|
||||||
56
auth/blacklist.go
Normal file
56
auth/blacklist.go
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
package auth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"ghproxy/config"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type BlacklistConfig struct {
|
||||||
|
Blacklist []string `json:"blacklist"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
cfg *config.Config
|
||||||
|
blacklistfile = "/data/ghproxy/config/blacklist.json"
|
||||||
|
blacklist *BlacklistConfig
|
||||||
|
)
|
||||||
|
|
||||||
|
func LoadBlacklist(cfg *config.Config) {
|
||||||
|
blacklistfile = cfg.Blacklist.BlacklistFile
|
||||||
|
blacklist = &BlacklistConfig{}
|
||||||
|
|
||||||
|
data, err := os.ReadFile(blacklistfile)
|
||||||
|
if err != nil {
|
||||||
|
logError("Failed to read blacklist file: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.Unmarshal(data, blacklist)
|
||||||
|
if err != nil {
|
||||||
|
logError("Failed to unmarshal blacklist JSON: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fullrepo: "owner/repo" or "owner/*"
|
||||||
|
func CheckBlacklist(fullrepo string) bool {
|
||||||
|
return forRangeCheckBlacklist(blacklist.Blacklist, fullrepo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func sliceRepoName_Blacklist(fullrepo string) (string, string) {
|
||||||
|
s := strings.Split(fullrepo, "/")
|
||||||
|
if len(s) != 2 {
|
||||||
|
return "", ""
|
||||||
|
}
|
||||||
|
return s[0], s[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
func forRangeCheckBlacklist(blist []string, fullrepo string) bool {
|
||||||
|
repoUser, _ := sliceRepoName_Blacklist(fullrepo)
|
||||||
|
for _, blocked := range blist {
|
||||||
|
if blocked == fullrepo || (strings.HasSuffix(blocked, "/*") && strings.HasPrefix(repoUser, blocked[:len(blocked)-2])) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
54
auth/whitelist.go
Normal file
54
auth/whitelist.go
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
package auth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"ghproxy/config"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type WhitelistConfig struct {
|
||||||
|
Whitelist []string `json:"whitelist"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
whitelistfile = "/data/ghproxy/config/whitelist.json"
|
||||||
|
whitelist *WhitelistConfig
|
||||||
|
)
|
||||||
|
|
||||||
|
func LoadWhitelist(cfg *config.Config) {
|
||||||
|
whitelistfile = cfg.Whitelist.WhitelistFile
|
||||||
|
whitelist = &WhitelistConfig{}
|
||||||
|
|
||||||
|
data, err := os.ReadFile(whitelistfile)
|
||||||
|
if err != nil {
|
||||||
|
logError("Failed to read whitelist file: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.Unmarshal(data, whitelist)
|
||||||
|
if err != nil {
|
||||||
|
logError("Failed to unmarshal whitelist JSON: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func CheckWhitelist(fullrepo string) bool {
|
||||||
|
return forRangeCheckWhitelist(whitelist.Whitelist, fullrepo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func sliceRepoName_Whitelist(fullrepo string) (string, string) {
|
||||||
|
s := strings.Split(fullrepo, "/")
|
||||||
|
if len(s) != 2 {
|
||||||
|
return "", ""
|
||||||
|
}
|
||||||
|
return s[0], s[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
func forRangeCheckWhitelist(wlist []string, fullrepo string) bool {
|
||||||
|
repoUser, _ := sliceRepoName_Whitelist(fullrepo)
|
||||||
|
for _, blocked := range wlist {
|
||||||
|
if blocked == fullrepo || (strings.HasSuffix(blocked, "/*") && strings.HasPrefix(repoUser, blocked[:len(blocked)-2])) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
https_port 443
|
https_port 443
|
||||||
order cache before rewrite
|
order cache before rewrite
|
||||||
cache {
|
cache {
|
||||||
cache_name W-Cache
|
cache_name GhProxyCache
|
||||||
}
|
}
|
||||||
log {
|
log {
|
||||||
level INFO
|
level INFO
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
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]}"` {
|
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"
|
time_format "02/Jan/2006:15:04:05 -0700"
|
||||||
}
|
}
|
||||||
output file /data/caddy/log/{args.0}/access.log {
|
output file /data/caddy/log/{args[0]}/access.log {
|
||||||
roll_size 5MB
|
roll_size 5MB
|
||||||
roll_keep 10
|
roll_keep 10
|
||||||
roll_keep_for 24h
|
roll_keep_for 24h
|
||||||
@@ -48,8 +48,8 @@
|
|||||||
(cache) {
|
(cache) {
|
||||||
cache {
|
cache {
|
||||||
allowed_http_verbs GET
|
allowed_http_verbs GET
|
||||||
stale {args.0}
|
stale {args[0]}
|
||||||
ttl {args.1}
|
ttl {args[1]}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,30 +60,22 @@
|
|||||||
header_up X-Forwarded-Proto {http.request.header.CF-Visitor}
|
header_up X-Forwarded-Proto {http.request.header.CF-Visitor}
|
||||||
}
|
}
|
||||||
|
|
||||||
(buffer) {
|
|
||||||
flush_interval 2000s
|
|
||||||
buffer_responses
|
|
||||||
max_buffer_size 256k
|
|
||||||
}
|
|
||||||
|
|
||||||
(rate_limit) {
|
(rate_limit) {
|
||||||
route /* {
|
route /* {
|
||||||
rate_limit {remote.ip} {args.0}r/m 10000 429
|
rate_limit {remote.ip} {args[0]}r/m 10000 429
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
:80 {
|
:80 {
|
||||||
reverse_proxy {
|
reverse_proxy {
|
||||||
to 127.0.0.1:8080
|
to h2c://127.0.0.1:8080
|
||||||
import header_realip
|
import header_realip
|
||||||
}
|
}
|
||||||
import log go
|
import log ghproxy
|
||||||
import cache 0s 600s
|
import cache 0s 300s
|
||||||
import error_page
|
import error_page
|
||||||
import encode
|
import encode
|
||||||
route /* {
|
import rate_limit 60
|
||||||
rate_limit {remote.ip} 60r/m 10000 429
|
|
||||||
}
|
|
||||||
route / {
|
route / {
|
||||||
root /data/www
|
root /data/www
|
||||||
file_server
|
file_server
|
||||||
@@ -92,24 +84,13 @@
|
|||||||
route /favicon.ico {
|
route /favicon.ico {
|
||||||
root /data/www
|
root /data/www
|
||||||
file_server
|
file_server
|
||||||
import cache 60s 24h
|
import cache 0s 24h
|
||||||
}
|
|
||||||
handle_errors {
|
|
||||||
@redirects `{err.status_code} in [301, 302, 307]`
|
|
||||||
reverse_proxy @redirects {
|
|
||||||
header_up Location {http.response.header.Location}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
route /v2* {
|
route /api* {
|
||||||
reverse_proxy https://registry-1.docker.io {
|
rate_limit {remote.ip} 15r/m 10000 429
|
||||||
header_up Host registry-1.docker.io
|
import cache 0s 6h
|
||||||
header_up X-Real-IP {remote}
|
}
|
||||||
header_up X-Forwarded-For {http.request.header.X-Forwarded-For}
|
|
||||||
header_up X-Forwarded-Proto {scheme}
|
|
||||||
header_up Authorization {http.request.header.Authorization}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
import /data/caddy/config.d/*
|
import /data/caddy/config.d/*
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
https_port 443
|
https_port 443
|
||||||
order cache before rewrite
|
order cache before rewrite
|
||||||
cache {
|
cache {
|
||||||
cache_name W-Cache
|
cache_name GhProxyCache
|
||||||
}
|
}
|
||||||
log {
|
log {
|
||||||
level INFO
|
level INFO
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
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]}"` {
|
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"
|
time_format "02/Jan/2006:15:04:05 -0700"
|
||||||
}
|
}
|
||||||
output file /data/caddy/log/{args.0}/access.log {
|
output file /data/caddy/log/{args[0]}/access.log {
|
||||||
roll_size 5MB
|
roll_size 5MB
|
||||||
roll_keep 10
|
roll_keep 10
|
||||||
roll_keep_for 24h
|
roll_keep_for 24h
|
||||||
@@ -48,8 +48,8 @@
|
|||||||
(cache) {
|
(cache) {
|
||||||
cache {
|
cache {
|
||||||
allowed_http_verbs GET
|
allowed_http_verbs GET
|
||||||
stale {args.0}
|
stale {args[0]}
|
||||||
ttl {args.1}
|
ttl {args[1]}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,30 +60,22 @@
|
|||||||
header_up X-Forwarded-Proto {http.request.header.CF-Visitor}
|
header_up X-Forwarded-Proto {http.request.header.CF-Visitor}
|
||||||
}
|
}
|
||||||
|
|
||||||
(buffer) {
|
|
||||||
flush_interval 2000s
|
|
||||||
buffer_responses
|
|
||||||
max_buffer_size 256k
|
|
||||||
}
|
|
||||||
|
|
||||||
(rate_limit) {
|
(rate_limit) {
|
||||||
route /* {
|
route /* {
|
||||||
rate_limit {remote.ip} {args.0}r/m 10000 429
|
rate_limit {remote.ip} {args[0]}r/m 10000 429
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
:80 {
|
:80 {
|
||||||
reverse_proxy {
|
reverse_proxy {
|
||||||
to 127.0.0.1:8080
|
to h2c://127.0.0.1:8080
|
||||||
import header_realip
|
import header_realip
|
||||||
}
|
}
|
||||||
import log go
|
import log ghproxy
|
||||||
import cache 0s 600s
|
import cache 0s 300s
|
||||||
import error_page
|
import error_page
|
||||||
import encode
|
import encode
|
||||||
route /* {
|
import rate_limit 60
|
||||||
rate_limit {remote.ip} 60r/m 10000 429
|
|
||||||
}
|
|
||||||
route / {
|
route / {
|
||||||
root /data/www
|
root /data/www
|
||||||
file_server
|
file_server
|
||||||
@@ -92,24 +84,13 @@
|
|||||||
route /favicon.ico {
|
route /favicon.ico {
|
||||||
root /data/www
|
root /data/www
|
||||||
file_server
|
file_server
|
||||||
import cache 60s 24h
|
import cache 0s 24h
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handle_errors {
|
route /api* {
|
||||||
@redirects `{err.status_code} in [301, 302, 307]`
|
rate_limit {remote.ip} 15r/m 10000 429
|
||||||
reverse_proxy @redirects {
|
import cache 0s 6h
|
||||||
header_up Location {http.response.header.Location}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
route /v2* {
|
|
||||||
reverse_proxy https://registry-1.docker.io {
|
|
||||||
header_up Host registry-1.docker.io
|
|
||||||
header_up X-Real-IP {remote}
|
|
||||||
header_up X-Forwarded-For {http.request.header.X-Forwarded-For}
|
|
||||||
header_up X-Forwarded-Proto {scheme}
|
|
||||||
header_up Authorization {http.request.header.Authorization}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
7
config/blacklist.json
Normal file
7
config/blacklist.json
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"blacklist": [
|
||||||
|
"black/list",
|
||||||
|
"test/test1",
|
||||||
|
"example/*"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -1,30 +1,58 @@
|
|||||||
package config
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"github.com/BurntSushi/toml"
|
||||||
|
|
||||||
"gopkg.in/yaml.v3"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Port int `yaml:"port"`
|
Server ServerConfig
|
||||||
Host string `yaml:"host"`
|
Pages PagesConfig
|
||||||
SizeLimit int `yaml:"sizelimit"`
|
Log LogConfig
|
||||||
LogFilePath string `yaml:"logfilepath"`
|
CORS CORSConfig
|
||||||
CORSOrigin bool `yaml:"CorsAllowOrigins"`
|
Auth AuthConfig
|
||||||
Auth bool `yaml:"auth"`
|
Blacklist BlacklistConfig
|
||||||
AuthToken string `yaml:"authtoken"`
|
Whitelist WhitelistConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadConfig 从 YAML 配置文件加载配置
|
type ServerConfig struct {
|
||||||
|
Port int `toml:"port"`
|
||||||
|
Host string `toml:"host"`
|
||||||
|
SizeLimit int `toml:"sizeLimit"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PagesConfig struct {
|
||||||
|
Enabled bool `toml:"enabled"`
|
||||||
|
StaticDir string `toml:"staticDir"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type LogConfig struct {
|
||||||
|
LogFilePath string `toml:"logFilePath"`
|
||||||
|
MaxLogSize int `toml:"maxLogSize"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type CORSConfig struct {
|
||||||
|
Enabled bool `toml:"enabled"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AuthConfig struct {
|
||||||
|
Enabled bool `toml:"enabled"`
|
||||||
|
AuthToken string `toml:"authToken"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type BlacklistConfig struct {
|
||||||
|
Enabled bool `toml:"enabled"`
|
||||||
|
BlacklistFile string `toml:"blacklistFile"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type WhitelistConfig struct {
|
||||||
|
Enabled bool `toml:"enabled"`
|
||||||
|
WhitelistFile string `toml:"whitelistFile"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadConfig 从 TOML 配置文件加载配置
|
||||||
func LoadConfig(filePath string) (*Config, error) {
|
func LoadConfig(filePath string) (*Config, error) {
|
||||||
var config Config
|
var config Config
|
||||||
data, err := os.ReadFile(filePath)
|
if _, err := toml.DecodeFile(filePath, &config); err != nil {
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
err = yaml.Unmarshal(data, &config)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &config, nil
|
return &config, nil
|
||||||
|
|||||||
27
config/config.toml
Normal file
27
config/config.toml
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
[server]
|
||||||
|
host = "127.0.0.1"
|
||||||
|
port = 8080
|
||||||
|
sizeLimit = 131072000 # 125MB
|
||||||
|
|
||||||
|
[pages]
|
||||||
|
enabled = false
|
||||||
|
staticDir = "/data/www"
|
||||||
|
|
||||||
|
[log]
|
||||||
|
logFilePath = "/data/ghproxy/log/ghproxy.log"
|
||||||
|
maxLogSize = 5 # MB
|
||||||
|
|
||||||
|
[cors]
|
||||||
|
enabled = true
|
||||||
|
|
||||||
|
[auth]
|
||||||
|
authToken = "token"
|
||||||
|
enabled = false
|
||||||
|
|
||||||
|
[blacklist]
|
||||||
|
blacklistFile = "/data/ghproxy/config/blacklist.json"
|
||||||
|
enabled = false
|
||||||
|
|
||||||
|
[whitelist]
|
||||||
|
enabled = false
|
||||||
|
whitelistFile = "/data/ghproxy/config/whitelist.json"
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
port: 8080
|
|
||||||
host: "127.0.0.1"
|
|
||||||
sizelimit: 131072000 # 125MB
|
|
||||||
logfilepath: "/data/ghproxy/log/ghproxy-0rtt.log"
|
|
||||||
CorsAllowOrigins: true
|
|
||||||
auth: true
|
|
||||||
authtoken: "test"
|
|
||||||
7
config/whitelist.json
Normal file
7
config/whitelist.json
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"whitelist": [
|
||||||
|
"white/list",
|
||||||
|
"white/test1",
|
||||||
|
"example/*"
|
||||||
|
]
|
||||||
|
}
|
||||||
27
deploy/config.toml
Normal file
27
deploy/config.toml
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
[server]
|
||||||
|
host = "127.0.0.1"
|
||||||
|
port = 8080
|
||||||
|
sizeLimit = 131072000 # 125MB
|
||||||
|
|
||||||
|
[pages]
|
||||||
|
enabled = true
|
||||||
|
staticDir = "/root/data/ghproxy/pages"
|
||||||
|
|
||||||
|
[log]
|
||||||
|
logFilePath = "/root/data/ghproxy/log/ghproxy.log"
|
||||||
|
maxLogSize = 5 # MB
|
||||||
|
|
||||||
|
[cors]
|
||||||
|
enabled = true
|
||||||
|
|
||||||
|
[auth]
|
||||||
|
authToken = "token"
|
||||||
|
enabled = false
|
||||||
|
|
||||||
|
[blacklist]
|
||||||
|
blacklistFile = "/root/data/ghproxy/config/blacklist.json"
|
||||||
|
enabled = false
|
||||||
|
|
||||||
|
[whitelist]
|
||||||
|
enabled = false
|
||||||
|
whitelistFile = "/root/data/ghproxy/config/whitelist.json"
|
||||||
13
deploy/ghproxy.service
Normal file
13
deploy/ghproxy.service
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Github Proxy Service
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
ExecStart=/bin/bash -c '/root/data/ghproxy/ghproxy -cfg /root/data/ghproxy/config/config.toml > /root/data/ghproxy/log/run.log 2>&1'
|
||||||
|
WorkingDirectory=/root/data/ghproxy
|
||||||
|
Restart=always
|
||||||
|
User=root
|
||||||
|
Group=root
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
91
deploy/install-dev.sh
Normal file
91
deploy/install-dev.sh
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
# /bin/bash
|
||||||
|
|
||||||
|
# install packages
|
||||||
|
install() {
|
||||||
|
if [ $# -eq 0 ]; then
|
||||||
|
echo "ARGS NOT FOUND"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
for package in "$@"; do
|
||||||
|
if ! command -v "$package" &>/dev/null; then
|
||||||
|
if command -v dnf &>/dev/null; then
|
||||||
|
dnf -y update && dnf install -y "$package"
|
||||||
|
elif command -v yum &>/dev/null; then
|
||||||
|
yum -y update && yum -y install "$package"
|
||||||
|
elif command -v apt &>/dev/null; then
|
||||||
|
apt update -y && apt install -y "$package"
|
||||||
|
elif command -v apk &>/dev/null; then
|
||||||
|
apk update && apk add "$package"
|
||||||
|
else
|
||||||
|
echo "UNKNOWN PACKAGE MANAGER"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# 安装依赖包
|
||||||
|
install curl wget sed
|
||||||
|
|
||||||
|
# 查看当前架构是否为linux/amd64或linux/arm64
|
||||||
|
ARCH=$(uname -m)
|
||||||
|
if [ "$ARCH" != "x86_64" ] && [ "$ARCH" != "aarch64" ]; then
|
||||||
|
echo " $ARCH 架构不被支持"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 重写架构值,改为amd64或arm64
|
||||||
|
if [ "$ARCH" == "x86_64" ]; then
|
||||||
|
ARCH="amd64"
|
||||||
|
elif [ "$ARCH" == "aarch64" ]; then
|
||||||
|
ARCH="arm64"
|
||||||
|
fi
|
||||||
|
|
||||||
|
read -p "请输入程序监听的端口(默认8080): " PORT
|
||||||
|
if [ -z "$PORT" ]; then
|
||||||
|
PORT=8080
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 创建目录
|
||||||
|
mkdir -p /root/data/ghproxy
|
||||||
|
mkdir -p /root/data/ghproxy/config
|
||||||
|
mkdir -p /root/data/ghproxy/log
|
||||||
|
mkdir -p /root/data/ghproxy/pages
|
||||||
|
|
||||||
|
# 获取最新版本号
|
||||||
|
VERSION=$(curl -s https://raw.githubusercontent.com/WJQSERVER-STUDIO/ghproxy/main/DEV-VERSION)
|
||||||
|
wget -q -O /root/data/ghproxy/VERSION https://raw.githubusercontent.com/WJQSERVER-STUDIO/ghproxy/main/DEV-VERSION
|
||||||
|
|
||||||
|
# 下载ghproxy
|
||||||
|
wget -q -O /root/data/ghproxy/ghproxy https://github.com/WJQSERVER-STUDIO/ghproxy/releases/download/$VERSION/ghproxy-linux-$ARCH
|
||||||
|
chmod +x /root/data/ghproxy/ghproxy
|
||||||
|
|
||||||
|
# 下载pages
|
||||||
|
wget -q -O /root/data/ghproxy/pages/index.html https://raw.githubusercontent.com/WJQSERVER-STUDIO/ghproxy/main/pages/index.html
|
||||||
|
wget -q -O /root/data/ghproxy/pages/favicon.ico https://raw.githubusercontent.com/WJQSERVER-STUDIO/ghproxy/main/pages/favicon.ico
|
||||||
|
|
||||||
|
|
||||||
|
# 下载配置文件
|
||||||
|
if [ -f /root/data/ghproxy/config/config.toml ]; then
|
||||||
|
echo "配置文件已存在, 跳过下载"
|
||||||
|
echo "[WARNING] 请检查配置文件是否正确,DEV版本升级时请注意配置文件兼容性"
|
||||||
|
sleep 2
|
||||||
|
else
|
||||||
|
wget -q -O /root/data/ghproxy/config/config.toml https://raw.githubusercontent.com/WJQSERVER-STUDIO/ghproxy/main/deploy/config.toml
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 替换 port = 8080
|
||||||
|
sed -i "s/port = 8080/port = $PORT/g" /root/data/ghproxy/config/config.toml
|
||||||
|
|
||||||
|
# 下载systemd服务文件
|
||||||
|
wget -q -O /etc/systemd/system/ghproxy.service https://raw.githubusercontent.com/WJQSERVER-STUDIO/ghproxy/main/deploy/ghproxy.service
|
||||||
|
|
||||||
|
# 启动ghproxy
|
||||||
|
systemctl daemon-reload
|
||||||
|
systemctl enable ghproxy
|
||||||
|
systemctl start ghproxy
|
||||||
|
|
||||||
|
echo "ghproxy 安装成功, 监听端口为 $PORT"
|
||||||
85
deploy/install.sh
Normal file
85
deploy/install.sh
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
# /bin/bash
|
||||||
|
|
||||||
|
# install packages
|
||||||
|
install() {
|
||||||
|
if [ $# -eq 0 ]; then
|
||||||
|
echo "ARGS NOT FOUND"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
for package in "$@"; do
|
||||||
|
if ! command -v "$package" &>/dev/null; then
|
||||||
|
if command -v dnf &>/dev/null; then
|
||||||
|
dnf -y update && dnf install -y "$package"
|
||||||
|
elif command -v yum &>/dev/null; then
|
||||||
|
yum -y update && yum -y install "$package"
|
||||||
|
elif command -v apt &>/dev/null; then
|
||||||
|
apt update -y && apt install -y "$package"
|
||||||
|
elif command -v apk &>/dev/null; then
|
||||||
|
apk update && apk add "$package"
|
||||||
|
else
|
||||||
|
echo "UNKNOWN PACKAGE MANAGER"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# 安装依赖包
|
||||||
|
install curl wget -q sed
|
||||||
|
|
||||||
|
# 查看当前架构是否为linux/amd64或linux/arm64
|
||||||
|
ARCH=$(uname -m)
|
||||||
|
if [ "$ARCH" != "x86_64" ] && [ "$ARCH" != "aarch64" ]; then
|
||||||
|
echo " $ARCH 架构不被支持"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 重写架构值,改为amd64或arm64
|
||||||
|
if [ "$ARCH" == "x86_64" ]; then
|
||||||
|
ARCH="amd64"
|
||||||
|
elif [ "$ARCH" == "aarch64" ]; then
|
||||||
|
ARCH="arm64"
|
||||||
|
fi
|
||||||
|
|
||||||
|
read -p "请输入程序监听的端口(默认8080): " PORT
|
||||||
|
if [ -z "$PORT" ]; then
|
||||||
|
PORT=8080
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 创建目录
|
||||||
|
mkdir -p /root/data/ghproxy
|
||||||
|
mkdir -p /root/data/ghproxy/config
|
||||||
|
mkdir -p /root/data/ghproxy/log
|
||||||
|
|
||||||
|
# 获取最新版本号
|
||||||
|
VERSION=$(curl -s https://raw.githubusercontent.com/WJQSERVER-STUDIO/ghproxy/main/VERSION)
|
||||||
|
wget -q -O /root/data/ghproxy/VERSION https://raw.githubusercontent.com/WJQSERVER-STUDIO/ghproxy/main/VERSION
|
||||||
|
|
||||||
|
# 下载ghproxy
|
||||||
|
wget -q -O /root/data/ghproxy/ghproxy https://github.com/WJQSERVER-STUDIO/ghproxy/releases/download/$VERSION/ghproxy-linux-$ARCH
|
||||||
|
chmod +x /root/data/ghproxy/ghproxy
|
||||||
|
|
||||||
|
# 下载配置文件
|
||||||
|
if [ -f /root/data/ghproxy/config/config.toml ]; then
|
||||||
|
echo "配置文件已存在, 跳过下载"
|
||||||
|
echo "请检查配置文件是否正确,跨大版本升级时请注意配置文件兼容性"
|
||||||
|
sleep 2
|
||||||
|
else
|
||||||
|
wget -q -O /root/data/ghproxy/config/config.toml https://raw.githubusercontent.com/WJQSERVER-STUDIO/ghproxy/main/deploy/config.toml
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 替换 port = 8080
|
||||||
|
sed -i "s/port = 8080/port = $PORT/g" /root/data/ghproxy/config/config.toml
|
||||||
|
|
||||||
|
# 下载systemd服务文件
|
||||||
|
wget -q -O /etc/systemd/system/ghproxy.service https://raw.githubusercontent.com/WJQSERVER-STUDIO/ghproxy/main/deploy/ghproxy.service
|
||||||
|
|
||||||
|
# 启动ghproxy
|
||||||
|
systemctl daemon-reload
|
||||||
|
systemctl enable ghproxy
|
||||||
|
systemctl start ghproxy
|
||||||
|
|
||||||
|
echo "ghproxy 安装成功, 监听端口为 $PORT"
|
||||||
13
deploy/uninstall.sh
Normal file
13
deploy/uninstall.sh
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# /bin/bash
|
||||||
|
|
||||||
|
# 停止 ghproxy 服务
|
||||||
|
systemctl stop ghproxy
|
||||||
|
|
||||||
|
# 删除 ghproxy 服务
|
||||||
|
systemctl disable ghproxy
|
||||||
|
rm /etc/systemd/system/ghproxy.service
|
||||||
|
|
||||||
|
# 删除 ghproxy 文件夹
|
||||||
|
rm -r /root/data/ghproxy
|
||||||
|
|
||||||
|
echo "ghproxy 已成功卸载"
|
||||||
@@ -1,19 +1,47 @@
|
|||||||
FROM wjqserver/caddy:daily
|
FROM wjqserver/caddy:daily-alpine AS builder
|
||||||
|
|
||||||
ARG USER=WJQSERVER-STUDIO
|
ARG USER=WJQSERVER-STUDIO
|
||||||
ARG REPO=ghproxy
|
ARG REPO=ghproxy
|
||||||
ARG APPLICATION=ghproxy
|
ARG APPLICATION=ghproxy
|
||||||
|
ARG TARGETOS
|
||||||
|
ARG TARGETARCH
|
||||||
|
ARG TARGETPLATFORM
|
||||||
|
|
||||||
|
# 创建文件夹
|
||||||
RUN mkdir -p /data/www
|
RUN mkdir -p /data/www
|
||||||
RUN mkdir -p /data/${APPLICATION}/config
|
RUN mkdir -p /data/${APPLICATION}/config
|
||||||
RUN mkdir -p /data/${APPLICATION}/log
|
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/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 wget -O /data/www/favicon.ico https://raw.githubusercontent.com/${USER}/${REPO}/main/pages/favicon.ico
|
||||||
RUN wget -O /data/caddy/Caddyfile https://raw.githubusercontent.com/${USER}/${REPO}/main/caddyfile/release/Caddyfile
|
|
||||||
|
# 后端
|
||||||
RUN VERSION=$(curl -s https://raw.githubusercontent.com/${USER}/${REPO}/main/DEV-VERSION) && \
|
RUN VERSION=$(curl -s https://raw.githubusercontent.com/${USER}/${REPO}/main/DEV-VERSION) && \
|
||||||
wget -O /data/${APPLICATION}/${APPLICATION} https://github.com/${USER}/${REPO}/releases/download/$VERSION/${APPLICATION}
|
wget -O /data/${APPLICATION}/${APPLICATION} https://github.com/${USER}/${REPO}/releases/download/$VERSION/${APPLICATION}-${TARGETOS}-${TARGETARCH}
|
||||||
RUN wget -O /data/${APPLICATION}/config.yaml https://raw.githubusercontent.com/${USER}/${REPO}/main/config/config.yaml
|
RUN wget -O /usr/local/bin/init.sh https://raw.githubusercontent.com/${USER}/${REPO}/main/docker/dockerfile/dev/init.sh
|
||||||
RUN wget -O /usr/local/bin/init.sh https://raw.githubusercontent.com/${USER}/${REPO}/main/init.sh
|
|
||||||
|
# 拉取配置
|
||||||
|
RUN wget -O /data/caddy/Caddyfile https://raw.githubusercontent.com/${USER}/${REPO}/main/caddyfile/dev/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:daily-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 /data/${APPLICATION}/${APPLICATION}
|
||||||
RUN chmod +x /usr/local/bin/init.sh
|
RUN chmod +x /usr/local/bin/init.sh
|
||||||
|
|
||||||
|
|||||||
27
docker/dockerfile/dev/init.sh
Normal file
27
docker/dockerfile/dev/init.sh
Normal 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
|
||||||
@@ -1,19 +1,47 @@
|
|||||||
FROM wjqserver/caddy:latest
|
FROM wjqserver/caddy:2.9.0-rc-alpine AS builder
|
||||||
|
|
||||||
ARG USER=WJQSERVER-STUDIO
|
ARG USER=WJQSERVER-STUDIO
|
||||||
ARG REPO=ghproxy
|
ARG REPO=ghproxy
|
||||||
ARG APPLICATION=ghproxy
|
ARG APPLICATION=ghproxy
|
||||||
|
ARG TARGETOS
|
||||||
|
ARG TARGETARCH
|
||||||
|
ARG TARGETPLATFORM
|
||||||
|
|
||||||
|
# 创建文件夹
|
||||||
RUN mkdir -p /data/www
|
RUN mkdir -p /data/www
|
||||||
RUN mkdir -p /data/${APPLICATION}/config
|
RUN mkdir -p /data/${APPLICATION}/config
|
||||||
RUN mkdir -p /data/${APPLICATION}/log
|
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/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 wget -O /data/www/favicon.ico https://raw.githubusercontent.com/${USER}/${REPO}/main/pages/favicon.ico
|
||||||
RUN wget -O /data/caddy/Caddyfile https://raw.githubusercontent.com/${USER}/${REPO}/main/caddyfile/release/Caddyfile
|
|
||||||
|
# 后端
|
||||||
RUN VERSION=$(curl -s https://raw.githubusercontent.com/${USER}/${REPO}/main/VERSION) && \
|
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}
|
wget -O /data/${APPLICATION}/${APPLICATION} https://github.com/${USER}/${REPO}/releases/download/$VERSION/${APPLICATION}-${TARGETOS}-${TARGETARCH}
|
||||||
RUN wget -O /data/${APPLICATION}/config.yaml https://raw.githubusercontent.com/${USER}/${REPO}/main/config/config.yaml
|
RUN wget -O /usr/local/bin/init.sh https://raw.githubusercontent.com/${USER}/${REPO}/main/docker/dockerfile/release/init.sh
|
||||||
RUN wget -O /usr/local/bin/init.sh https://raw.githubusercontent.com/${USER}/${REPO}/main/init.sh
|
|
||||||
|
# 拉取配置
|
||||||
|
RUN wget -O /data/caddy/Caddyfile https://raw.githubusercontent.com/${USER}/${REPO}/main/caddyfile/release/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 /data/${APPLICATION}/${APPLICATION}
|
||||||
RUN chmod +x /usr/local/bin/init.sh
|
RUN chmod +x /usr/local/bin/init.sh
|
||||||
|
|
||||||
|
|||||||
27
docker/dockerfile/release/init.sh
Normal file
27
docker/dockerfile/release/init.sh
Normal 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
|
||||||
52
go.mod
52
go.mod
@@ -1,50 +1,54 @@
|
|||||||
module ghproxy
|
module ghproxy
|
||||||
|
|
||||||
go 1.23.1
|
go 1.23.2
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/andybalholm/brotli v1.1.0 // indirect
|
github.com/BurntSushi/toml v1.4.0
|
||||||
github.com/bytedance/sonic v1.11.6 // indirect
|
github.com/gin-gonic/gin v1.10.0
|
||||||
github.com/bytedance/sonic/loader v0.1.1 // indirect
|
github.com/imroc/req/v3 v3.48.0
|
||||||
github.com/cloudflare/circl v1.4.0 // indirect
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/andybalholm/brotli v1.1.1 // indirect
|
||||||
|
github.com/bytedance/sonic v1.12.3 // indirect
|
||||||
|
github.com/bytedance/sonic/loader v0.2.0 // indirect
|
||||||
|
github.com/cloudflare/circl v1.5.0 // indirect
|
||||||
github.com/cloudwego/base64x v0.1.4 // indirect
|
github.com/cloudwego/base64x v0.1.4 // indirect
|
||||||
github.com/cloudwego/iasm v0.2.0 // indirect
|
github.com/cloudwego/iasm v0.2.0 // indirect
|
||||||
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
|
github.com/gabriel-vasile/mimetype v1.4.6 // indirect
|
||||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||||
github.com/gin-gonic/gin v1.10.0 // indirect
|
|
||||||
github.com/go-playground/locales v0.14.1 // indirect
|
github.com/go-playground/locales v0.14.1 // indirect
|
||||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||||
github.com/go-playground/validator/v10 v10.20.0 // indirect
|
github.com/go-playground/validator/v10 v10.22.1 // indirect
|
||||||
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
|
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
|
||||||
github.com/goccy/go-json v0.10.2 // indirect
|
github.com/goccy/go-json v0.10.3 // indirect
|
||||||
github.com/google/pprof v0.0.0-20240910150728-a0b0bb1d4134 // indirect
|
github.com/google/pprof v0.0.0-20241017200806-017d972448fc // indirect
|
||||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||||
github.com/imroc/req/v3 v3.46.1 // indirect
|
|
||||||
github.com/json-iterator/go v1.1.12 // indirect
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
github.com/klauspost/compress v1.17.9 // indirect
|
github.com/klauspost/compress v1.17.11 // indirect
|
||||||
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
|
github.com/klauspost/cpuid/v2 v2.2.8 // indirect
|
||||||
github.com/leodido/go-urn v1.4.0 // indirect
|
github.com/leodido/go-urn v1.4.0 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
github.com/onsi/ginkgo/v2 v2.20.2 // indirect
|
github.com/onsi/ginkgo/v2 v2.20.2 // indirect
|
||||||
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
|
||||||
github.com/quic-go/qpack v0.5.1 // indirect
|
github.com/quic-go/qpack v0.5.1 // indirect
|
||||||
github.com/quic-go/quic-go v0.47.0 // indirect
|
github.com/quic-go/quic-go v0.48.0 // indirect
|
||||||
github.com/refraction-networking/utls v1.6.7 // indirect
|
github.com/refraction-networking/utls v1.6.7 // indirect
|
||||||
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
|
||||||
go.uber.org/mock v0.4.0 // indirect
|
go.uber.org/mock v0.5.0 // indirect
|
||||||
golang.org/x/arch v0.8.0 // indirect
|
golang.org/x/arch v0.11.0 // indirect
|
||||||
golang.org/x/crypto v0.27.0 // indirect
|
golang.org/x/crypto v0.28.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 // indirect
|
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c // indirect
|
||||||
golang.org/x/mod v0.21.0 // indirect
|
golang.org/x/mod v0.21.0 // indirect
|
||||||
golang.org/x/net v0.29.0 // indirect
|
golang.org/x/net v0.30.0 // indirect
|
||||||
golang.org/x/sync v0.8.0 // indirect
|
golang.org/x/sync v0.8.0 // indirect
|
||||||
golang.org/x/sys v0.25.0 // indirect
|
golang.org/x/sys v0.26.0 // indirect
|
||||||
golang.org/x/text v0.18.0 // indirect
|
golang.org/x/text v0.19.0 // indirect
|
||||||
golang.org/x/tools v0.25.0 // indirect
|
golang.org/x/tools v0.26.0 // indirect
|
||||||
google.golang.org/protobuf v1.34.1 // indirect
|
google.golang.org/protobuf v1.35.1 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
118
go.sum
118
go.sum
@@ -1,50 +1,65 @@
|
|||||||
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
|
github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
|
||||||
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
|
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||||
github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0=
|
github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA=
|
||||||
github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
|
github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA=
|
||||||
github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=
|
github.com/bytedance/sonic v1.12.3 h1:W2MGa7RCU1QTeYRTPE3+88mVC0yXmsRQRChiyVocVjU=
|
||||||
|
github.com/bytedance/sonic v1.12.3/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk=
|
||||||
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
||||||
github.com/cloudflare/circl v1.4.0 h1:BV7h5MgrktNzytKmWjpOtdYrf0lkkbF8YMlBGPhJQrY=
|
github.com/bytedance/sonic/loader v0.2.0 h1:zNprn+lsIP06C/IqCHs3gPQIvnvpKbbxyXQP1iU4kWM=
|
||||||
github.com/cloudflare/circl v1.4.0/go.mod h1:PDRU+oXvdD7KCtgKxW95M5Z8BpSCJXQORiZFnBQS5QU=
|
github.com/bytedance/sonic/loader v0.2.0/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
||||||
|
github.com/cloudflare/circl v1.5.0 h1:hxIWksrX6XN5a1L2TI/h53AGPhNHoUBo+TD1ms9+pys=
|
||||||
|
github.com/cloudflare/circl v1.5.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
|
||||||
github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
|
github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
|
||||||
github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
|
github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
|
||||||
github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
|
github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
|
||||||
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
|
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
|
github.com/gabriel-vasile/mimetype v1.4.5 h1:J7wGKdGu33ocBOhGy0z653k/lFKLFDPJMG8Gql0kxn4=
|
||||||
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
|
github.com/gabriel-vasile/mimetype v1.4.5/go.mod h1:ibHel+/kbxn9x2407k1izTA1S81ku1z/DlgOW2QE0M4=
|
||||||
|
github.com/gabriel-vasile/mimetype v1.4.6 h1:3+PzJTKLkvgjeTbts6msPJt4DixhT4YtFNf1gtGe3zc=
|
||||||
|
github.com/gabriel-vasile/mimetype v1.4.6/go.mod h1:JX1qVKqZd40hUPpAfiNTe0Sne7hdfKSbOqqmkq8GCXc=
|
||||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||||
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
|
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
|
||||||
github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
|
github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
|
||||||
|
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||||
|
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||||
|
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||||
|
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||||
github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8=
|
github.com/go-playground/validator/v10 v10.22.1 h1:40JcKH+bBNGFczGuoBYgX4I6m/i27HYW8P9FDk5PbgA=
|
||||||
github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
github.com/go-playground/validator/v10 v10.22.1/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
||||||
|
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
|
||||||
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
||||||
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
||||||
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
|
||||||
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||||
|
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||||
|
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
github.com/google/pprof v0.0.0-20240910150728-a0b0bb1d4134 h1:c5FlPPgxOn7kJz3VoPLkQYQXGBS3EklQ4Zfi57uOuqQ=
|
github.com/google/pprof v0.0.0-20241009165004-a3522334989c h1:NDovD0SMpBYXlE1zJmS1q55vWB/fUQBcPAqAboZSccA=
|
||||||
github.com/google/pprof v0.0.0-20240910150728-a0b0bb1d4134/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
|
github.com/google/pprof v0.0.0-20241009165004-a3522334989c/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
|
||||||
|
github.com/google/pprof v0.0.0-20241017200806-017d972448fc h1:NGyrhhFhwvRAZg02jnYVg3GBQy0qGBKmFQJwaPmpmxs=
|
||||||
|
github.com/google/pprof v0.0.0-20241017200806-017d972448fc/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
|
||||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||||
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||||
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||||
github.com/imroc/req/v3 v3.46.1 h1:oahr2hBTb3AaFI4P6jkN0Elj2ZVKJcdQ/IjWqeIKjvc=
|
github.com/imroc/req/v3 v3.48.0 h1:IYuMGetuwLzOOTzDCquDqs912WNwpsPK0TBXWPIvoqg=
|
||||||
github.com/imroc/req/v3 v3.46.1/go.mod h1:weam9gmyb00QnOtu6HXSnk44dNFkIUQb5QdMx13FeUU=
|
github.com/imroc/req/v3 v3.48.0/go.mod h1:weam9gmyb00QnOtu6HXSnk44dNFkIUQb5QdMx13FeUU=
|
||||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||||
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
|
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
|
||||||
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
|
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
|
||||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||||
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
|
github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM=
|
||||||
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||||
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
||||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||||
@@ -57,66 +72,67 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G
|
|||||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||||
github.com/onsi/ginkgo/v2 v2.20.2 h1:7NVCeyIWROIAheY21RLS+3j2bb52W0W82tkberYytp4=
|
github.com/onsi/ginkgo/v2 v2.20.2 h1:7NVCeyIWROIAheY21RLS+3j2bb52W0W82tkberYytp4=
|
||||||
github.com/onsi/ginkgo/v2 v2.20.2/go.mod h1:K9gyxPIlb+aIvnZ8bd9Ak+YP18w3APlR+5coaZoE2ag=
|
github.com/onsi/ginkgo/v2 v2.20.2/go.mod h1:K9gyxPIlb+aIvnZ8bd9Ak+YP18w3APlR+5coaZoE2ag=
|
||||||
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
|
github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
|
||||||
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
|
github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
|
||||||
|
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
|
||||||
|
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
|
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
|
||||||
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
|
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
|
||||||
github.com/quic-go/quic-go v0.47.0 h1:yXs3v7r2bm1wmPTYNLKAAJTHMYkPEsfYJmTazXrCZ7Y=
|
github.com/quic-go/quic-go v0.47.0 h1:yXs3v7r2bm1wmPTYNLKAAJTHMYkPEsfYJmTazXrCZ7Y=
|
||||||
github.com/quic-go/quic-go v0.47.0/go.mod h1:3bCapYsJvXGZcipOHuu7plYtaV6tnF+z7wIFsU0WK9E=
|
github.com/quic-go/quic-go v0.47.0/go.mod h1:3bCapYsJvXGZcipOHuu7plYtaV6tnF+z7wIFsU0WK9E=
|
||||||
|
github.com/quic-go/quic-go v0.48.0 h1:2TCyvBrMu1Z25rvIAlnp2dPT4lgh/uTqLqiXVpp5AeU=
|
||||||
|
github.com/quic-go/quic-go v0.48.0/go.mod h1:yBgs3rWBOADpga7F+jJsb6Ybg1LSYiQvwWlLX+/6HMs=
|
||||||
github.com/refraction-networking/utls v1.6.7 h1:zVJ7sP1dJx/WtVuITug3qYUq034cDq9B2MR1K67ULZM=
|
github.com/refraction-networking/utls v1.6.7 h1:zVJ7sP1dJx/WtVuITug3qYUq034cDq9B2MR1K67ULZM=
|
||||||
github.com/refraction-networking/utls v1.6.7/go.mod h1:BC3O4vQzye5hqpmDTWUqi4P5DDhzJfkV1tdqtawQIH0=
|
github.com/refraction-networking/utls v1.6.7/go.mod h1:BC3O4vQzye5hqpmDTWUqi4P5DDhzJfkV1tdqtawQIH0=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||||
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
|
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
|
||||||
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||||
|
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
|
||||||
|
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
|
||||||
go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
|
go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
|
||||||
go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
|
go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
|
||||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU=
|
||||||
golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc=
|
go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=
|
||||||
golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
golang.org/x/arch v0.11.0 h1:KXV8WWKCXm6tRpLirl2szsO5j/oOODwZf4hATmGVNs4=
|
||||||
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
|
golang.org/x/arch v0.11.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
|
||||||
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
|
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
|
||||||
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
|
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY=
|
||||||
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk=
|
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8=
|
||||||
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY=
|
|
||||||
golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
|
golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
|
||||||
golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||||
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
|
golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
|
||||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
|
||||||
golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo=
|
|
||||||
golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0=
|
|
||||||
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
||||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
|
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
|
||||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
|
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
|
||||||
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||||
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
|
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||||
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
|
golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ=
|
||||||
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=
|
||||||
golang.org/x/tools v0.25.0 h1:oFU9pkj/iJgs+0DT+VMHrx+oBKs/LJMV+Uvg78sl+fE=
|
google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
|
||||||
golang.org/x/tools v0.25.0/go.mod h1:/vtpO8WL1N9cQC3FN5zPqb//fRXskFHbLKk4OW1Q7rg=
|
google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||||
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
|
||||||
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=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
|
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
|
||||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
|
||||||
|
|||||||
10
init.sh
10
init.sh
@@ -6,13 +6,21 @@ if [ ! -f /data/caddy/config/Caddyfile ]; then
|
|||||||
cp /data/caddy/Caddyfile /data/caddy/config/Caddyfile
|
cp /data/caddy/Caddyfile /data/caddy/config/Caddyfile
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [ ! -f /data/${APPLICATON}/config/blacklist.json ]; then
|
||||||
|
cp /data/${APPLICATON}/blacklist.json /data/${APPLICATON}/config/blacklist.json
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -f /data/${APPLICATON}/config/whitelist.json ]; then
|
||||||
|
cp /data/${APPLICATON}/whitelist.json /data/${APPLICATON}/config/whitelist.json
|
||||||
|
fi
|
||||||
|
|
||||||
if [ ! -f /data/${APPLICATON}/config/config.yaml ]; then
|
if [ ! -f /data/${APPLICATON}/config/config.yaml ]; then
|
||||||
cp /data/${APPLICATON}/config.yaml /data/${APPLICATON}/config/config.yaml
|
cp /data/${APPLICATON}/config.yaml /data/${APPLICATON}/config/config.yaml
|
||||||
fi
|
fi
|
||||||
|
|
||||||
/data/caddy/caddy run --config /data/caddy/config/Caddyfile > /data/${APPLICATON}/log/caddy.log 2>&1 &
|
/data/caddy/caddy run --config /data/caddy/config/Caddyfile > /data/${APPLICATON}/log/caddy.log 2>&1 &
|
||||||
|
|
||||||
/data/${APPLICATON}/${APPLICATON} > /data/ghproxy/log/run.log 2>&1 &
|
/data/${APPLICATON}/${APPLICATON} > /data/${APPLICATON}/log/run.log 2>&1 &
|
||||||
|
|
||||||
while [[ true ]]; do
|
while [[ true ]]; do
|
||||||
sleep 1
|
sleep 1
|
||||||
|
|||||||
167
logger/logger.go
167
logger/logger.go
@@ -1,44 +1,179 @@
|
|||||||
// logger/logger.go
|
|
||||||
package logger
|
package logger
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"archive/tar"
|
||||||
|
"compress/gzip"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
var logFile *os.File
|
var (
|
||||||
var logger *log.Logger
|
logw = Logw
|
||||||
|
logFile *os.File
|
||||||
|
logger *log.Logger
|
||||||
|
logChannel = make(chan string, 100)
|
||||||
|
quitChannel = make(chan struct{})
|
||||||
|
logFileMutex sync.Mutex
|
||||||
|
logFilePath = "/data/ghproxy/log/ghproxy.log"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 初始化,接受日志文件路径作为参数
|
||||||
|
func Init(logFilePath_input string, maxLogsize int) error {
|
||||||
|
logFileMutex.Lock()
|
||||||
|
defer logFileMutex.Unlock()
|
||||||
|
|
||||||
// Init 初始化日志记录器,接受日志文件路径作为参数
|
|
||||||
func Init(logFilePath string) error {
|
|
||||||
var err error
|
var err error
|
||||||
|
logFilePath = logFilePath_input
|
||||||
logFile, err = os.OpenFile(logFilePath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
|
logFile, err = os.OpenFile(logFilePath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
logger = log.New(logFile, "", 0) // 不使用默认前缀
|
logger = log.New(logFile, "", 0)
|
||||||
|
|
||||||
|
go logWorker()
|
||||||
|
go monitorLogSize(logFilePath, maxLogsize)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Log 直接记录日志的函数,带有时间戳
|
func logWorker() {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case msg := <-logChannel:
|
||||||
|
timestamp := time.Now().Format("02/Jan/2006:15:04:05 -0700")
|
||||||
|
logger.Println(timestamp + " - " + msg)
|
||||||
|
case <-quitChannel:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func Log(customMessage string) {
|
func Log(customMessage string) {
|
||||||
if logger != nil {
|
logChannel <- customMessage
|
||||||
timestamp := time.Now().Format("02/Jan/2006:15:04:05 -0700") // 使用自定义时间格式
|
|
||||||
logger.Println(timestamp + " - " + customMessage)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Logw 用于格式化日志记录
|
// 格式化日志记录
|
||||||
func Logw(format string, args ...interface{}) {
|
func Logw(format string, args ...interface{}) {
|
||||||
message := fmt.Sprintf(format, args...) // 格式化消息
|
message := fmt.Sprintf(format, args...)
|
||||||
Log(message) // 记录日志
|
Log(message)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close 关闭日志文件
|
// INFO
|
||||||
|
func LogInfo(format string, args ...interface{}) {
|
||||||
|
message := fmt.Sprintf(format, args...)
|
||||||
|
output := fmt.Sprintf("[INFO] %s", message)
|
||||||
|
Log(output)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WARNING
|
||||||
|
func LogWarning(format string, args ...interface{}) {
|
||||||
|
message := fmt.Sprintf(format, args...)
|
||||||
|
output := fmt.Sprintf("[WARNING] %s", message)
|
||||||
|
Log(output)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ERROR
|
||||||
|
func LogError(format string, args ...interface{}) {
|
||||||
|
message := fmt.Sprintf(format, args...)
|
||||||
|
Log(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭日志文件
|
||||||
func Close() {
|
func Close() {
|
||||||
|
logFileMutex.Lock()
|
||||||
|
defer logFileMutex.Unlock()
|
||||||
|
|
||||||
if logFile != nil {
|
if logFile != nil {
|
||||||
logFile.Close()
|
quitChannel <- struct{}{}
|
||||||
|
if err := logFile.Close(); err != nil {
|
||||||
|
fmt.Printf("Error closing log file: %v", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func monitorLogSize(logFilePath string, maxLogsize int) {
|
||||||
|
var maxLogsizeBytes int64 = int64(maxLogsize) * 1024 * 1024 // 最大日志文件大小,单位为MB
|
||||||
|
for {
|
||||||
|
time.Sleep(120 * time.Minute) // 每120分钟检查一次日志文件大小
|
||||||
|
logFileMutex.Lock()
|
||||||
|
info, err := logFile.Stat()
|
||||||
|
logFileMutex.Unlock()
|
||||||
|
|
||||||
|
if err == nil && info.Size() > maxLogsizeBytes {
|
||||||
|
if err := rotateLogFile(logFilePath); err != nil {
|
||||||
|
logw("Log Rotation Failed: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func rotateLogFile(logFilePath string) error {
|
||||||
|
logFileMutex.Lock()
|
||||||
|
defer logFileMutex.Unlock()
|
||||||
|
|
||||||
|
if logFile != nil {
|
||||||
|
if err := logFile.Close(); err != nil {
|
||||||
|
logw("Error closing log file for rotation: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 打开当前日志文件
|
||||||
|
logFile, err := os.Open(logFilePath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to open log file: %s, error: %w", logFilePath, err)
|
||||||
|
}
|
||||||
|
defer logFile.Close()
|
||||||
|
|
||||||
|
newLogFilePath := logFilePath + "-" + time.Now().Format("20060102-150405") + ".tar.gz"
|
||||||
|
outFile, err := os.Create(newLogFilePath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create gz file: %s, error: %w", newLogFilePath, err)
|
||||||
|
}
|
||||||
|
defer outFile.Close()
|
||||||
|
|
||||||
|
gzWriter, err := gzip.NewWriterLevel(outFile, gzip.BestCompression)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create gz writer: %w", err)
|
||||||
|
}
|
||||||
|
defer gzWriter.Close()
|
||||||
|
|
||||||
|
tarWriter := tar.NewWriter(gzWriter)
|
||||||
|
defer tarWriter.Close()
|
||||||
|
|
||||||
|
logFileStat, err := logFile.Stat()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to stat log file: %s, error: %w", logFilePath, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
logFileHeader := &tar.Header{
|
||||||
|
Name: filepath.Base(logFilePath),
|
||||||
|
Size: logFileStat.Size(),
|
||||||
|
Mode: 0644,
|
||||||
|
ModTime: logFileStat.ModTime(),
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := tarWriter.WriteHeader(logFileHeader); err != nil {
|
||||||
|
return fmt.Errorf("failed to write log file header: %s, error: %w", logFilePath, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := io.Copy(tarWriter, logFile); err != nil {
|
||||||
|
return fmt.Errorf("failed to copy log file: %s, error: %w", logFilePath, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.Truncate(logFilePath, 0); err != nil {
|
||||||
|
return fmt.Errorf("failed to truncate log file: %s, error: %w", logFilePath, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重新打开日志文件
|
||||||
|
logFile, err = os.OpenFile(logFilePath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to reopen log file: %s, error: %w", logFilePath, err)
|
||||||
|
}
|
||||||
|
logger.SetOutput(logFile)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
279
main.go
279
main.go
@@ -1,267 +1,114 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"regexp"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
|
"ghproxy/api"
|
||||||
|
"ghproxy/auth"
|
||||||
"ghproxy/config"
|
"ghproxy/config"
|
||||||
"ghproxy/logger"
|
"ghproxy/logger"
|
||||||
|
"ghproxy/proxy"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/imroc/req/v3"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var cfg *config.Config
|
|
||||||
var logw = logger.Logw
|
|
||||||
var router *gin.Engine
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
exps = []*regexp.Regexp{
|
cfg *config.Config
|
||||||
regexp.MustCompile(`^(?:https?://)?github\.com/([^/]+)/([^/]+)/(?:releases|archive)/.*`),
|
router *gin.Engine
|
||||||
regexp.MustCompile(`^(?:https?://)?github\.com/([^/]+)/([^/]+)/(?:blob|raw)/.*`),
|
configfile = "/data/ghproxy/config/config.toml"
|
||||||
regexp.MustCompile(`^(?:https?://)?github\.com/([^/]+)/([^/]+)/(?:info|git-).*`),
|
cfgfile string
|
||||||
regexp.MustCompile(`^(?:https?://)?raw\.github(?:usercontent|)\.com/([^/]+)/([^/]+)/.+?/.+`),
|
|
||||||
regexp.MustCompile(`^(?:https?://)?gist\.github\.com/([^/]+)/.+?/.+`),
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// 日志模块
|
||||||
|
var (
|
||||||
|
logw = logger.Logw
|
||||||
|
logInfo = logger.LogInfo
|
||||||
|
logWarning = logger.LogWarning
|
||||||
|
logError = logger.LogError
|
||||||
|
)
|
||||||
|
|
||||||
|
func readFlag() {
|
||||||
|
flag.StringVar(&cfgfile, "cfg", configfile, "config file path")
|
||||||
|
}
|
||||||
|
|
||||||
func loadConfig() {
|
func loadConfig() {
|
||||||
var err error
|
var err error
|
||||||
// 初始化配置
|
// 初始化配置
|
||||||
cfg, err = config.LoadConfig("/data/ghproxy/config/config.yaml")
|
cfg, err = config.LoadConfig(cfgfile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Failed to load config: %v", err)
|
log.Fatalf("Failed to load config: %v", err)
|
||||||
}
|
}
|
||||||
|
fmt.Println("Config File Path: ", cfgfile)
|
||||||
fmt.Printf("Loaded config: %v\n", cfg)
|
fmt.Printf("Loaded config: %v\n", cfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func setupLogger() {
|
func setupLogger(cfg *config.Config) {
|
||||||
// 初始化日志模块
|
// 初始化日志模块
|
||||||
var err error
|
var err error
|
||||||
err = logger.Init(cfg.LogFilePath) // 传递日志文件路径
|
err = logger.Init(cfg.Log.LogFilePath, cfg.Log.MaxLogSize) // 传递日志文件路径
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Failed to initialize logger: %v", err)
|
log.Fatalf("Failed to initialize logger: %v", err)
|
||||||
}
|
}
|
||||||
logw("Logger initialized")
|
logInfo("Config File Path: ", cfgfile)
|
||||||
logw("Init Completed")
|
logInfo("Loaded config: %v\n", cfg)
|
||||||
|
logInfo("Init Completed")
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadlist(cfg *config.Config) {
|
||||||
|
auth.Init(cfg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupApi(cfg *config.Config, router *gin.Engine) {
|
||||||
|
api.InitHandleRouter(cfg, router)
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
readFlag()
|
||||||
|
flag.Parse()
|
||||||
loadConfig()
|
loadConfig()
|
||||||
setupLogger()
|
setupLogger(cfg)
|
||||||
|
loadlist(cfg)
|
||||||
|
|
||||||
// 设置 Gin 模式
|
|
||||||
gin.SetMode(gin.ReleaseMode)
|
gin.SetMode(gin.ReleaseMode)
|
||||||
|
|
||||||
// 初始化路由
|
|
||||||
router = gin.Default()
|
router = gin.Default()
|
||||||
|
router.UseH2C = true
|
||||||
|
|
||||||
// 定义路由
|
setupApi(cfg, router)
|
||||||
router.GET("/", func(c *gin.Context) {
|
|
||||||
c.Redirect(http.StatusMovedPermanently, "https://ghproxy0rtt.1888866.xyz/")
|
|
||||||
})
|
|
||||||
|
|
||||||
router.GET("/api", api)
|
if cfg.Pages.Enabled {
|
||||||
|
indexPagePath := fmt.Sprintf("%s/index.html", cfg.Pages.StaticDir)
|
||||||
// 健康检查
|
faviconPath := fmt.Sprintf("%s/favicon.ico", cfg.Pages.StaticDir)
|
||||||
router.GET("/api/healthcheck", func(c *gin.Context) {
|
// 静态index页
|
||||||
c.String(http.StatusOK, "OK")
|
//router.StaticFile("/", indexPagePath)
|
||||||
})
|
router.GET("/", func(c *gin.Context) {
|
||||||
|
c.File(indexPagePath)
|
||||||
|
logInfo("IP:%s UA:%s METHOD:%s HTTPv:%s", c.ClientIP(), c.Request.UserAgent(), c.Request.Method, c.Request.Proto)
|
||||||
|
})
|
||||||
|
// 静态favicon.ico
|
||||||
|
router.StaticFile("/favicon.ico", faviconPath)
|
||||||
|
} else if !cfg.Pages.Enabled {
|
||||||
|
router.GET("/", func(c *gin.Context) {
|
||||||
|
c.String(http.StatusForbidden, "403 Forbidden This route is not allowed to access.")
|
||||||
|
logWarning("Forbidden: IP:%s UA:%s METHOD:%s HTTPv:%s", c.ClientIP(), c.Request.UserAgent(), c.Request.Method, c.Request.Proto)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// 未匹配路由处理
|
// 未匹配路由处理
|
||||||
router.NoRoute(noRouteHandler(cfg))
|
router.NoRoute(func(c *gin.Context) {
|
||||||
|
proxy.NoRouteHandler(cfg)(c)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
// 启动服务器
|
// 启动服务器
|
||||||
err := router.Run(fmt.Sprintf("%s:%d", cfg.Host, cfg.Port))
|
err := router.Run(fmt.Sprintf("%s:%d", cfg.Server.Host, cfg.Server.Port))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Error starting server: %v\n", err)
|
logError("Error starting server: %v\n", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("Program finished")
|
fmt.Println("Program finished")
|
||||||
}
|
}
|
||||||
|
|
||||||
func api(c *gin.Context) {
|
|
||||||
// 设置响应头
|
|
||||||
c.Writer.Header().Set("Content-Type", "application/json")
|
|
||||||
json.NewEncoder(c.Writer).Encode(map[string]interface{}{
|
|
||||||
"MaxResponseBodySize": cfg.SizeLimit,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func authHandler(c *gin.Context) bool {
|
|
||||||
if cfg.Auth {
|
|
||||||
authToken := c.Query("auth_token")
|
|
||||||
return authToken == cfg.AuthToken
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func noRouteHandler(config *config.Config) gin.HandlerFunc {
|
|
||||||
return func(c *gin.Context) {
|
|
||||||
rawPath := strings.TrimPrefix(c.Request.URL.RequestURI(), "/")
|
|
||||||
re := regexp.MustCompile(`^(http:|https:)?/?/?(.*)`)
|
|
||||||
matches := re.FindStringSubmatch(rawPath)
|
|
||||||
|
|
||||||
rawPath = "https://" + matches[2]
|
|
||||||
|
|
||||||
matches = checkURL(rawPath)
|
|
||||||
if matches == nil {
|
|
||||||
c.String(http.StatusForbidden, "Invalid input.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if exps[1].MatchString(rawPath) {
|
|
||||||
rawPath = strings.Replace(rawPath, "/blob/", "/raw/", 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !authHandler(c) {
|
|
||||||
c.AbortWithStatusJSON(401, gin.H{"error": "Unauthorized"})
|
|
||||||
logw("Unauthorized request: %s", rawPath)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 日志记录
|
|
||||||
logw("Request: %s %s", c.Request.Method, rawPath)
|
|
||||||
logw("Matches: %v", matches)
|
|
||||||
|
|
||||||
// 代理请求
|
|
||||||
switch {
|
|
||||||
case exps[0].MatchString(rawPath), exps[1].MatchString(rawPath), exps[3].MatchString(rawPath), exps[4].MatchString(rawPath):
|
|
||||||
logw("%s Matched - USE proxy-chrome", rawPath)
|
|
||||||
proxyRequest(c, rawPath, config, "chrome")
|
|
||||||
case exps[2].MatchString(rawPath):
|
|
||||||
logw("%s Matched - USE proxy-git", rawPath)
|
|
||||||
proxyRequest(c, rawPath, config, "git")
|
|
||||||
default:
|
|
||||||
c.String(http.StatusForbidden, "Invalid input.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func proxyRequest(c *gin.Context, u string, config *config.Config, mode string) {
|
|
||||||
method := c.Request.Method
|
|
||||||
logw("%s Method: %s", u, method)
|
|
||||||
|
|
||||||
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/128.0.0.0 Safari/537.36").
|
|
||||||
SetTLSFingerprintChrome().
|
|
||||||
ImpersonateChrome()
|
|
||||||
case "git":
|
|
||||||
client.SetUserAgent("git/2.33.1")
|
|
||||||
}
|
|
||||||
|
|
||||||
// 读取请求体
|
|
||||||
body, err := io.ReadAll(c.Request.Body)
|
|
||||||
if err != nil {
|
|
||||||
handleError(c, fmt.Sprintf("Failed to read request body: %v", err))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer c.Request.Body.Close()
|
|
||||||
|
|
||||||
// 创建新的请求
|
|
||||||
req := client.R().SetBody(body)
|
|
||||||
|
|
||||||
// 复制请求头
|
|
||||||
for key, values := range c.Request.Header {
|
|
||||||
for _, value := range values {
|
|
||||||
req.SetHeader(key, value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 发送请求并处理响应
|
|
||||||
resp, err := sendRequest(req, method, u)
|
|
||||||
if err != nil {
|
|
||||||
handleError(c, fmt.Sprintf("Failed to send request: %v", err))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
// 检查响应内容长度并处理重定向
|
|
||||||
if err := handleResponseSize(resp, config, c); err != nil {
|
|
||||||
logw("Error handling response size: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
copyResponseHeaders(resp, c, config)
|
|
||||||
c.Status(resp.StatusCode)
|
|
||||||
if _, err := io.Copy(c.Writer, resp.Body); err != nil {
|
|
||||||
logw("Failed to copy response body: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func sendRequest(req *req.Request, method, url string) (*req.Response, error) {
|
|
||||||
switch method {
|
|
||||||
case "GET":
|
|
||||||
return req.Get(url)
|
|
||||||
case "POST":
|
|
||||||
return req.Post(url)
|
|
||||||
case "PUT":
|
|
||||||
return req.Put(url)
|
|
||||||
case "DELETE":
|
|
||||||
return req.Delete(url)
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("unsupported method: %s", method)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func handleResponseSize(resp *req.Response, config *config.Config, c *gin.Context) error {
|
|
||||||
contentLength := resp.Header.Get("Content-Length")
|
|
||||||
if contentLength != "" {
|
|
||||||
size, err := strconv.Atoi(contentLength)
|
|
||||||
if err == nil && size > config.SizeLimit {
|
|
||||||
finalURL := resp.Request.URL.String()
|
|
||||||
c.Redirect(http.StatusMovedPermanently, finalURL)
|
|
||||||
logw("Redirecting to %s due to size limit (%d bytes)", finalURL, size)
|
|
||||||
return fmt.Errorf("response size exceeds limit")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func copyResponseHeaders(resp *req.Response, c *gin.Context, config *config.Config) {
|
|
||||||
headersToRemove := []string{"Content-Security-Policy", "Referrer-Policy", "Strict-Transport-Security"}
|
|
||||||
|
|
||||||
for _, header := range headersToRemove {
|
|
||||||
resp.Header.Del(header)
|
|
||||||
}
|
|
||||||
|
|
||||||
for key, values := range resp.Header {
|
|
||||||
for _, value := range values {
|
|
||||||
c.Header(key, value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if config.CORSOrigin {
|
|
||||||
c.Header("Access-Control-Allow-Origin", "*")
|
|
||||||
} else {
|
|
||||||
c.Header("Access-Control-Allow-Origin", "")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func handleError(c *gin.Context, message string) {
|
|
||||||
c.String(http.StatusInternalServerError, fmt.Sprintf("server error %v", message))
|
|
||||||
logw(message)
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkURL(u string) []string {
|
|
||||||
for _, exp := range exps {
|
|
||||||
if matches := exp.FindStringSubmatch(u); matches != nil {
|
|
||||||
logw("URL matched: %s, Matches: %v", u, matches[1:])
|
|
||||||
return matches[1:]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
logw("Invalid URL: %s", u)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|||||||
134
pages/index.html
134
pages/index.html
@@ -10,7 +10,7 @@
|
|||||||
<link rel="stylesheet" href="https://font.sec.miui.com/font/css?family=MiSans:400,700:MiSans">
|
<link rel="stylesheet" href="https://font.sec.miui.com/font/css?family=MiSans:400,700:MiSans">
|
||||||
<style>
|
<style>
|
||||||
body {
|
body {
|
||||||
background-color: #f8f9fac5;
|
background-color: #ecececf3;
|
||||||
font-family: 'Misans', Arial, sans-serif;
|
font-family: 'Misans', Arial, sans-serif;
|
||||||
padding: 30px;
|
padding: 30px;
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -30,15 +30,15 @@
|
|||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
margin-bottom: 75px;
|
margin-bottom: 85px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.rounded-button {
|
.rounded-button {
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
transition: background-color 0.3s, transform 0.2s;
|
transition: background-color 0.3s, transform 0.2s;
|
||||||
padding: 10px 30px;
|
padding: 10px 30px;
|
||||||
background-color: #39c5bb;
|
background-color: #555c5c;
|
||||||
color: white;
|
color: rgb(255, 255, 255);
|
||||||
border: none;
|
border: none;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
@@ -55,6 +55,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
footer {
|
footer {
|
||||||
|
line-height: 1.25;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
@@ -63,8 +64,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
pre {
|
pre {
|
||||||
background: #2d2d2d;
|
background: #012333;
|
||||||
color: #f8f8f2;
|
color: #39c5bc;
|
||||||
padding: 20px 20px;
|
padding: 20px 20px;
|
||||||
margin: 10px 0;
|
margin: 10px 0;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
@@ -80,17 +81,28 @@
|
|||||||
left: 10px;
|
left: 10px;
|
||||||
width: 12px;
|
width: 12px;
|
||||||
height: 12px;
|
height: 12px;
|
||||||
background: #ff5f56;
|
background: #bd3c35;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
box-shadow: 20px 0 0 #ffbd2e, 40px 0 0 #27c93f;
|
box-shadow: 20px 0 0 #d69f27, 40px 0 0 #39c5bb;
|
||||||
}
|
}
|
||||||
|
|
||||||
code {
|
code {
|
||||||
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
|
font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace;
|
||||||
font-size: 0.875em;
|
font-size: 1em;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.status-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-container p {
|
||||||
|
margin: 0px 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
.code {
|
.code {
|
||||||
position: relative;
|
position: relative;
|
||||||
padding-right: 0px;
|
padding-right: 0px;
|
||||||
@@ -100,7 +112,7 @@
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
top: 5px;
|
top: 5px;
|
||||||
right: 10px;
|
right: 10px;
|
||||||
background: rgba(118, 119, 121, 0.7);
|
background: rgba(0, 217, 224, 0.822);
|
||||||
color: white;
|
color: white;
|
||||||
border: none;
|
border: none;
|
||||||
padding: 5px 10px;
|
padding: 5px 10px;
|
||||||
@@ -117,7 +129,20 @@
|
|||||||
#visitor-info {
|
#visitor-info {
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
line-height: 0.5;
|
line-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#toast {
|
||||||
|
position: fixed;
|
||||||
|
top: 10%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
background-color: #39c5bcde;
|
||||||
|
color: white;
|
||||||
|
padding: 15px 20px;
|
||||||
|
border-radius: 10px;
|
||||||
|
font-size: 16px;
|
||||||
|
z-index: 1000;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
@@ -126,19 +151,27 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<h1>Github文件加速</h1>
|
<h1>Github文件加速</h1>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<input type="text" class="form-control" id="githubLinkInput" placeholder="键入Github链接">
|
<input type="text" class="form-control" id="githubLinkInput" placeholder="请键入需要代理Github链接">
|
||||||
</div>
|
</div>
|
||||||
<button class="btn rounded-button" id="formatButton">获取文件链接</button>
|
<button class="btn rounded-button" id="formatButton">获取文件链接</button>
|
||||||
|
|
||||||
<div class="code" id="outputBlock">
|
<div class="code" id="outputBlock">
|
||||||
<button class="copy-button" id="copyButton" onclick="copyCode(this)">Copy</button>
|
<button class="copy-button" id="copyButton">复制</button>
|
||||||
<pre id="formattedLinkOutput"></pre>
|
<pre id="formattedLinkOutput"></pre>
|
||||||
</div>
|
</div>
|
||||||
<div class="tips">
|
<div class="tips">
|
||||||
<p>GitHub链接带不带协议头均可,支持release、archive以及文件,转换后链接均可使用</a>。</p>
|
<p>GitHub链接带不带协议头均可,支持release、archive以及文件,转换后链接均可使用</a>。</p>
|
||||||
<p id="sizeLimitDisplay">文件大小限制: ...</p>
|
<div class="status-container">
|
||||||
|
<p id="sizeLimitDisplay">文件大小限制: ...</p>
|
||||||
|
<p id="whiteListStatus">白名单状态: ...</p>
|
||||||
|
<p id="blackListStatus">黑名单状态: ...</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="toast" style="display:none;">
|
||||||
|
链接已复制到剪贴板
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
function formatGithubLink() {
|
function formatGithubLink() {
|
||||||
@@ -155,7 +188,8 @@
|
|||||||
} else if (githubLinkInput.value.startsWith("raw.githubusercontent.com/")) {
|
} else if (githubLinkInput.value.startsWith("raw.githubusercontent.com/")) {
|
||||||
formattedLink = "https://" + currentHost + "/" + githubLinkInput.value;
|
formattedLink = "https://" + currentHost + "/" + githubLinkInput.value;
|
||||||
} else if (!githubLinkInput.value.trim()) {
|
} else if (!githubLinkInput.value.trim()) {
|
||||||
alert('请输入有效的GitHub链接');
|
//alert('请输入有效的GitHub链接');
|
||||||
|
showToast('请输入有效的GitHub链接');
|
||||||
}
|
}
|
||||||
var formattedLinkOutput = document.getElementById('formattedLinkOutput');
|
var formattedLinkOutput = document.getElementById('formattedLinkOutput');
|
||||||
formattedLinkOutput.textContent = formattedLink;
|
formattedLinkOutput.textContent = formattedLink;
|
||||||
@@ -170,33 +204,79 @@
|
|||||||
window.getSelection().addRange(range);
|
window.getSelection().addRange(range);
|
||||||
document.execCommand('copy');
|
document.execCommand('copy');
|
||||||
window.getSelection().removeAllRanges();
|
window.getSelection().removeAllRanges();
|
||||||
alert('链接已复制到剪贴板');
|
//alert('链接已复制到剪贴板');
|
||||||
|
showToast('链接已复制到剪贴板');
|
||||||
});
|
});
|
||||||
|
|
||||||
function fetchAPI() {
|
function showToast(message) {
|
||||||
fetch(window.location.origin + '/api')
|
const toast = document.getElementById('toast');
|
||||||
|
toast.textContent = message;
|
||||||
|
toast.style.display = 'block';
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
toast.style.display = 'none';
|
||||||
|
}, 3000); // 3秒后隐藏
|
||||||
|
}
|
||||||
|
|
||||||
|
function fetchSizeLimit() {
|
||||||
|
fetch(window.location.origin + '/api/size_limit')
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
const sizeLimitDisplay = document.getElementById('sizeLimitDisplay');
|
const sizeLimitDisplay = document.getElementById('sizeLimitDisplay');
|
||||||
const sizeInMB = (data.MaxResponseBodySize / (1024 * 1024)).toFixed(0);
|
sizeLimitDisplay.textContent = `文件大小限制: ${data.MaxResponseBodySize} MB`;
|
||||||
sizeLimitDisplay.textContent = `文件大小限制: ${sizeInMB} MB`;
|
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
console.error('Error fetching API:', error);
|
console.error('Error fetching API:', error);
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
function fetchWhiteList() {
|
||||||
|
fetch(window.location.origin + '/api/whitelist/status')
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
const whiteListStatus = document.getElementById('whiteListStatus');
|
||||||
|
if (data.Whitelist) {
|
||||||
|
whiteListStatus.textContent = `白名单状态: 已开启`;
|
||||||
|
} else if (!data.Whitelist) {
|
||||||
|
whiteListStatus.textContent = `白名单状态: 已关闭`;
|
||||||
|
} else {
|
||||||
|
whiteListStatus.textContent = `白名单状态: 未知`;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Error fetching API:', error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function fetchBlackList() {
|
||||||
|
fetch(window.location.origin + '/api/blacklist/status')
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
const blackListStatus = document.getElementById('blackListStatus');
|
||||||
|
if (data.Blacklist) {
|
||||||
|
blackListStatus.textContent = `黑名单状态: 已开启`;
|
||||||
|
} else if (!data.Blacklist) {
|
||||||
|
blackListStatus.textContent = `黑名单状态: 已关闭`;
|
||||||
|
} else {
|
||||||
|
blackListStatus.textContent = `黑名单状态: 未知`;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Error fetching API:', error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function fetchAPI() {
|
||||||
|
fetchSizeLimit();
|
||||||
|
fetchWhiteList();
|
||||||
|
fetchBlackList();
|
||||||
|
}
|
||||||
document.addEventListener('DOMContentLoaded', fetchAPI);
|
document.addEventListener('DOMContentLoaded', fetchAPI);
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
<footer>
|
<footer>
|
||||||
<p>
|
<p>
|
||||||
Copyright © 2024 WJQSERVER-STUDIO
|
Copyright © 2024 WJQSERVER-STUDIO<br>
|
||||||
</p>
|
GitHub仓库地址:<a href="https://github.com/WJQSERVER-STUDIO/ghproxy">https://github.com/WJQSERVER-STUDIO/ghproxy</a> <br><a href="https://t.me/ghproxy_go">Telegram交流群</a>
|
||||||
<p>
|
|
||||||
GitHub仓库地址:<a
|
|
||||||
href="https://github.com/WJQSERVER-STUDIO/ghproxy">https://github.com/WJQSERVER-STUDIO/ghproxy</a>
|
|
||||||
</p>
|
</p>
|
||||||
<div id="visitor-info" style="text-align: center; margin-top: 15px;">
|
<div id="visitor-info" style="text-align: center; margin-top: 15px;">
|
||||||
<p>您的IP地址: <span id="visitor-ip"></span></p>
|
<p>您的IP地址: <span id="visitor-ip"></span></p>
|
||||||
|
|||||||
284
proxy/proxy.go
Normal file
284
proxy/proxy.go
Normal file
@@ -0,0 +1,284 @@
|
|||||||
|
// proxy/proxy.go 实验性
|
||||||
|
package proxy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"ghproxy/auth"
|
||||||
|
"ghproxy/config"
|
||||||
|
"ghproxy/logger"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/imroc/req/v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 日志模块
|
||||||
|
var (
|
||||||
|
logw = logger.Logw
|
||||||
|
logInfo = logger.LogInfo
|
||||||
|
logWarning = logger.LogWarning
|
||||||
|
logError = logger.LogError
|
||||||
|
)
|
||||||
|
|
||||||
|
var exps = []*regexp.Regexp{
|
||||||
|
regexp.MustCompile(`^(?:https?://)?github\.com/([^/]+)/([^/]+)/(?:releases|archive)/.*`),
|
||||||
|
regexp.MustCompile(`^(?:https?://)?github\.com/([^/]+)/([^/]+)/(?:blob|raw)/.*`),
|
||||||
|
regexp.MustCompile(`^(?:https?://)?github\.com/([^/]+)/([^/]+)/(?:info|git-).*`),
|
||||||
|
regexp.MustCompile(`^(?:https?://)?raw\.github(?:usercontent|)\.com/([^/]+)/([^/]+)/.+?/.+`),
|
||||||
|
regexp.MustCompile(`^(?:https?://)?gist\.github\.com/([^/]+)/.+?/.+`),
|
||||||
|
}
|
||||||
|
|
||||||
|
func NoRouteHandler(cfg *config.Config) gin.HandlerFunc {
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
rawPath := strings.TrimPrefix(c.Request.URL.RequestURI(), "/")
|
||||||
|
re := regexp.MustCompile(`^(http:|https:)?/?/?(.*)`)
|
||||||
|
matches := re.FindStringSubmatch(rawPath)
|
||||||
|
|
||||||
|
if len(matches) < 3 {
|
||||||
|
logWarning("Invalid URL: %s", rawPath)
|
||||||
|
c.String(http.StatusForbidden, "Invalid URL.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
rawPath = "https://" + matches[2]
|
||||||
|
|
||||||
|
// 提取用户名和仓库名,格式为 handle/<username>/<repo>/*
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
username := pathParts[2]
|
||||||
|
repo := pathParts[3]
|
||||||
|
logWarning("Blacklist Check > Username: %s, Repo: %s", username, repo)
|
||||||
|
fullrepo := fmt.Sprintf("%s/%s", username, repo)
|
||||||
|
|
||||||
|
// 白名单检查
|
||||||
|
if cfg.Whitelist.Enabled {
|
||||||
|
whitelistpass := auth.CheckWhitelist(fullrepo)
|
||||||
|
if !whitelistpass {
|
||||||
|
errMsg := fmt.Sprintf("Whitelist Blocked repo: %s", fullrepo)
|
||||||
|
c.JSON(http.StatusForbidden, gin.H{"error": errMsg})
|
||||||
|
logWarning(errMsg)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 黑名单检查
|
||||||
|
if cfg.Blacklist.Enabled {
|
||||||
|
blacklistpass := auth.CheckBlacklist(fullrepo)
|
||||||
|
if blacklistpass {
|
||||||
|
errMsg := fmt.Sprintf("Blacklist Blocked repo: %s", fullrepo)
|
||||||
|
c.JSON(http.StatusForbidden, gin.H{"error": errMsg})
|
||||||
|
logWarning(errMsg)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
matches = CheckURL(rawPath)
|
||||||
|
if matches == nil {
|
||||||
|
c.AbortWithStatus(http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if exps[1].MatchString(rawPath) {
|
||||||
|
rawPath = strings.Replace(rawPath, "/blob/", "/raw/", 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !auth.AuthHandler(c, cfg) {
|
||||||
|
c.AbortWithStatusJSON(401, gin.H{"error": "Unauthorized"})
|
||||||
|
logWarning("Unauthorized request: %s", rawPath)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
logInfo("Matches: %v", matches)
|
||||||
|
|
||||||
|
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.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ProxyRequest(c *gin.Context, u string, cfg *config.Config, mode string) {
|
||||||
|
method := c.Request.Method
|
||||||
|
// 记录日志 IP 地址、请求方法、请求 URL、请求头 User-Agent 、HTTP版本
|
||||||
|
logInfo("%s %s %s %s %s", c.ClientIP(), method, u, c.Request.Header.Get("User-Agent"), c.Request.Proto)
|
||||||
|
|
||||||
|
client := createHTTPClient(mode)
|
||||||
|
|
||||||
|
body, err := readRequestBody(c)
|
||||||
|
if err != nil {
|
||||||
|
HandleError(c, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
req := client.R().SetBody(body)
|
||||||
|
setRequestHeaders(c, req)
|
||||||
|
|
||||||
|
resp, err := SendRequest(req, method, u)
|
||||||
|
if err != nil {
|
||||||
|
HandleError(c, fmt.Sprintf("Failed to send request: %v", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if err := HandleResponseSize(resp, cfg, c); err != nil {
|
||||||
|
logWarning("Error handling response size: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
CopyResponseHeaders(resp, c, cfg)
|
||||||
|
c.Status(resp.StatusCode)
|
||||||
|
if err := copyResponseBody(c, resp.Body); err != nil {
|
||||||
|
logError("Failed to copy response body: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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").
|
||||||
|
SetTLSFingerprintChrome().
|
||||||
|
ImpersonateChrome()
|
||||||
|
case "git":
|
||||||
|
client.SetUserAgent("git/2.33.1")
|
||||||
|
}
|
||||||
|
return client
|
||||||
|
}
|
||||||
|
|
||||||
|
// readRequestBody 读取请求体
|
||||||
|
func readRequestBody(c *gin.Context) ([]byte, error) {
|
||||||
|
body, err := io.ReadAll(c.Request.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to read request body: %v", err)
|
||||||
|
}
|
||||||
|
defer c.Request.Body.Close()
|
||||||
|
return body, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// setRequestHeaders 设置请求头
|
||||||
|
func setRequestHeaders(c *gin.Context, req *req.Request) {
|
||||||
|
for key, values := range c.Request.Header {
|
||||||
|
for _, value := range values {
|
||||||
|
req.SetHeader(key, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// copyResponseBody 复制响应体到客户端
|
||||||
|
func copyResponseBody(c *gin.Context, respBody io.Reader) error {
|
||||||
|
_, err := io.Copy(c.Writer, respBody)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func SendRequest(req *req.Request, method, url string) (*req.Response, error) {
|
||||||
|
switch method {
|
||||||
|
case "GET":
|
||||||
|
return req.Get(url)
|
||||||
|
case "POST":
|
||||||
|
return req.Post(url)
|
||||||
|
case "PUT":
|
||||||
|
return req.Put(url)
|
||||||
|
case "DELETE":
|
||||||
|
return req.Delete(url)
|
||||||
|
default:
|
||||||
|
logInfo("Unsupported method: %s", method)
|
||||||
|
return nil, fmt.Errorf("unsupported method: %s", method)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func HandleResponseSize(resp *req.Response, cfg *config.Config, c *gin.Context) error {
|
||||||
|
contentLength := resp.Header.Get("Content-Length")
|
||||||
|
if contentLength != "" {
|
||||||
|
size, err := strconv.Atoi(contentLength)
|
||||||
|
if err == nil && size > cfg.Server.SizeLimit {
|
||||||
|
finalURL := resp.Request.URL.String()
|
||||||
|
c.Redirect(http.StatusMovedPermanently, finalURL)
|
||||||
|
logWarning("Size limit exceeded: %s, Size: %d", finalURL, size)
|
||||||
|
return fmt.Errorf("size limit exceeded: %d", size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func CopyResponseHeaders(resp *req.Response, c *gin.Context, cfg *config.Config) {
|
||||||
|
|
||||||
|
copyHeaders(resp, c)
|
||||||
|
|
||||||
|
removeHeaders(resp)
|
||||||
|
|
||||||
|
setCORSHeaders(c, cfg)
|
||||||
|
|
||||||
|
setDefaultHeaders(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// removeHeaders 移除指定的响应头
|
||||||
|
func removeHeaders(resp *req.Response) {
|
||||||
|
headersToRemove := map[string]struct{}{
|
||||||
|
"Content-Security-Policy": {},
|
||||||
|
"Referrer-Policy": {},
|
||||||
|
"Strict-Transport-Security": {},
|
||||||
|
}
|
||||||
|
|
||||||
|
for header := range headersToRemove {
|
||||||
|
resp.Header.Del(header)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// copyHeaders 复制响应头到 Gin 上下文
|
||||||
|
func copyHeaders(resp *req.Response, c *gin.Context) {
|
||||||
|
for key, values := range resp.Header {
|
||||||
|
for _, value := range values {
|
||||||
|
c.Header(key, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// setCORSHeaders 设置 CORS 相关的响应头
|
||||||
|
func setCORSHeaders(c *gin.Context, cfg *config.Config) {
|
||||||
|
if cfg.CORS.Enabled {
|
||||||
|
c.Header("Access-Control-Allow-Origin", "*")
|
||||||
|
} else {
|
||||||
|
c.Header("Access-Control-Allow-Origin", "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// setDefaultHeaders 设置默认的响应头
|
||||||
|
func setDefaultHeaders(c *gin.Context) {
|
||||||
|
c.Header("Age", "10")
|
||||||
|
c.Header("Cache-Control", "max-age=300")
|
||||||
|
}
|
||||||
|
|
||||||
|
func HandleError(c *gin.Context, message string) {
|
||||||
|
c.String(http.StatusInternalServerError, fmt.Sprintf("server error %v", message))
|
||||||
|
logWarning(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
func CheckURL(u string) []string {
|
||||||
|
for _, exp := range exps {
|
||||||
|
if matches := exp.FindStringSubmatch(u); matches != nil {
|
||||||
|
logInfo("URL matched: %s, Matches: %v", u, matches[1:])
|
||||||
|
return matches[1:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
errMsg := fmt.Sprintf("Invalid URL: %s", u)
|
||||||
|
logWarning(errMsg)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user