20 Commits

Author SHA1 Message Date
wmgit
4053481714 🔧 config: Default config field for docker hub auth 2025-10-03 16:48:05 +08:00
wmgit
58097f865d 🔒 docker: Support basic auth for docker hub 2025-10-03 16:41:40 +08:00
starry
3f614e8011 Merge pull request #74 from eryajf/main
feat: 针对action流水线做了一些优化
2025-09-29 14:20:49 +08:00
eryajf
198a18508b refactor: 重构 Docker 构建流程,使用多阶段构建 2025-09-29 14:18:40 +08:00
eryajf
780ac14a8f feat: 优化构建流程,使用预编译二进制文件 2025-09-29 10:11:02 +08:00
eryajf
62b3cb6b70 feat: 添加 UPX 压缩二进制文件 2025-09-29 09:51:23 +08:00
starry
714224bd29 Update README.md 2025-09-17 02:05:46 +08:00
starry
7f6c46f0c8 add截图 2025-09-17 01:58:46 +08:00
starry
fd9b0cf829 add截图 2025-09-17 01:51:41 +08:00
starry
42ddfaab9d Update docker-compose.yml 2025-09-13 03:45:28 +08:00
starry
6144883a6e Update docker-compose.yml 2025-09-13 03:44:25 +08:00
starry
c704923b64 禁用CGO 2025-09-09 12:25:21 +08:00
starry
dcb502d3c8 v1.1.9 2025-09-08 00:02:51 +08:00
starry
a011d560c6 shell转换中确保host有协议头 2025-09-04 04:13:21 +08:00
starry
53060d50db update 2025-09-02 12:34:42 +08:00
starry
68868388d3 更新为v1.1.8 2025-09-02 10:33:41 +08:00
starry
75833b937b 放宽gist匹配限制 2025-09-02 10:06:32 +08:00
starry
45b4acc31f 调整一些默认配置 2025-09-02 01:03:50 +08:00
starry
0cd5a7334d 增加.ps1脚本的处理 2025-09-01 12:16:42 +08:00
starry
40f5b597ab 增加检查是否为网页类型 2025-09-01 12:05:16 +08:00
18 changed files with 130 additions and 53 deletions

5
.github/demo/deepwiki.svg vendored Normal file
View File

@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" width="90" height="28" role="img" aria-label="DeepWiki">
<title>DeepWiki</title>
<rect width="90" height="28" rx="4" fill="#0f766e"></rect>
<text x="45" y="19" fill="#fff" font-family="Arial, Helvetica, sans-serif" font-size="12" text-anchor="middle">DeepWiki</text>
</svg>

After

Width:  |  Height:  |  Size: 320 B

BIN
.github/demo/demo1.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

BIN
.github/demo/demo2.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

BIN
.github/demo/demo3.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

View File

@@ -23,7 +23,8 @@ jobs:
- name: 设置Go环境
uses: actions/setup-go@v5
with:
go-version: '1.25'
go-version-file: "src/go.mod"
cache-dependency-path: "src/go.sum"
- name: 获取版本号
id: version
@@ -53,15 +54,24 @@ jobs:
run: |
mkdir -p build/hubproxy
- name: 安装 UPX
uses: crazy-max/ghaction-upx@v3
with:
install-only: true
- name: 编译二进制文件
run: |
cd src
# Linux AMD64
GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o ../build/hubproxy/hubproxy-linux-amd64 .
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o ../build/hubproxy/hubproxy-linux-amd64 .
# Linux ARM64
GOOS=linux GOARCH=arm64 go build -ldflags="-s -w" -o ../build/hubproxy/hubproxy-linux-arm64 .
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -ldflags="-s -w" -o ../build/hubproxy/hubproxy-linux-arm64 .
# 压缩二进制文件
upx -9 ../build/hubproxy/hubproxy-linux-amd64
upx -9 ../build/hubproxy/hubproxy-linux-arm64
- name: 复制配置文件
run: |
@@ -125,4 +135,4 @@ jobs:
build/checksums.txt
draft: false
prerelease: false
token: ${{ secrets.GITHUB_TOKEN }}
token: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -4,11 +4,11 @@ ARG TARGETARCH
WORKDIR /app
COPY src/go.mod src/go.sum ./
RUN go mod download
RUN go mod download && apk add upx
COPY src/ .
RUN CGO_ENABLED=0 GOOS=linux GOARCH=${TARGETARCH} go build -ldflags="-s -w" -trimpath -o hubproxy .
RUN CGO_ENABLED=0 GOOS=linux GOARCH=${TARGETARCH} go build -ldflags="-s -w" -trimpath -o hubproxy . && upx -9 hubproxy
FROM alpine

View File

@@ -2,8 +2,15 @@
🚀 **Docker 和 GitHub 加速代理服务器**
<p align="center">
<a href="https://deepwiki.com/sky22333/hubproxy">
<img src="./.github/demo/deepwiki.svg" alt="DeepWiki">
</a>
</p>
一个轻量级、高性能的多功能代理服务,提供 Docker 镜像加速、GitHub 文件加速、下载离线镜像、在线搜索 Docker 镜像等功能。
<p align="center">
<img src="https://count.getloli.com/get/@sky22333.hubproxy?theme=rule34" alt="Visitors">
</p>
@@ -42,7 +49,7 @@ docker run -d \
curl -fsSL https://raw.githubusercontent.com/sky22333/hubproxy/main/install.sh | sudo bash
```
也可以直接下载二进制文件执行`./hubproxy`使用,无需配置文件即可启动,内置默认配置,支持所有功能。
支持单个二进制文件直接启动,无需其他配置,内置默认配置,支持所有功能。
这个脚本会:
- 🔍 自动检测系统架构AMD64/ARM64
@@ -243,6 +250,15 @@ example.com {
</div>
## 界面预览
![1](./.github/demo/demo1.jpg)
![2](./.github/demo/demo2.jpg)
![3](./.github/demo/demo3.jpg)
## Star 趋势
[![Star 趋势](https://starchart.cc/sky22333/hubproxy.svg?variant=adaptive)](https://starchart.cc/sky22333/hubproxy)

View File

@@ -1,8 +1,14 @@
services:
hubproxy:
build: .
image: ghcr.io/sky22333/hubproxy
container_name: hubproxy
restart: always
ports:
- '5000:5000'
- "5000:5000"
volumes:
- ./src/config.toml:/root/config.toml
- ./src/config.toml:/root/config.toml
logging:
driver: json-file
options:
max-size: "1g"
max-file: "2"

View File

@@ -46,19 +46,24 @@ blackList = [
# 无认证: socks5://127.0.0.1:1080
# 有认证: socks5://username:password@127.0.0.1:1080
# 留空不使用代理
proxy = ""
proxy = ""
[download]
# 批量下载离线镜像数量限制
maxImages = 10
# Docker Hub 认证信息,留空则匿名拉取
[dockerHubAuth]
username = "" # e.g., user1
token = "" # e.g., dckr_pat_***
# Registry映射配置支持多种镜像仓库上游
[registries]
# GitHub Container Registry
[registries."ghcr.io"]
upstream = "ghcr.io"
authHost = "ghcr.io/token"
authHost = "ghcr.io/token"
authType = "github"
enabled = true

View File

@@ -48,6 +48,11 @@ type AppConfig struct {
MaxImages int `toml:"maxImages"`
} `toml:"download"`
DockerHubAuth struct {
Username string `toml:"username"`
Token string `toml:"token"`
} `toml:"dockerHubAuth"`
Registries map[string]RegistryMapping `toml:"registries"`
TokenCache struct {
@@ -84,8 +89,8 @@ func DefaultConfig() *AppConfig {
RequestLimit int `toml:"requestLimit"`
PeriodHours float64 `toml:"periodHours"`
}{
RequestLimit: 200,
PeriodHours: 1.0,
RequestLimit: 500,
PeriodHours: 3.0,
},
Security: struct {
WhiteList []string `toml:"whiteList"`
@@ -108,6 +113,13 @@ func DefaultConfig() *AppConfig {
}{
MaxImages: 10,
},
DockerHubAuth: struct {
Username string `toml:"username"`
Token string `toml:"token"`
}{
Username: "",
Token: "",
},
Registries: map[string]RegistryMapping{
"ghcr.io": {
Upstream: "ghcr.io",

View File

@@ -68,10 +68,18 @@ func InitDockerProxy() {
}
options := []remote.Option{
remote.WithAuth(authn.Anonymous),
remote.WithUserAgent("hubproxy/go-containerregistry"),
remote.WithTransport(utils.GetGlobalHTTPClient().Transport),
}
dockerHubAuth := config.GetConfig().DockerHubAuth
if dockerHubAuth.Token != "" && dockerHubAuth.Username != "" {
options = append(options, remote.WithAuth(&authn.Basic{
Username: dockerHubAuth.Username,
Password: dockerHubAuth.Token,
}))
} else {
options = append(options, remote.WithAuth(authn.Anonymous))
}
dockerProxy = &DockerProxy{
registry: registry,

View File

@@ -20,7 +20,7 @@ var (
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\.(?:githubusercontent|github)\.com/(.+?)/(.+?)/.+\.[a-zA-Z0-9]+$`),
regexp.MustCompile(`^(?:https?://)?gist\.(?:githubusercontent|github)\.com/([^/]+)/([^/]+).*`),
regexp.MustCompile(`^(?:https?://)?api\.github\.com/repos/([^/]+)/([^/]+)/.*`),
regexp.MustCompile(`^(?:https?://)?huggingface\.co(?:/spaces)?/([^/]+)/(.+)`),
regexp.MustCompile(`^(?:https?://)?cdn-lfs\.hf\.co(?:/spaces)?/([^/]+)/([^/]+)(?:/(.*))?`),
@@ -29,6 +29,14 @@ var (
}
)
// 全局变量:被阻止的内容类型
var blockedContentTypes = map[string]bool{
"text/html": true,
"application/xhtml+xml": true,
"text/xml": true,
"application/xml": true,
}
// GitHubProxyHandler GitHub代理处理器
func GitHubProxyHandler(c *gin.Context) {
rawPath := strings.TrimPrefix(c.Request.URL.RequestURI(), "/")
@@ -121,11 +129,16 @@ func proxyGitHubWithRedirect(c *gin.Context, u string, redirectCount int) {
fmt.Printf("关闭响应体失败: %v\n", err)
}
}()
// 如果Github上游404则返回错误信息
if resp.StatusCode == http.StatusNotFound {
c.String(http.StatusForbidden, "无效的GitHub地址")
return
// 检查并处理被阻止的内容类型
if c.Request.Method == "GET" {
if contentType := resp.Header.Get("Content-Type"); blockedContentTypes[strings.ToLower(strings.Split(contentType, ";")[0])] {
c.JSON(http.StatusForbidden, map[string]string{
"error": "Content type not allowed",
"message": "检测到网页类型,本服务不支持加速网页,请检查您的链接是否正确。",
})
return
}
}
// 检查文件大小限制
@@ -152,8 +165,8 @@ func proxyGitHubWithRedirect(c *gin.Context, u string, redirectCount int) {
realHost = "https://" + realHost
}
// 处理.sh文件的智能处理
if strings.HasSuffix(strings.ToLower(u), ".sh") {
// 处理.sh和.ps1文件的智能处理
if strings.HasSuffix(strings.ToLower(u), ".sh") || strings.HasSuffix(strings.ToLower(u), ".ps1") {
isGzipCompressed := resp.Header.Get("Content-Encoding") == "gzip"
processedBody, processedSize, err := utils.ProcessSmart(resp.Body, isGzipCompressed, realHost)

View File

@@ -125,7 +125,7 @@ func main() {
fmt.Printf("H2c: 已启用\n")
}
fmt.Printf("版本号: v1.1.7\n")
fmt.Printf("版本号: v1.1.9\n")
fmt.Printf("项目地址: https://github.com/sky22333/hubproxy\n")
// 创建HTTP2服务器

View File

@@ -1,13 +1,13 @@
<!DOCTYPE html>
<html lang="zh">
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="Docker镜像流式下载工具,即点即下,无需等待">
<meta name="keywords" content="Docker,镜像下载,流式下载,即时下载">
<meta name="description" content="Docker镜像流式下载工具即点即下无需等待">
<meta name="keywords" content="Docker镜像下载流式下载即时下载">
<meta name="color-scheme" content="dark light">
<title>Docker离线镜像下载</title>
<link rel="icon" href="./favicon.ico">
<link rel="icon" href="/favicon.ico">
<style>
:root {
--background: #ffffff;

15
src/public/index.html vendored
View File

@@ -1,14 +1,13 @@
<!DOCTYPE html>
<html lang="zh">
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="Github文件加速,docker镜像加速">
<meta name="keywords" content="Github,文件加速,ghproxy,docker镜像加速">
<meta name="description" content="Github文件加速docker镜像加速">
<meta name="keywords" content="Github文件加速ghproxydocker镜像加速">
<meta name="color-scheme" content="dark light">
<title>Github文件加速</title>
<link rel="icon" href="./favicon.ico">
<title>Github、Docker加速</title>
<link rel="icon" href="/favicon.ico">
<style>
:root {
--background: #ffffff;
@@ -602,7 +601,7 @@
<div class="hero">
<h1 class="hero-title">GitHub 文件加速</h1>
<p class="hero-subtitle">
快速下载GitHub上的文件和仓库解决国内访问GitHub速度慢的问题支持AI模型库Hugging Face
快速下载GitHub上的文件和仓库解决国内访问GitHub速度慢的问题支持Docker镜像加速和Hugging Face仓库。
</p>
</div>
@@ -685,7 +684,7 @@
<strong>Quay.io 镜像:</strong>
docker pull <span class="domain-base"></span>/quay.io/org/image
<strong>K8s 镜像:</strong>
<strong>Kubernetes 镜像:</strong>
docker pull <span class="domain-base"></span>/registry.k8s.io/pause:3.8
</div>
</div>

View File

@@ -1,13 +1,13 @@
<!DOCTYPE html>
<html lang="zh">
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="Docker镜像搜索">
<meta name="keywords" content="Docker,镜像搜索,docker search">
<meta name="keywords" content="Docker镜像搜索docker search">
<meta name="color-scheme" content="dark light">
<title>Docker镜像搜索</title>
<link rel="icon" href="./favicon.ico">
<link rel="icon" href="/favicon.ico">
<style>
:root {
--background: #ffffff;

View File

@@ -77,18 +77,21 @@ func processGitHubURLs(content, host string) string {
// transformURL URL转换函数
func transformURL(url, host string) string {
if strings.Contains(url, host) {
return url
}
if strings.Contains(url, host) {
return url
}
if strings.HasPrefix(url, "http://") {
url = "https" + url[4:]
} else if !strings.HasPrefix(url, "https://") && !strings.HasPrefix(url, "//") {
url = "https://" + url
}
cleanHost := strings.TrimPrefix(host, "https://")
cleanHost = strings.TrimPrefix(cleanHost, "http://")
cleanHost = strings.TrimSuffix(cleanHost, "/")
if strings.HasPrefix(url, "http://") {
url = "https" + url[4:]
} else if !strings.HasPrefix(url, "https://") && !strings.HasPrefix(url, "//") {
url = "https://" + url
}
return cleanHost + "/" + url
// 确保 host 有协议头
if !strings.HasPrefix(host, "http://") && !strings.HasPrefix(host, "https://") {
host = "https://" + host
}
host = strings.TrimSuffix(host, "/")
return host + "/" + url
}

View File

@@ -13,7 +13,7 @@ import (
)
const (
CleanupInterval = 10 * time.Minute
CleanupInterval = 20 * time.Minute
MaxIPCacheSize = 10000
)
@@ -98,7 +98,7 @@ func (i *IPRateLimiter) cleanupRoutine() {
i.mu.RLock()
for ip, entry := range i.ips {
if now.Sub(entry.lastAccess) > 1*time.Hour {
if now.Sub(entry.lastAccess) > 2*time.Hour {
expired = append(expired, ip)
}
}