mirror of
https://github.com/harness/drone.git
synced 2025-05-05 23:42:57 +00:00
* feat:[AH-1093]: NPM search package support * Merge branch 'main' of https://git0.harness.io/l7B_kbSEQD2wjrM7PShm5w/PROD/Harness_Commons/gitness * Merge branch 'main' of https://git0.harness.io/l7B_kbSEQD2wjrM7PShm5w/PROD/Harness_Commons/gitness * Merge branch 'main' of https://git0.harness.io/l7B_kbSEQD2wjrM7PShm5w/PROD/Harness_Commons/gitness * Merge branch 'release/registry-api_1.18.0' of https://git0.harness.io/l7B_kbSEQD2wjrM7PShm5w/PROD/Harness_Commons/gitness * feat:[AH-1083]: url fix (#3669) * feat:[AH-1083]: url fix
180 lines
5.1 KiB
Go
180 lines
5.1 KiB
Go
// Copyright 2023 Harness, Inc.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package npm
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"regexp"
|
|
"strings"
|
|
|
|
npm3 "github.com/harness/gitness/registry/app/api/controller/pkg/npm"
|
|
"github.com/harness/gitness/registry/app/api/handler/packages"
|
|
npm2 "github.com/harness/gitness/registry/app/metadata/npm"
|
|
"github.com/harness/gitness/registry/app/pkg"
|
|
"github.com/harness/gitness/registry/app/pkg/commons"
|
|
"github.com/harness/gitness/registry/app/pkg/types/npm"
|
|
|
|
"github.com/rs/zerolog/log"
|
|
)
|
|
|
|
var (
|
|
ErrInvalidPackageVersion = errors.New("package version is invalid")
|
|
ErrInvalidAttachment = errors.New("package attachment is invalid")
|
|
packageNameRegex = regexp.MustCompile(`^(?:@[\w.-]+\/)?[\w.-]+$`)
|
|
versionRegex = regexp.MustCompile(`^(\d+)\.(\d+)\.(\d+)(?:-([\w.-]+))?(?:\+([\w.-]+))?$`)
|
|
)
|
|
|
|
type Handler interface {
|
|
pkg.ArtifactInfoProvider
|
|
UploadPackage(writer http.ResponseWriter, request *http.Request)
|
|
DownloadPackageFile(http.ResponseWriter, *http.Request)
|
|
GetPackageMetadata(http.ResponseWriter, *http.Request)
|
|
DownloadPackageFileByName(http.ResponseWriter, *http.Request)
|
|
HeadPackageFileByName(http.ResponseWriter, *http.Request)
|
|
|
|
ListPackageTag(http.ResponseWriter, *http.Request)
|
|
AddPackageTag(http.ResponseWriter, *http.Request)
|
|
DeletePackageTag(http.ResponseWriter, *http.Request)
|
|
DeletePackage(w http.ResponseWriter, r *http.Request)
|
|
DeleteVersion(w http.ResponseWriter, r *http.Request)
|
|
DeletePreview(w http.ResponseWriter, r *http.Request)
|
|
SearchPackage(w http.ResponseWriter, r *http.Request)
|
|
}
|
|
|
|
type handler struct {
|
|
packages.Handler
|
|
controller npm3.Controller
|
|
}
|
|
|
|
func NewHandler(
|
|
controller npm3.Controller,
|
|
packageHandler packages.Handler,
|
|
) Handler {
|
|
return &handler{
|
|
Handler: packageHandler,
|
|
controller: controller,
|
|
}
|
|
}
|
|
|
|
var _ Handler = (*handler)(nil)
|
|
|
|
func (h *handler) GetPackageArtifactInfo(r *http.Request) (pkg.PackageArtifactInfo, error) {
|
|
info, e := h.GetArtifactInfo(r)
|
|
|
|
if !commons.IsEmpty(e) {
|
|
return npm.ArtifactInfo{}, e
|
|
}
|
|
|
|
info.Image = PackageNameFromParams(r)
|
|
version := GetVersionFromParams(r)
|
|
fileName := GetFileName(r)
|
|
|
|
if info.Image != "" && version != "" && !isValidNameAndVersion(info.Image, version) {
|
|
log.Info().Msgf("Invalid image name/version: %s/%s", info.Image, version)
|
|
return nil, fmt.Errorf("invalid name or version")
|
|
}
|
|
distTags := r.PathValue("tag")
|
|
|
|
npmInfo := npm.ArtifactInfo{
|
|
ArtifactInfo: info,
|
|
Filename: fileName,
|
|
Version: version,
|
|
ParentRegIdentifier: info.RegIdentifier,
|
|
DistTags: []string{distTags},
|
|
}
|
|
|
|
if r.Body == nil || r.ContentLength == 0 {
|
|
return npmInfo, nil
|
|
}
|
|
|
|
if strings.Contains(r.URL.Path, "/-rev/") {
|
|
return npmInfo, nil
|
|
}
|
|
|
|
if strings.Contains(r.URL.Path, "/-/package/") && strings.Contains(r.URL.Path, "/dist-tags/") {
|
|
// Process the payload only for add tag requests
|
|
if r.Body == nil || r.ContentLength == 0 {
|
|
return npmInfo, nil
|
|
}
|
|
|
|
body, err := io.ReadAll(r.Body)
|
|
if err != nil {
|
|
return npm.ArtifactInfo{}, err
|
|
}
|
|
npmInfo.Version = strings.Trim(string(body), "\"")
|
|
npmInfo.DistTags = []string{r.PathValue("tag")}
|
|
return npmInfo, err
|
|
}
|
|
|
|
return GetNPMMetadata(r, info)
|
|
}
|
|
|
|
func GetNPMMetadata(r *http.Request, info pkg.ArtifactInfo) (pkg.PackageArtifactInfo, error) {
|
|
var md npm2.PackageUpload
|
|
|
|
// Read body into a buffer
|
|
var buf bytes.Buffer
|
|
tee := io.TeeReader(r.Body, &buf)
|
|
if err := json.NewDecoder(tee).Decode(&md); err != nil {
|
|
return npm.ArtifactInfo{}, err
|
|
}
|
|
|
|
r.Body = io.NopCloser(&buf)
|
|
|
|
for _, meta := range md.Versions {
|
|
a := npm.ArtifactInfo{
|
|
ArtifactInfo: info,
|
|
Metadata: md.PackageMetadata,
|
|
Version: meta.Version,
|
|
DistTags: make([]string, 0),
|
|
ParentRegIdentifier: info.RegIdentifier,
|
|
}
|
|
for tag := range md.DistTags {
|
|
a.DistTags = append(a.DistTags, tag)
|
|
}
|
|
a.Filename = strings.ToLower(fmt.Sprintf("%s-%s.tgz", md.Name, a.Version))
|
|
return a, nil
|
|
}
|
|
return npm.ArtifactInfo{}, ErrInvalidPackageVersion
|
|
}
|
|
|
|
func GetNPMFile(r *http.Request) (io.ReadCloser, error) {
|
|
var md npm2.PackageUpload
|
|
if err := json.NewDecoder(r.Body).Decode(&md); err != nil {
|
|
return nil, err
|
|
}
|
|
attachment := func() *npm2.PackageAttachment {
|
|
for _, a := range md.Attachments {
|
|
return a
|
|
}
|
|
return nil
|
|
}()
|
|
if attachment == nil || len(attachment.Data) == 0 {
|
|
return nil, ErrInvalidAttachment
|
|
}
|
|
return io.NopCloser(base64.NewDecoder(base64.StdEncoding,
|
|
strings.NewReader(attachment.Data))), nil
|
|
}
|
|
|
|
func isValidNameAndVersion(image, version string) bool {
|
|
return packageNameRegex.MatchString(image) && versionRegex.MatchString(version)
|
|
}
|