Watch
1
0
Fork
You've already forked pkg-proxy
1
mirror of https://github.com/git-pkgs/proxy.git synced 2026-11-09 15:49:16 -05:00
pkg-proxy/internal/server/cache_api.go
Giles Westwood 8bebc560f7
Delete cached artifacts through the API and the UI (#491)
* Delete cached artifacts through the API and the UI

cache_delete (PROXY_CACHE_DELETE) registers
DELETE /api/cache/{ecosystem}/{name}/{version}, which removes a
version's cached files, or only ?filename=, so the next request fetches
them from the upstream again. Records are cleared and the stored objects
queued for deletion like a replaced artifact. Pushed archives are left
alone: a whole-version delete skips them and naming one answers 409.

With it enabled, the version page shows a Delete button for each cached
file and one for the whole version. The UI calls /ui/api/cache so the
same ingress rules that guard /ui guard its deletes. Neither route has
authentication of its own, so the option is off by default.

* Load the cache delete script from a static file

Move the version page's delete-button JavaScript into
static/cache-delete.js and load it with a script tag, as
packages-list.js does.

---------

Co-authored-by: Giles Westwood <201443+gilesw@users.noreply.github.com>
2026-10-20 11:35:08 +01:00

86 lines
2.9 KiB
Go

package server
import (
"net/http"
"strings"
"github.com/git-pkgs/proxy/internal/database"
"github.com/go-chi/chi/v5"
)
// CacheDeleteResponse lists the cached files removed for a version.
type CacheDeleteResponse struct {
PURL string `json:"purl"`
Deleted []string `json:"deleted"`
}
// handleCacheDelete handles DELETE /api/cache/{ecosystem}/{name}/{version}
// @Summary Delete cached artifacts
// @Description Removes the cached files of name@version, or only the one named by filename, so the next request fetches them from the upstream again. Pushed archives are not touched; delete those through /api/hosted.
// @Tags cache
// @Produce json
// @Param ecosystem path string true "Ecosystem"
// @Param name path string true "Package name as shown in the UI, e.g. com.google.guava:guava for Maven"
// @Param version path string true "Version"
// @Param filename query string false "Delete only this file"
// @Success 200 {object} CacheDeleteResponse
// @Failure 400 {object} ErrorResponse
// @Failure 404 {object} ErrorResponse
// @Failure 409 {object} ErrorResponse
// @Router /api/cache/{ecosystem}/{name}/{version} [delete]
func (s *Server) handleCacheDelete(w http.ResponseWriter, r *http.Request) {
segments, err := packagePathSegments(r)
if err != nil {
badRequest(w, err.Error())
return
}
if len(segments) < minVersionPathSegments {
badRequest(w, "path must be /api/cache/{ecosystem}/{name}/{version}")
return
}
last := len(segments) - 1
ecosystem, name, version := chi.URLParam(r, "ecosystem"), strings.Join(segments[:last], "/"), segments[last]
filename := r.URL.Query().Get("filename")
versionPURL := s.cachedVersionPURL(ecosystem, name, version)
if versionPURL == "" {
notFound(w, "package not found")
return
}
artifacts, err := s.db.GetArtifactsByVersionPURL(versionPURL)
if err != nil {
s.logger.Error("failed to list artifacts", "purl", versionPURL, "error", err)
internalError(w, "failed to list artifacts")
return
}
deleted := []string{}
for _, art := range artifacts {
if filename != "" && art.Filename != filename {
continue
}
if !art.StoragePath.Valid {
continue
}
if art.Source == database.SourceLocal {
if filename != "" {
writeError(w, http.StatusConflict, ErrCodeConflict, "pushed archives are deleted through /api/hosted")
return
}
continue
}
if err := s.db.DiscardArtifact(versionPURL, art.Filename, art.StoragePath.String); err != nil {
s.logger.Error("failed to delete cached artifact", "purl", versionPURL, "filename", art.Filename, "error", err)
internalError(w, "failed to delete cached artifact")
return
}
deleted = append(deleted, art.Filename)
}
if len(deleted) == 0 {
notFound(w, "no cached artifact matched")
return
}
s.logger.Info("deleted cached artifacts", "purl", versionPURL, "files", deleted)
writeJSON(w, CacheDeleteResponse{PURL: versionPURL, Deleted: deleted})
}