Skip to content

Commit

Permalink
Allow for resolution of NPM registry paths that match upstream (#21568)
Browse files Browse the repository at this point in the history
This PR fixes issue #21567 allowing for package tarball URLs to match
the upstream registry (and GitLab/JFrog Artifactory URLs). It uses a
regex to parse the filename (which contains the NPM version) and does a
fuzzy search to pull it out. The regex was built/expanded from
http://json.schemastore.org/package,
https://github.com/Masterminds/semver, and
https://docs.npmjs.com/cli/v6/using-npm/semver and is testable here:
https://regex101.com/r/OydBJq/5

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
  • Loading branch information
Racer159 and lunny authored Oct 24, 2022
1 parent 0218fa7 commit 49a4464
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 1 deletion.
2 changes: 2 additions & 0 deletions routers/api/packages/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@ func Routes(ctx gocontext.Context) *web.Route {
r.Get("", npm.DownloadPackageFile)
r.Delete("/-rev/{revision}", reqPackageAccess(perm.AccessModeWrite), npm.DeletePackageVersion)
})
r.Get("/-/{filename}", npm.DownloadPackageFileByName)
r.Group("/-rev/{revision}", func() {
r.Delete("", npm.DeletePackage)
r.Put("", npm.DeletePreview)
Expand All @@ -227,6 +228,7 @@ func Routes(ctx gocontext.Context) *web.Route {
r.Get("", npm.DownloadPackageFile)
r.Delete("/-rev/{revision}", reqPackageAccess(perm.AccessModeWrite), npm.DeletePackageVersion)
})
r.Get("/-/{filename}", npm.DownloadPackageFileByName)
r.Group("/-rev/{revision}", func() {
r.Delete("", npm.DeletePackage)
r.Put("", npm.DeletePreview)
Expand Down
43 changes: 43 additions & 0 deletions routers/api/packages/npm/npm.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,49 @@ func DownloadPackageFile(ctx *context.Context) {
ctx.ServeContent(pf.Name, s, pf.CreatedUnix.AsLocalTime())
}

// DownloadPackageFileByName finds the version and serves the contents of a package
func DownloadPackageFileByName(ctx *context.Context) {
filename := ctx.Params("filename")

pvs, _, err := packages_model.SearchVersions(ctx, &packages_model.PackageSearchOptions{
OwnerID: ctx.Package.Owner.ID,
Type: packages_model.TypeNpm,
Name: packages_model.SearchValue{
ExactMatch: true,
Value: packageNameFromParams(ctx),
},
HasFileWithName: filename,
IsInternal: util.OptionalBoolFalse,
})
if err != nil {
apiError(ctx, http.StatusInternalServerError, err)
return
}
if len(pvs) != 1 {
apiError(ctx, http.StatusNotFound, nil)
return
}

s, pf, err := packages_service.GetFileStreamByPackageVersion(
ctx,
pvs[0],
&packages_service.PackageFileInfo{
Filename: filename,
},
)
if err != nil {
if err == packages_model.ErrPackageFileNotExist {
apiError(ctx, http.StatusNotFound, err)
return
}
apiError(ctx, http.StatusInternalServerError, err)
return
}
defer s.Close()

ctx.ServeContent(pf.Name, s, pf.CreatedUnix.AsLocalTime())
}

// UploadPackage creates a new package
func UploadPackage(ctx *context.Context) {
npmPackage, err := npm_module.ParsePackage(ctx.Req.Body)
Expand Down
8 changes: 7 additions & 1 deletion tests/integration/api_packages_npm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,10 +127,16 @@ func TestPackageNpm(t *testing.T) {
b, _ := base64.StdEncoding.DecodeString(data)
assert.Equal(t, b, resp.Body.Bytes())

req = NewRequest(t, "GET", fmt.Sprintf("%s/-/%s", root, filename))
req = addTokenAuthHeader(req, token)
resp = MakeRequest(t, req, http.StatusOK)

assert.Equal(t, b, resp.Body.Bytes())

pvs, err := packages.GetVersionsByPackageType(db.DefaultContext, user.ID, packages.TypeNpm)
assert.NoError(t, err)
assert.Len(t, pvs, 1)
assert.Equal(t, int64(1), pvs[0].DownloadCount)
assert.Equal(t, int64(2), pvs[0].DownloadCount)
})

t.Run("PackageMetadata", func(t *testing.T) {
Expand Down

0 comments on commit 49a4464

Please sign in to comment.