Add gems virtual repositories
ci/woodpecker/pr/pre-commit Pipeline was successful
ci/woodpecker/pr/test Pipeline was successful
ci/woodpecker/pr/build Pipeline was successful

Merge the compact index across gems members and redirect downloads to the owning member.
This commit is contained in:
2026-10-18 00:46:03 +11:00
parent 38641983e7
commit d035c6cec2
10 changed files with 633 additions and 2 deletions
+39
View File
@@ -0,0 +1,39 @@
package v1
import (
"errors"
"log/slog"
"net/http"
"git.unkin.net/unkin/artifactapi/internal/virtual"
"git.unkin.net/unkin/artifactapi/pkg/models"
)
// serveVirtualGems serves the merged compact index and 302s downloads to the
// member owning that version.
func (h *ProxyHandler) serveVirtualGems(w http.ResponseWriter, r *http.Request, virt models.Virtual, path, proxyBaseURL string) {
var body []byte
var loc string
var err error
if isGemsIndexPath(path) {
body, _, err = h.virtualEngine.FetchGems(r.Context(), virt, path)
} else {
loc, err = h.virtualEngine.GemsRedirect(r.Context(), virt, path, r.URL.RawQuery, proxyBaseURL)
}
switch {
case errors.Is(err, virtual.ErrNotFound):
gemsText(w, http.StatusNotFound, "not found")
case errors.Is(err, virtual.ErrBadPath):
gemsText(w, http.StatusBadRequest, err.Error())
case err != nil:
slog.Error("virtual gems fetch failed", "virtual", virt.Name, "path", path, "error", err)
gemsText(w, http.StatusBadGateway, "bad gateway")
case loc != "":
w.Header().Set("Cache-Control", "no-store")
w.Header().Set("Vary", "Host, X-Forwarded-Proto")
http.Redirect(w, r, loc, http.StatusFound)
default:
w.Header().Set("X-Artifact-Source", "virtual")
gemsText(w, http.StatusOK, string(body))
}
}
+6
View File
@@ -52,6 +52,7 @@ func (h *ProxyHandler) Routes() chi.Router {
r.Post("/local/{localName}/*", h.handleLocalPost)
r.Delete("/local/{localName}/*", h.handleLocalDelete)
r.Get("/virtual/{virtualName}/*", h.handleVirtual)
r.Head("/virtual/{virtualName}/*", h.handleVirtual)
return r
}
@@ -204,6 +205,11 @@ func (h *ProxyHandler) handleVirtual(w http.ResponseWriter, r *http.Request) {
return
}
if virt.PackageType == models.PackageGems {
h.serveVirtualGems(w, r, *virt, path, proxyBaseURL)
return
}
loc, ok, err := h.virtualEngine.MemberRedirect(r.Context(), *virt, path, r.URL.RawQuery, proxyBaseURL)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)