/* * Copyright 2026 Oleg Borodin * * This work is published and licensed under a Creative Commons * Attribution-NonCommercial-NoDerivatives 4.0 International License. * * Distribution of this work is permitted, but commercial use and * modifications are strictly prohibited. */ package handler import ( "io" "net/http" "mstore/app/operator" "mstore/app/router" "mstore/pkg/terms" ) // HEAD /v2//blobs/ 200 404 func (hand *Handler) BlobExists(rctx *router.Context) { name, _ := rctx.GetSubpath("name") digest, _ := rctx.GetSubpath("digest") //hand.DumpHeaders("BlobExists", rctx) params := &operator.BlobExistsParams{ Name: name, Digest: digest, } // Rigth checking operatorID, _ := rctx.GetString(userTag) opEnable, err := hand.CheckRight(rctx.Ctx, operatorID, terms.RightReadImages, "") if err != nil { rctx.SetStatus(http.StatusInternalServerError) return } if !opEnable { rctx.SetStatus(http.StatusMethodNotAllowed) return } // Execution of the operation ctx := rctx.GetContext() res, code, err := hand.oper.BlobExists(ctx, params) if err != nil { hand.logg.Errorf("BlobExist error: %v", err) } if code == http.StatusOK { rctx.SetHeader("Docker-Content-Digest", res.DockerContentDigest) rctx.SetHeader("Content-Length", res.ContentLength) rctx.SetHeader("Content-Type", res.ContentType) } rctx.SetStatus(code) } // POST /v2//blobs/uploads/ 202 404 func (hand *Handler) PostUpload(rctx *router.Context) { name, _ := rctx.GetSubpath("name") digest := rctx.GetQuery("digest") mount := rctx.GetQuery("mount") from := rctx.GetQuery("from") params := &operator.PostUploadParams{ Name: name, Digest: digest, Mount: mount, From: from, } // Rigth checking operatorID, _ := rctx.GetString(userTag) opEnable, err := hand.CheckRight(rctx.Ctx, operatorID, terms.RightWriteImages, "") if err != nil { rctx.SetStatus(http.StatusInternalServerError) return } if !opEnable { rctx.SetStatus(http.StatusMethodNotAllowed) return } // Execution of the operation res, code, err := hand.oper.PostUpload(rctx.Ctx, params) if err != nil { hand.logg.Errorf("PostUpload error: %v", err) } else { rctx.SetHeader("Location", res.Location) rctx.SetHeader("Content-Length", res.ContentLength) rctx.SetHeader("Docker-Upload-UUID", res.DockerUploadUUID) } rctx.SetStatus(code) } // POST /v2//blobs/uploads/?digest= 201/202 404/400 // POST /v2//blobs/uploads/?mount=&from= 201 404 // PATCH /v2//blobs/uploads/ 202 404/416 func (hand *Handler) PatchUpload(rctx *router.Context) { contentLength := rctx.GetHeader("Content-Length") contentType := rctx.GetHeader("Content-Type") contentRange := rctx.GetHeader("Content-Range") name, _ := rctx.GetSubpath("name") reference, _ := rctx.GetSubpath("reference") reader := rctx.Request.Body params := &operator.PatchUploadParams{ ContentLength: contentLength, ContentType: contentType, ContentRange: contentRange, Name: name, Reference: reference, Reader: reader, } // Rigth checking operatorID, _ := rctx.GetString(userTag) opEnable, err := hand.CheckRight(rctx.Ctx, operatorID, terms.RightWriteImages, "") if err != nil { rctx.SetStatus(http.StatusInternalServerError) return } if !opEnable { rctx.SetStatus(http.StatusMethodNotAllowed) return } // Execution of the operation ctx := rctx.GetContext() res, code, err := hand.oper.PatchUpload(ctx, params) if err != nil { hand.logg.Errorf("PatchUpload error: %v", err) } rctx.SetHeader("Location", res.Location) rctx.SetHeader("Range", res.Range) rctx.SetStatus(code) } // PUT /v2//blobs/uploads/?digest= 202 404/416 // PUT /v2//uploads/?digest= 202 404/416 func (hand *Handler) PutUpload(rctx *router.Context) { contentType := rctx.GetHeader("Content-Type") contentLength := rctx.GetHeader("Content-Length") contentRange := rctx.GetHeader("Content-Range") name, _ := rctx.GetSubpath("name") reference, _ := rctx.GetSubpath("reference") digest := rctx.GetQuery("digest") params := &operator.PutUploadParams{ ContentLength: contentLength, ContentType: contentType, ContentRange: contentRange, Name: name, Reference: reference, Digest: digest, } // Rigth checking operatorID, _ := rctx.GetString(userTag) opEnable, err := hand.CheckRight(rctx.Ctx, operatorID, terms.RightWriteImages, "") if err != nil { rctx.SetStatus(http.StatusInternalServerError) return } if !opEnable { rctx.SetStatus(http.StatusMethodNotAllowed) return } // Execution of the operation res, code, err := hand.oper.PutUpload(rctx.Ctx, params) if err != nil { hand.logg.Errorf("PutUpload error: %v", err) } rctx.SetHeader("Location", res.Location) rctx.SetStatus(code) } // GET /v2//blobs/ 200 404 func (hand *Handler) GetBlob(rctx *router.Context) { name, _ := rctx.GetSubpath("name") digest, _ := rctx.GetSubpath("digest") params := &operator.GetBlobParams{ Name: name, Digest: digest, } // Rigth checking operatorID, _ := rctx.GetString(userTag) opEnable, err := hand.CheckRight(rctx.Ctx, operatorID, terms.RightReadImages, "") if err != nil { rctx.SetStatus(http.StatusInternalServerError) return } if !opEnable { rctx.SetStatus(http.StatusMethodNotAllowed) return } // Execution of the operation ctx := rctx.GetContext() res, code, err := hand.oper.GetBlob(ctx, params) if err != nil { hand.logg.Errorf("GetBlob error: %v", err) } if code == http.StatusOK { rctx.SetHeader("Content-Length", res.ContentLength) rctx.SetHeader("Content-Type", res.ContentType) rctx.SetHeader("Docker-Content-Digest", res.DockerContentDigest) rctx.SetStatus(code) defer res.ReadCloser.Close() _, err = io.Copy(rctx.Writer, res.ReadCloser) if err != nil { hand.logg.Errorf("GetBlob error: %v", err) rctx.SetStatus(http.StatusInternalServerError) return } return } rctx.SetStatus(code) } // DELETE /v2//blobs/ 202 404/405 func (hand *Handler) DeleteBlob(rctx *router.Context) { name, _ := rctx.GetSubpath("name") digest, _ := rctx.GetSubpath("digest") params := &operator.DeleteBlobParams{ Name: name, Digest: digest, } // Rigth checking operatorID, _ := rctx.GetString(userTag) opEnable, err := hand.CheckRight(rctx.Ctx, operatorID, terms.RightWriteImages, "") if err != nil { rctx.SetStatus(http.StatusInternalServerError) return } if !opEnable { rctx.SetStatus(http.StatusMethodNotAllowed) return } // Execution of the operation ctx := rctx.GetContext() _, code, err := hand.oper.DeleteBlob(ctx, params) if err != nil { hand.logg.Errorf("DeleteBlob error: %v", err) } rctx.SetStatus(code) }