This commit is contained in:
beck-8
2025-06-19 22:53:20 +08:00
parent 8c127a795b
commit 5bd32cd6c1
8 changed files with 2226 additions and 2232 deletions

View File

@@ -385,12 +385,11 @@ func (is *ImageStreamer) streamDockerFormatWithReturn(ctx context.Context, tarWr
log.Printf("已处理层 %d/%d", i+1, len(layers)) log.Printf("已处理层 %d/%d", i+1, len(layers))
} }
// 构建单个镜像的manifest信息 // 构建单个镜像的manifest信息
singleManifest := map[string]interface{}{ singleManifest := map[string]interface{}{
"Config": configDigest.String() + ".json", "Config": configDigest.String() + ".json",
"RepoTags": []string{imageRef}, "RepoTags": []string{imageRef},
"Layers": func() []string { "Layers": func() []string {
var layers []string var layers []string
for _, digest := range layerDigests { for _, digest := range layerDigests {
layers = append(layers, digest+"/layer.tar") layers = append(layers, digest+"/layer.tar")
@@ -549,8 +548,8 @@ func (is *ImageStreamer) selectPlatformImage(desc *remote.Descriptor, options *S
} }
if m.Platform.OS == targetOS && if m.Platform.OS == targetOS &&
m.Platform.Architecture == targetArch && m.Platform.Architecture == targetArch &&
m.Platform.Variant == targetVariant { m.Platform.Variant == targetVariant {
selectedDesc = &m selectedDesc = &m
break break
} }
@@ -632,7 +631,7 @@ func handleDirectImageDownload(c *gin.Context) {
if !singleImageDebouncer.ShouldAllow(userID, contentKey) { if !singleImageDebouncer.ShouldAllow(userID, contentKey) {
c.JSON(http.StatusTooManyRequests, gin.H{ c.JSON(http.StatusTooManyRequests, gin.H{
"error": "请求过于频繁,请稍后再试", "error": "请求过于频繁,请稍后再试",
"retry_after": 5, "retry_after": 5,
}) })
return return
@@ -692,7 +691,7 @@ func handleSimpleBatchDownload(c *gin.Context) {
if !batchImageDebouncer.ShouldAllow(userID, contentKey) { if !batchImageDebouncer.ShouldAllow(userID, contentKey) {
c.JSON(http.StatusTooManyRequests, gin.H{ c.JSON(http.StatusTooManyRequests, gin.H{
"error": "批量下载请求过于频繁,请稍后再试", "error": "批量下载请求过于频繁,请稍后再试",
"retry_after": 60, "retry_after": 60,
}) })
return return

View File

@@ -122,7 +122,6 @@ func main() {
// 注册Docker Registry代理路由 // 注册Docker Registry代理路由
router.Any("/v2/*path", ProxyDockerRegistryGin) router.Any("/v2/*path", ProxyDockerRegistryGin)
// 注册NoRoute处理器 // 注册NoRoute处理器
router.NoRoute(handler) router.NoRoute(handler)
@@ -177,12 +176,10 @@ func handler(c *gin.Context) {
proxyRequest(c, rawPath) proxyRequest(c, rawPath)
} }
func proxyRequest(c *gin.Context, u string) { func proxyRequest(c *gin.Context, u string) {
proxyWithRedirect(c, u, 0) proxyWithRedirect(c, u, 0)
} }
func proxyWithRedirect(c *gin.Context, u string, redirectCount int) { func proxyWithRedirect(c *gin.Context, u string, redirectCount int) {
// 限制最大重定向次数,防止无限递归 // 限制最大重定向次数,防止无限递归
const maxRedirects = 20 const maxRedirects = 20

View File

@@ -14,7 +14,7 @@ import (
const ( const (
// 清理间隔 // 清理间隔
CleanupInterval = 10 * time.Minute CleanupInterval = 10 * time.Minute
MaxIPCacheSize = 10000 MaxIPCacheSize = 10000
) )
// IPRateLimiter IP限流器结构体 // IPRateLimiter IP限流器结构体
@@ -233,7 +233,7 @@ func RateLimitMiddleware(limiter *IPRateLimiter) gin.HandlerFunc {
// 静态文件豁免:跳过限流检查 // 静态文件豁免:跳过限流检查
path := c.Request.URL.Path path := c.Request.URL.Path
if path == "/" || path == "/favicon.ico" || path == "/images.html" || path == "/search.html" || if path == "/" || path == "/favicon.ico" || path == "/images.html" || path == "/search.html" ||
strings.HasPrefix(path, "/public/") { strings.HasPrefix(path, "/public/") {
c.Next() c.Next()
return return
} }
@@ -299,5 +299,3 @@ func RateLimitMiddleware(limiter *IPRateLimiter) gin.HandlerFunc {
c.Next() c.Next()
} }
} }

View File

@@ -25,27 +25,27 @@ type SearchResult struct {
// Repository 仓库信息 // Repository 仓库信息
type Repository struct { type Repository struct {
Name string `json:"repo_name"` Name string `json:"repo_name"`
Description string `json:"short_description"` Description string `json:"short_description"`
IsOfficial bool `json:"is_official"` IsOfficial bool `json:"is_official"`
IsAutomated bool `json:"is_automated"` IsAutomated bool `json:"is_automated"`
StarCount int `json:"star_count"` StarCount int `json:"star_count"`
PullCount int `json:"pull_count"` PullCount int `json:"pull_count"`
RepoOwner string `json:"repo_owner"` RepoOwner string `json:"repo_owner"`
LastUpdated string `json:"last_updated"` LastUpdated string `json:"last_updated"`
Status int `json:"status"` Status int `json:"status"`
Organization string `json:"affiliation"` Organization string `json:"affiliation"`
PullsLastWeek int `json:"pulls_last_week"` PullsLastWeek int `json:"pulls_last_week"`
Namespace string `json:"namespace"` Namespace string `json:"namespace"`
} }
// TagInfo 标签信息 // TagInfo 标签信息
type TagInfo struct { type TagInfo struct {
Name string `json:"name"` Name string `json:"name"`
FullSize int64 `json:"full_size"` FullSize int64 `json:"full_size"`
LastUpdated time.Time `json:"last_updated"` LastUpdated time.Time `json:"last_updated"`
LastPusher string `json:"last_pusher"` LastPusher string `json:"last_pusher"`
Images []Image `json:"images"` Images []Image `json:"images"`
Vulnerabilities struct { Vulnerabilities struct {
Critical int `json:"critical"` Critical int `json:"critical"`
High int `json:"high"` High int `json:"high"`
@@ -77,9 +77,9 @@ const (
) )
type Cache struct { type Cache struct {
data map[string]cacheEntry data map[string]cacheEntry
mu sync.RWMutex mu sync.RWMutex
maxSize int maxSize int
} }
var ( var (
@@ -385,9 +385,9 @@ func isRetryableError(err error) bool {
// 网络错误、超时等可以重试 // 网络错误、超时等可以重试
if strings.Contains(err.Error(), "timeout") || if strings.Contains(err.Error(), "timeout") ||
strings.Contains(err.Error(), "connection refused") || strings.Contains(err.Error(), "connection refused") ||
strings.Contains(err.Error(), "no such host") || strings.Contains(err.Error(), "no such host") ||
strings.Contains(err.Error(), "too many requests") { strings.Contains(err.Error(), "too many requests") {
return true return true
} }

View File

@@ -13,10 +13,10 @@ import (
// CachedItem 通用缓存项支持Token和Manifest // CachedItem 通用缓存项支持Token和Manifest
type CachedItem struct { type CachedItem struct {
Data []byte // 缓存数据(token字符串或manifest字节) Data []byte // 缓存数据(token字符串或manifest字节)
ContentType string // 内容类型 ContentType string // 内容类型
Headers map[string]string // 额外的响应头 Headers map[string]string // 额外的响应头
ExpiresAt time.Time // 过期时间 ExpiresAt time.Time // 过期时间
} }
// UniversalCache 通用缓存支持Token和Manifest // UniversalCache 通用缓存支持Token和Manifest
@@ -86,7 +86,7 @@ func getManifestTTL(reference string) time.Duration {
// mutable tag的智能判断 // mutable tag的智能判断
if reference == "latest" || reference == "main" || reference == "master" || if reference == "latest" || reference == "main" || reference == "master" ||
reference == "dev" || reference == "develop" { reference == "dev" || reference == "develop" {
// 热门可变标签: 短期缓存 // 热门可变标签: 短期缓存
return 10 * time.Minute return 10 * time.Minute
} }