diff --git a/routers/api/packages/api.go b/routers/api/packages/api.go index bc79669d51..214651fc2d 100644 --- a/routers/api/packages/api.go +++ b/routers/api/packages/api.go @@ -505,6 +505,7 @@ func CommonRoutes() *web.Route { r.Group("/{packagename}/{packageversion}", func() { r.Delete("", reqPackageAccess(perm.AccessModeWrite), generic.DeletePackage) r.Group("/{filename}", func() { + r.Head("", generic.CheckPackageFileExistence) r.Get("", generic.DownloadPackageFile) r.Group("", func() { r.Put("", enforcePackagesQuota(), generic.UploadPackage) diff --git a/routers/api/packages/generic/generic.go b/routers/api/packages/generic/generic.go index d61a9b4cbd..adffdcbe87 100644 --- a/routers/api/packages/generic/generic.go +++ b/routers/api/packages/generic/generic.go @@ -29,6 +29,38 @@ func apiError(ctx *context.Context, status int, obj any) { }) } +func CheckPackageFileExistence(ctx *context.Context) { + pv, err := packages_model.GetVersionByNameAndVersion( + ctx, + ctx.Package.Owner.ID, + packages_model.TypeGeneric, + ctx.Params("packagename"), + ctx.Params("packageversion"), + ) + if err != nil { + if err == packages_model.ErrPackageNotExist { + apiError(ctx, http.StatusNotFound, err) + return + } + apiError(ctx, http.StatusInternalServerError, err) + } + + pf, err := packages_model.GetFileForVersionByName(ctx, pv.ID, ctx.Params("filename"), "") + if err != nil { + if err == packages_model.ErrPackageFileNotExist { + apiError(ctx, http.StatusNotFound, err) + return + } + apiError(ctx, http.StatusInternalServerError, err) + } + + ctx.SetServeHeaders(&context.ServeHeaderOptions{ + Filename: pf.Name, + LastModified: pf.CreatedUnix.AsLocalTime(), + }) + ctx.Status(http.StatusOK) +} + // DownloadPackageFile serves the specific generic package. func DownloadPackageFile(ctx *context.Context) { s, u, pf, err := packages_service.GetFileStreamByPackageNameAndVersion( diff --git a/tests/integration/api_packages_generic_test.go b/tests/integration/api_packages_generic_test.go index ab65b76f15..2853bdde18 100644 --- a/tests/integration/api_packages_generic_test.go +++ b/tests/integration/api_packages_generic_test.go @@ -116,7 +116,10 @@ func TestPackageGeneric(t *testing.T) { checkDownloadCount(0) - req := NewRequest(t, "GET", url+"/"+filename) + req := NewRequest(t, "HEAD", url+"/"+filename) + MakeRequest(t, req, http.StatusOK) + + req = NewRequest(t, "GET", url+"/"+filename) resp := MakeRequest(t, req, http.StatusOK) assert.Equal(t, content, resp.Body.Bytes()) @@ -131,7 +134,10 @@ func TestPackageGeneric(t *testing.T) { t.Run("NotExists", func(t *testing.T) { defer tests.PrintCurrentTest(t)() - req := NewRequest(t, "GET", url+"/not.found") + req := NewRequest(t, "HEAD", url+"/not.found") + MakeRequest(t, req, http.StatusNotFound) + + req = NewRequest(t, "GET", url+"/not.found") MakeRequest(t, req, http.StatusNotFound) }) @@ -143,6 +149,9 @@ func TestPackageGeneric(t *testing.T) { setting.Service.RequireSignInView = false }() + req = NewRequest(t, "HEAD", url+"/dummy.bin") + MakeRequest(t, req, http.StatusUnauthorized) + req = NewRequest(t, "GET", url+"/dummy.bin") MakeRequest(t, req, http.StatusUnauthorized) }) @@ -164,7 +173,10 @@ func TestPackageGeneric(t *testing.T) { setting.Packages.Storage.MinioConfig.ServeDirect = true } - req := NewRequest(t, "GET", url+"/"+filename) + req := NewRequest(t, "HEAD", url+"/"+filename) + MakeRequest(t, req, http.StatusOK) + + req = NewRequest(t, "GET", url+"/"+filename) resp := MakeRequest(t, req, http.StatusSeeOther) checkDownloadCount(3)