package handlers import ( "crypto/sha256" "encoding/hex" "io" "net/http" "strconv" "strings" "gitea.loveuer.com/loveuer/cluster/internal/registry/storage" "github.com/gin-gonic/gin" ) // GetManifest 获取 manifest func GetManifest(store storage.Storage) gin.HandlerFunc { return func(c *gin.Context) { repo := c.GetString("repo_name") if repo == "" { repo = strings.TrimPrefix(c.Param("name"), "/") } reference := c.Param("reference") data, mediaType, err := store.GetManifest(repo, reference) if err != nil { c.JSON(http.StatusNotFound, gin.H{ "errors": []gin.H{ { "code": "MANIFEST_UNKNOWN", "message": err.Error(), }, }, }) return } c.Header("Content-Type", mediaType) c.Header("Content-Length", strconv.FormatInt(int64(len(data)), 10)) c.Header("Docker-Content-Digest", calculateDigest(data)) c.Data(http.StatusOK, mediaType, data) } } // PutManifest 推送 manifest func PutManifest(store storage.Storage) gin.HandlerFunc { return func(c *gin.Context) { repo := c.Param("name") reference := c.Param("reference") // 读取请求体 data, err := io.ReadAll(c.Request.Body) if err != nil { c.JSON(http.StatusBadRequest, gin.H{ "errors": []gin.H{ { "code": "BAD_REQUEST", "message": err.Error(), }, }, }) return } // 获取 Content-Type mediaType := c.GetHeader("Content-Type") if mediaType == "" { mediaType = "application/vnd.docker.distribution.manifest.v2+json" } // 存储 manifest if err := store.PutManifest(repo, reference, data, mediaType); err != nil { c.JSON(http.StatusInternalServerError, gin.H{ "errors": []gin.H{ { "code": "INTERNAL_ERROR", "message": err.Error(), }, }, }) return } // 返回 Location 和 Digest digest := calculateDigest(data) c.Header("Location", c.Request.URL.Path) c.Header("Docker-Content-Digest", digest) c.Status(http.StatusCreated) } } // DeleteManifest 删除 manifest func DeleteManifest(store storage.Storage) gin.HandlerFunc { return func(c *gin.Context) { repo := c.Param("name") reference := c.Param("reference") if err := store.DeleteManifest(repo, reference); err != nil { c.JSON(http.StatusNotFound, gin.H{ "errors": []gin.H{ { "code": "MANIFEST_UNKNOWN", "message": err.Error(), }, }, }) return } c.Status(http.StatusAccepted) } } // HeadManifest 检查 manifest 是否存在 func HeadManifest(store storage.Storage) gin.HandlerFunc { return func(c *gin.Context) { repo := c.GetString("repo_name") if repo == "" { repo = strings.TrimPrefix(c.Param("name"), "/") } reference := c.Param("reference") exists, err := store.ManifestExists(repo, reference) if err != nil || !exists { c.Status(http.StatusNotFound) return } // 获取 manifest 以设置正确的 headers data, mediaType, err := store.GetManifest(repo, reference) if err != nil { c.Status(http.StatusNotFound) return } c.Header("Content-Type", mediaType) c.Header("Content-Length", strconv.FormatInt(int64(len(data)), 10)) c.Header("Docker-Content-Digest", calculateDigest(data)) c.Status(http.StatusOK) } } // calculateDigest 计算 SHA256 digest func calculateDigest(data []byte) string { hash := sha256.Sum256(data) return "sha256:" + hex.EncodeToString(hash[:]) }