优化代码格式
This commit is contained in:
@@ -26,27 +26,27 @@ type SearchResult struct {
|
||||
|
||||
// Repository 仓库信息
|
||||
type Repository struct {
|
||||
Name string `json:"repo_name"`
|
||||
Description string `json:"short_description"`
|
||||
IsOfficial bool `json:"is_official"`
|
||||
IsAutomated bool `json:"is_automated"`
|
||||
StarCount int `json:"star_count"`
|
||||
PullCount int `json:"pull_count"`
|
||||
RepoOwner string `json:"repo_owner"`
|
||||
LastUpdated string `json:"last_updated"`
|
||||
Status int `json:"status"`
|
||||
Organization string `json:"affiliation"`
|
||||
PullsLastWeek int `json:"pulls_last_week"`
|
||||
Namespace string `json:"namespace"`
|
||||
Name string `json:"repo_name"`
|
||||
Description string `json:"short_description"`
|
||||
IsOfficial bool `json:"is_official"`
|
||||
IsAutomated bool `json:"is_automated"`
|
||||
StarCount int `json:"star_count"`
|
||||
PullCount int `json:"pull_count"`
|
||||
RepoOwner string `json:"repo_owner"`
|
||||
LastUpdated string `json:"last_updated"`
|
||||
Status int `json:"status"`
|
||||
Organization string `json:"affiliation"`
|
||||
PullsLastWeek int `json:"pulls_last_week"`
|
||||
Namespace string `json:"namespace"`
|
||||
}
|
||||
|
||||
// TagInfo 标签信息
|
||||
type TagInfo struct {
|
||||
Name string `json:"name"`
|
||||
FullSize int64 `json:"full_size"`
|
||||
LastUpdated time.Time `json:"last_updated"`
|
||||
LastPusher string `json:"last_pusher"`
|
||||
Images []Image `json:"images"`
|
||||
Name string `json:"name"`
|
||||
FullSize int64 `json:"full_size"`
|
||||
LastUpdated time.Time `json:"last_updated"`
|
||||
LastPusher string `json:"last_pusher"`
|
||||
Images []Image `json:"images"`
|
||||
Vulnerabilities struct {
|
||||
Critical int `json:"critical"`
|
||||
High int `json:"high"`
|
||||
@@ -79,15 +79,15 @@ type cacheEntry struct {
|
||||
}
|
||||
|
||||
const (
|
||||
maxCacheSize = 1000
|
||||
maxPaginationCache = 200
|
||||
cacheTTL = 30 * time.Minute
|
||||
maxCacheSize = 1000
|
||||
maxPaginationCache = 200
|
||||
cacheTTL = 30 * time.Minute
|
||||
)
|
||||
|
||||
type Cache struct {
|
||||
data map[string]cacheEntry
|
||||
mu sync.RWMutex
|
||||
maxSize int
|
||||
data map[string]cacheEntry
|
||||
mu sync.RWMutex
|
||||
maxSize int
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -101,18 +101,18 @@ func (c *Cache) Get(key string) (interface{}, bool) {
|
||||
c.mu.RLock()
|
||||
entry, exists := c.data[key]
|
||||
c.mu.RUnlock()
|
||||
|
||||
|
||||
if !exists {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
|
||||
if time.Now().After(entry.expiresAt) {
|
||||
c.mu.Lock()
|
||||
delete(c.data, key)
|
||||
c.mu.Unlock()
|
||||
return nil, false
|
||||
}
|
||||
|
||||
|
||||
return entry.data, true
|
||||
}
|
||||
|
||||
@@ -123,11 +123,11 @@ func (c *Cache) Set(key string, data interface{}) {
|
||||
func (c *Cache) SetWithTTL(key string, data interface{}, ttl time.Duration) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
|
||||
if len(c.data) >= c.maxSize {
|
||||
c.cleanupExpiredLocked()
|
||||
}
|
||||
|
||||
|
||||
c.data[key] = cacheEntry{
|
||||
data: data,
|
||||
expiresAt: time.Now().Add(ttl),
|
||||
@@ -153,7 +153,7 @@ func init() {
|
||||
go func() {
|
||||
ticker := time.NewTicker(5 * time.Minute)
|
||||
defer ticker.Stop()
|
||||
|
||||
|
||||
for range ticker.C {
|
||||
searchCache.Cleanup()
|
||||
}
|
||||
@@ -163,45 +163,45 @@ func init() {
|
||||
func filterSearchResults(results []Repository, query string) []Repository {
|
||||
searchTerm := strings.ToLower(strings.TrimPrefix(query, "library/"))
|
||||
filtered := make([]Repository, 0)
|
||||
|
||||
|
||||
for _, repo := range results {
|
||||
repoName := strings.ToLower(repo.Name)
|
||||
repoDesc := strings.ToLower(repo.Description)
|
||||
|
||||
|
||||
score := 0
|
||||
|
||||
|
||||
if repoName == searchTerm {
|
||||
score += 100
|
||||
}
|
||||
|
||||
|
||||
if strings.HasPrefix(repoName, searchTerm) {
|
||||
score += 50
|
||||
}
|
||||
|
||||
|
||||
if strings.Contains(repoName, searchTerm) {
|
||||
score += 30
|
||||
}
|
||||
|
||||
|
||||
if strings.Contains(repoDesc, searchTerm) {
|
||||
score += 10
|
||||
}
|
||||
|
||||
|
||||
if repo.IsOfficial {
|
||||
score += 20
|
||||
}
|
||||
|
||||
|
||||
if score > 0 {
|
||||
filtered = append(filtered, repo)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sort.Slice(filtered, func(i, j int) bool {
|
||||
if filtered[i].IsOfficial != filtered[j].IsOfficial {
|
||||
return filtered[i].IsOfficial
|
||||
}
|
||||
return filtered[i].PullCount > filtered[j].PullCount
|
||||
})
|
||||
|
||||
|
||||
return filtered
|
||||
}
|
||||
|
||||
@@ -216,7 +216,7 @@ func normalizeRepository(repo *Repository) {
|
||||
if repo.Namespace == "" && repo.RepoOwner != "" {
|
||||
repo.Namespace = repo.RepoOwner
|
||||
}
|
||||
|
||||
|
||||
if strings.Contains(repo.Name, "/") {
|
||||
parts := strings.Split(repo.Name, "/")
|
||||
if len(parts) > 1 {
|
||||
@@ -239,14 +239,14 @@ func searchDockerHubWithDepth(ctx context.Context, query string, page, pageSize
|
||||
return nil, fmt.Errorf("搜索请求过于复杂,请尝试更具体的关键词")
|
||||
}
|
||||
cacheKey := fmt.Sprintf("search:%s:%d:%d", query, page, pageSize)
|
||||
|
||||
|
||||
if cached, ok := searchCache.Get(cacheKey); ok {
|
||||
return cached.(*SearchResult), nil
|
||||
}
|
||||
|
||||
|
||||
isUserRepo := strings.Contains(query, "/")
|
||||
var namespace, repoName string
|
||||
|
||||
|
||||
if isUserRepo {
|
||||
parts := strings.Split(query, "/")
|
||||
if len(parts) == 2 {
|
||||
@@ -254,11 +254,11 @@ func searchDockerHubWithDepth(ctx context.Context, query string, page, pageSize
|
||||
repoName = parts[1]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
baseURL := "https://registry.hub.docker.com/v2"
|
||||
var fullURL string
|
||||
var params url.Values
|
||||
|
||||
|
||||
if isUserRepo && namespace != "" {
|
||||
fullURL = fmt.Sprintf("%s/repositories/%s/", baseURL, namespace)
|
||||
params = url.Values{
|
||||
@@ -273,20 +273,20 @@ func searchDockerHubWithDepth(ctx context.Context, query string, page, pageSize
|
||||
"page_size": {fmt.Sprintf("%d", pageSize)},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fullURL = fullURL + "?" + params.Encode()
|
||||
|
||||
|
||||
resp, err := utils.GetSearchHTTPClient().Get(fullURL)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("请求Docker Hub API失败: %v", err)
|
||||
}
|
||||
defer safeCloseResponseBody(resp.Body, "搜索响应体")
|
||||
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("读取响应失败: %v", err)
|
||||
}
|
||||
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
switch resp.StatusCode {
|
||||
case http.StatusTooManyRequests:
|
||||
@@ -302,7 +302,7 @@ func searchDockerHubWithDepth(ctx context.Context, query string, page, pageSize
|
||||
return nil, fmt.Errorf("请求失败: 状态码=%d, 响应=%s", resp.StatusCode, string(body))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var result *SearchResult
|
||||
if isUserRepo && namespace != "" {
|
||||
var userRepos struct {
|
||||
@@ -314,14 +314,14 @@ func searchDockerHubWithDepth(ctx context.Context, query string, page, pageSize
|
||||
if err := json.Unmarshal(body, &userRepos); err != nil {
|
||||
return nil, fmt.Errorf("解析响应失败: %v", err)
|
||||
}
|
||||
|
||||
|
||||
result = &SearchResult{
|
||||
Count: userRepos.Count,
|
||||
Next: userRepos.Next,
|
||||
Previous: userRepos.Previous,
|
||||
Results: make([]Repository, 0),
|
||||
}
|
||||
|
||||
|
||||
for _, repo := range userRepos.Results {
|
||||
if repoName == "" || strings.Contains(strings.ToLower(repo.Name), strings.ToLower(repoName)) {
|
||||
repo.Namespace = namespace
|
||||
@@ -329,22 +329,22 @@ func searchDockerHubWithDepth(ctx context.Context, query string, page, pageSize
|
||||
result.Results = append(result.Results, repo)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if len(result.Results) == 0 {
|
||||
return searchDockerHubWithDepth(ctx, repoName, page, pageSize, depth+1)
|
||||
}
|
||||
|
||||
|
||||
result.Count = len(result.Results)
|
||||
} else {
|
||||
result = &SearchResult{}
|
||||
if err := json.Unmarshal(body, &result); err != nil {
|
||||
return nil, fmt.Errorf("解析响应失败: %v", err)
|
||||
}
|
||||
|
||||
|
||||
for i := range result.Results {
|
||||
normalizeRepository(&result.Results[i])
|
||||
}
|
||||
|
||||
|
||||
if isUserRepo && namespace != "" {
|
||||
filteredResults := make([]Repository, 0)
|
||||
for _, repo := range result.Results {
|
||||
@@ -356,7 +356,7 @@ func searchDockerHubWithDepth(ctx context.Context, query string, page, pageSize
|
||||
result.Count = len(filteredResults)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
searchCache.Set(cacheKey, result)
|
||||
return result, nil
|
||||
}
|
||||
@@ -365,14 +365,14 @@ func isRetryableError(err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
if strings.Contains(err.Error(), "timeout") ||
|
||||
strings.Contains(err.Error(), "connection refused") ||
|
||||
strings.Contains(err.Error(), "no such host") ||
|
||||
strings.Contains(err.Error(), "too many requests") {
|
||||
strings.Contains(err.Error(), "connection refused") ||
|
||||
strings.Contains(err.Error(), "no such host") ||
|
||||
strings.Contains(err.Error(), "too many requests") {
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -423,7 +423,7 @@ func fetchTagPage(ctx context.Context, url string, maxRetries int) (*struct {
|
||||
Results []TagInfo `json:"results"`
|
||||
}, error) {
|
||||
var lastErr error
|
||||
|
||||
|
||||
for retry := 0; retry < maxRetries; retry++ {
|
||||
if retry > 0 {
|
||||
time.Sleep(time.Duration(retry) * 500 * time.Millisecond)
|
||||
@@ -442,7 +442,7 @@ func fetchTagPage(ctx context.Context, url string, maxRetries int) (*struct {
|
||||
defer safeCloseResponseBody(resp.Body, "标签响应体")
|
||||
return io.ReadAll(resp.Body)
|
||||
}()
|
||||
|
||||
|
||||
if err != nil {
|
||||
lastErr = err
|
||||
if retry < maxRetries-1 {
|
||||
@@ -478,21 +478,21 @@ func fetchTagPage(ctx context.Context, url string, maxRetries int) (*struct {
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
|
||||
return nil, lastErr
|
||||
}
|
||||
|
||||
func parsePaginationParams(c *gin.Context, defaultPageSize int) (page, pageSize int) {
|
||||
page = 1
|
||||
pageSize = defaultPageSize
|
||||
|
||||
|
||||
if p := c.Query("page"); p != "" {
|
||||
fmt.Sscanf(p, "%d", &page)
|
||||
}
|
||||
if ps := c.Query("page_size"); ps != "" {
|
||||
fmt.Sscanf(ps, "%d", &pageSize)
|
||||
}
|
||||
|
||||
|
||||
return page, pageSize
|
||||
}
|
||||
|
||||
@@ -556,4 +556,4 @@ func RegisterSearchRoute(r *gin.Engine) {
|
||||
c.JSON(http.StatusOK, tags)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user