Files
cluster/internal/module/registry/handler.list.go
loveuer 9780a2b028 feat: add registry config, image upload/download, and OCI format support
Backend:
- Add registry_address configuration API (GET/POST)
- Add tar image upload with OCI and Docker format support
- Add image download with streaming optimization
- Fix blob download using c.Send (Fiber v3 SendStream bug)
- Add registry_address prefix stripping for all OCI v2 endpoints
- Add AGENTS.md for project documentation

Frontend:
- Add settings store with Snackbar notifications
- Add image upload dialog with progress bar
- Add download state tracking with multi-stage feedback
- Replace alert() with MUI Snackbar messages
- Display image names without registry_address prefix

🤖 Generated with [Qoder](https://qoder.com)
2025-11-10 16:28:58 +08:00

85 lines
2.5 KiB
Go

package registry
import (
"context"
"gitea.loveuer.com/loveuer/cluster/internal/model"
"gitea.loveuer.com/loveuer/cluster/pkg/resp"
"gitea.loveuer.com/loveuer/cluster/pkg/store"
"github.com/gofiber/fiber/v3"
"gorm.io/gorm"
)
// RegistryImageList returns the list of images/repositories
func RegistryImageList(ctx context.Context, db *gorm.DB, store store.Store) fiber.Handler {
return func(c fiber.Ctx) error {
// Get current registry_address setting
var registryConfig model.RegistryConfig
registryAddress := ""
if err := db.Where("key = ?", "registry_address").First(&registryConfig).Error; err == nil {
registryAddress = registryConfig.Value
}
if registryAddress == "" {
registryAddress = "localhost:9119"
}
var repositories []model.Repository
// Query all repositories from the database
if err := db.Find(&repositories).Error; err != nil {
return resp.R500(c, "", nil, err)
}
// Convert to the expected format for the frontend
var result []map[string]interface{}
for _, repo := range repositories {
// Get all tags for this repository
var tags []model.Tag
if err := db.Where("repository = ?", repo.Name).Find(&tags).Error; err != nil {
continue // Skip this repository if we can't get tags
}
// If no tags, skip this repository
if len(tags) == 0 {
continue
}
// Calculate total size of all blobs for this repository
var totalSize int64
var sizeResult struct {
Total int64
}
err := db.Model(&model.Blob{}).
Where("repository = ?", repo.Name).
Select("COALESCE(SUM(size), 0) as total").
Scan(&sizeResult).Error
if err == nil {
totalSize = sizeResult.Total
}
// Format updated_at to second precision
uploadTime := repo.UpdatedAt.Format("2006-01-02 15:04:05")
// Create an entry for each tag with full image name
// Dynamically prepend registry_address to the repository name
for _, tag := range tags {
fullRepoName := registryAddress + "/" + repo.Name
fullImageName := fullRepoName + ":" + tag.Tag
repoMap := map[string]interface{}{
"id": repo.ID,
"name": fullImageName, // Full image name: registry_address/repo:tag
"repository": repo.Name, // Original repository name (without registry_address)
"tag": tag.Tag, // Tag name
"upload_time": uploadTime,
"size": totalSize,
}
result = append(result, repoMap)
}
}
return resp.R200(c, map[string]interface{}{
"images": result,
})
}
}