Files
cluster/internal/module/registry/registry.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

116 lines
2.4 KiB
Go

package registry
import (
"context"
"log"
"strings"
"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"
)
func Registry(ctx context.Context, db *gorm.DB, store store.Store) fiber.Handler {
if err := db.AutoMigrate(
&model.Repository{},
&model.Blob{},
&model.Manifest{},
&model.Tag{},
&model.BlobUpload{},
&model.RegistryConfig{},
); err != nil {
log.Fatalf("failed to migrate database: %v", err)
}
if err := store.CreatePartition(ctx, "registry"); err != nil {
log.Fatalf("failed to create registry partition: %v", err)
}
return func(c fiber.Ctx) error {
if isBlob(c) {
return HandleBlobs(c, db, store)
}
if isManifest(c) {
return HandleManifest(c, db, store)
}
if isTags(c) {
return HandleTags(c, db, store)
}
if isCatalog(c) {
return HandleCatalog(c, db, store)
}
if isReferrers(c) {
return HandleReferrers(c, db, store)
}
// Handle root v2 endpoint
if c.Path() == "/v2/" {
c.Set("Docker-Distribution-API-Version", "registry/2.0")
return c.SendStatus(200)
}
c.Set("Docker-Distribution-API-Version", "registry/2.0")
log.Printf("[Warn] Registry: unknown endpoint - path = %s, method = %s, headers = %v", c.Path(), c.Method(), &c.Request().Header)
return resp.R404(c, "UNKNOWN_ENDPOINT", nil, "endpoint not found")
}
}
func isBlob(c fiber.Ctx) bool {
elem := strings.Split(c.Path(), "/")
elem = elem[1:]
if elem[len(elem)-1] == "" {
elem = elem[:len(elem)-1]
}
if len(elem) < 3 {
return false
}
return elem[len(elem)-2] == "blobs" || (elem[len(elem)-3] == "blobs" &&
elem[len(elem)-2] == "uploads")
}
func isManifest(c fiber.Ctx) bool {
elems := strings.Split(c.Path(), "/")
elems = elems[1:]
if len(elems) < 4 {
return false
}
return elems[len(elems)-2] == "manifests"
}
func isTags(c fiber.Ctx) bool {
elems := strings.Split(c.Path(), "/")
elems = elems[1:]
if len(elems) < 4 {
return false
}
return elems[len(elems)-2] == "tags"
}
func isCatalog(c fiber.Ctx) bool {
elems := strings.Split(c.Path(), "/")
elems = elems[1:]
if len(elems) < 2 {
return false
}
return elems[len(elems)-1] == "_catalog"
}
func isReferrers(c fiber.Ctx) bool {
elems := strings.Split(c.Path(), "/")
elems = elems[1:]
if len(elems) < 4 {
return false
}
return elems[len(elems)-2] == "referrers"
}