268 lines
7.2 KiB
Go
268 lines
7.2 KiB
Go
/*
|
|
* Copyright 2026 Oleg Borodin <onborodin@gmail.com>
|
|
*
|
|
* 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/imageoper"
|
|
"mstore/app/router"
|
|
"mstore/pkg/terms"
|
|
)
|
|
|
|
// HEAD /v2/<name>/blobs/<digest> 200 404
|
|
func (hand *Handler) BlobExists(rctx *router.Context) {
|
|
name, _ := rctx.GetSubpath("name")
|
|
digest, _ := rctx.GetSubpath("digest")
|
|
|
|
//hand.DumpHeaders("BlobExists", rctx)
|
|
|
|
params := &imageoper.BlobExistsParams{
|
|
Name: name,
|
|
Digest: digest,
|
|
}
|
|
// Rigth checking
|
|
operatorID, _ := rctx.GetString(userTag)
|
|
opEnable, err := hand.CheckRight(rctx.Ctx, operatorID, terms.RightReadImages, params.Name)
|
|
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.imop.BlobExists(ctx, operatorID, 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/<name>/blobs/uploads/ 202 404
|
|
// POST /v2/<name>/blobs/uploads/?digest=<digest> 201/202 404/400
|
|
// POST /v2/<name>/blobs/uploads/?mount=<digest>&from=<other_name> 201 404
|
|
func (hand *Handler) PostUpload(rctx *router.Context) {
|
|
name, _ := rctx.GetSubpath("name")
|
|
|
|
//hand.DumpHeaders("PostUploads", rctx)
|
|
|
|
authorization := rctx.GetHeader("Authorization")
|
|
if authorization == "" {
|
|
rctx.SetHeader("WWW-Authenticate", `Basic realm="mstore"`)
|
|
rctx.SetStatus(http.StatusUnauthorized)
|
|
return
|
|
}
|
|
|
|
digest := rctx.GetQuery("digest")
|
|
mount := rctx.GetQuery("mount")
|
|
from := rctx.GetQuery("from")
|
|
|
|
params := &imageoper.PostUploadParams{
|
|
Name: name,
|
|
Digest: digest,
|
|
Mount: mount,
|
|
From: from,
|
|
}
|
|
// Rigth checking
|
|
operatorID, _ := rctx.GetString(userTag)
|
|
opEnable, err := hand.CheckRight(rctx.Ctx, operatorID, terms.RightWriteImages, params.Name)
|
|
if err != nil {
|
|
rctx.SetStatus(http.StatusInternalServerError)
|
|
return
|
|
}
|
|
if !opEnable {
|
|
rctx.SetStatus(http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
// Execution of the operation
|
|
res, code, err := hand.imop.PostUpload(rctx.Ctx, operatorID, 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", string(res.DockerUploadUUID))
|
|
}
|
|
rctx.SetStatus(code)
|
|
}
|
|
|
|
// PATCH /v2/<name>/blobs/uploads/<reference> 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 := &imageoper.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, params.Name)
|
|
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.imop.PatchUpload(ctx, operatorID, 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/<name>/blobs/uploads/<reference>?digest=<digest> 202 404/416
|
|
// PUT /v2/<name>/uploads/<reference>?digest=<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")
|
|
reader := rctx.Request.Body
|
|
|
|
params := &imageoper.PutUploadParams{
|
|
ContentLength: contentLength,
|
|
ContentType: contentType,
|
|
ContentRange: contentRange,
|
|
Name: name,
|
|
Reference: reference,
|
|
Digest: digest,
|
|
Reader: reader,
|
|
}
|
|
// Rigth checking
|
|
operatorID, _ := rctx.GetString(userTag)
|
|
opEnable, err := hand.CheckRight(rctx.Ctx, operatorID, terms.RightWriteImages, params.Name)
|
|
if err != nil {
|
|
rctx.SetStatus(http.StatusInternalServerError)
|
|
return
|
|
}
|
|
if !opEnable {
|
|
rctx.SetStatus(http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
// Execution of the operation
|
|
res, code, err := hand.imop.PutUpload(rctx.Ctx, operatorID, params)
|
|
if err != nil {
|
|
hand.logg.Errorf("PutUpload error: %v", err)
|
|
}
|
|
rctx.SetHeader("Location", res.Location)
|
|
rctx.SetStatus(code)
|
|
}
|
|
|
|
// GET /v2/<name>/blobs/<digest> 200 404
|
|
func (hand *Handler) GetBlob(rctx *router.Context) {
|
|
|
|
name, _ := rctx.GetSubpath("name")
|
|
digest, _ := rctx.GetSubpath("digest")
|
|
|
|
params := &imageoper.GetBlobParams{
|
|
Name: name,
|
|
Digest: digest,
|
|
}
|
|
// Rigth checking
|
|
operatorID, _ := rctx.GetString(userTag)
|
|
opEnable, err := hand.CheckRight(rctx.Ctx, operatorID, terms.RightReadImages, params.Name)
|
|
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.imop.GetBlob(ctx, operatorID, 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/<name>/blobs/<digest> 202 404/405
|
|
func (hand *Handler) DeleteBlob(rctx *router.Context) {
|
|
|
|
name, _ := rctx.GetSubpath("name")
|
|
digest, _ := rctx.GetSubpath("digest")
|
|
|
|
params := &imageoper.DeleteBlobParams{
|
|
Name: name,
|
|
Digest: digest,
|
|
}
|
|
// Rigth checking
|
|
operatorID, _ := rctx.GetString(userTag)
|
|
opEnable, err := hand.CheckRight(rctx.Ctx, operatorID, terms.RightWriteImages, params.Name)
|
|
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.imop.DeleteBlob(ctx, operatorID, params)
|
|
if err != nil {
|
|
hand.logg.Errorf("DeleteBlob error: %v", err)
|
|
}
|
|
|
|
rctx.SetStatus(code)
|
|
}
|