package adminapi import ( "net/http" "github.com/iceBear67/simplepages/api" "github.com/iceBear67/simplepages/internal/cas" "github.com/iceBear67/simplepages/internal/httpx" ) // putBlob handles PUT /api/v1/blobs/{digest}. // // Blobs are global rather than per-project because the store deduplicates // across projects, so any authenticated key may upload one — but only content // some manifest already declared, and only bytes that really hash to the digest // in the URL. Both checks live in deploy.Service and cas.Store; the digest here // is a claim until then. // // The route is deliberately not owner-guarded: there is no project in the path // to guard against. What a caller can do with it is bounded by the manifest // requirement, and the resulting cross-project existence oracle is the known, // documented trade-off of shared deduplication. func (s *Server) putBlob(w http.ResponseWriter, r *http.Request) error { digest, err := cas.ParseDigest(r.PathValue("digest")) if err != nil { return api.Errorf(api.CodeBadRequest, "%s", err) } size, stored, err := s.Deploy.Upload(r.Context(), digest, r.Body) if err != nil { return err } status := http.StatusOK // already had it if stored { status = http.StatusCreated } httpx.WriteJSON(w, status, api.BlobResponse{Digest: digest.String(), Size: size}) return nil }