All import paths updated to git.wntrmute.dev/mc/. Bumps mcdsl to v1.2.0. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
93 lines
2.9 KiB
Go
93 lines
2.9 KiB
Go
package oci
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"net/http"
|
|
|
|
"git.wntrmute.dev/mc/mcr/internal/db"
|
|
"git.wntrmute.dev/mc/mcr/internal/policy"
|
|
)
|
|
|
|
// handleManifestDelete handles DELETE /v2/<name>/manifests/<digest>.
|
|
// Per OCI spec, deletion by tag is not supported — only by digest.
|
|
func (h *Handler) handleManifestDelete(w http.ResponseWriter, r *http.Request, repo, reference string) {
|
|
if !h.checkPolicy(w, r, policy.ActionDelete, repo) {
|
|
return
|
|
}
|
|
|
|
// Reference must be a digest, not a tag.
|
|
if !isDigest(reference) {
|
|
writeOCIError(w, "UNSUPPORTED", http.StatusMethodNotAllowed,
|
|
"manifest deletion by tag is not supported; use digest")
|
|
return
|
|
}
|
|
|
|
repoID, err := h.db.GetRepositoryByName(repo)
|
|
if err != nil {
|
|
if errors.Is(err, db.ErrRepoNotFound) {
|
|
writeOCIError(w, "NAME_UNKNOWN", http.StatusNotFound,
|
|
fmt.Sprintf("repository %q not found", repo))
|
|
return
|
|
}
|
|
h.log.Error("manifest delete: lookup repository", "error", err, "repo", repo)
|
|
writeOCIError(w, "UNKNOWN", http.StatusInternalServerError, "internal error")
|
|
return
|
|
}
|
|
|
|
if err := h.db.DeleteManifest(repoID, reference); err != nil {
|
|
if errors.Is(err, db.ErrManifestNotFound) {
|
|
writeOCIError(w, "MANIFEST_UNKNOWN", http.StatusNotFound,
|
|
fmt.Sprintf("manifest %q not found", reference))
|
|
return
|
|
}
|
|
h.log.Error("manifest delete: delete from database", "error", err, "repo", repo, "digest", reference)
|
|
writeOCIError(w, "UNKNOWN", http.StatusInternalServerError, "internal error")
|
|
return
|
|
}
|
|
|
|
h.audit(r, "manifest_deleted", repo, reference)
|
|
|
|
h.log.Info("manifest deleted", "repo", repo, "digest", reference)
|
|
|
|
w.WriteHeader(http.StatusAccepted)
|
|
}
|
|
|
|
// handleBlobDelete handles DELETE /v2/<name>/blobs/<digest>.
|
|
// Removes manifest_blobs associations for this repo only. Does not delete
|
|
// the blob row or file — that is GC's responsibility.
|
|
func (h *Handler) handleBlobDelete(w http.ResponseWriter, r *http.Request, repo, digest string) {
|
|
if !h.checkPolicy(w, r, policy.ActionDelete, repo) {
|
|
return
|
|
}
|
|
|
|
repoID, err := h.db.GetRepositoryByName(repo)
|
|
if err != nil {
|
|
if errors.Is(err, db.ErrRepoNotFound) {
|
|
writeOCIError(w, "NAME_UNKNOWN", http.StatusNotFound,
|
|
fmt.Sprintf("repository %q not found", repo))
|
|
return
|
|
}
|
|
h.log.Error("blob delete: lookup repository", "error", err, "repo", repo)
|
|
writeOCIError(w, "UNKNOWN", http.StatusInternalServerError, "internal error")
|
|
return
|
|
}
|
|
|
|
if err := h.db.DeleteBlobFromRepo(repoID, digest); err != nil {
|
|
if errors.Is(err, db.ErrBlobNotFound) {
|
|
writeOCIError(w, "BLOB_UNKNOWN", http.StatusNotFound,
|
|
fmt.Sprintf("blob %q not found in repository", digest))
|
|
return
|
|
}
|
|
h.log.Error("blob delete: delete from database", "error", err, "repo", repo, "digest", digest)
|
|
writeOCIError(w, "UNKNOWN", http.StatusInternalServerError, "internal error")
|
|
return
|
|
}
|
|
|
|
h.audit(r, "blob_deleted", repo, digest)
|
|
|
|
h.log.Info("blob deleted", "repo", repo, "digest", digest)
|
|
|
|
w.WriteHeader(http.StatusAccepted)
|
|
}
|