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)
This commit is contained in:
loveuer
2025-11-10 16:28:58 +08:00
parent 29088a6b54
commit 9780a2b028
35 changed files with 3065 additions and 91 deletions

View File

@@ -13,6 +13,16 @@ import (
// 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
@@ -23,6 +33,17 @@ func RegistryImageList(ctx context.Context, db *gorm.DB, store store.Store) fibe
// 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 {
@@ -39,13 +60,21 @@ func RegistryImageList(ctx context.Context, db *gorm.DB, store store.Store) fibe
// Format updated_at to second precision
uploadTime := repo.UpdatedAt.Format("2006-01-02 15:04:05")
repoMap := map[string]interface{}{
"id": repo.ID,
"name": repo.Name,
"upload_time": uploadTime,
"size": totalSize,
// 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)
}
result = append(result, repoMap)
}
return resp.R200(c, map[string]interface{}{