feat(nuget): basic manifest download
This commit is contained in:
		
							parent
							
								
									9fe826605f
								
							
						
					
					
						commit
						5f837efc15
					
				
					 3 changed files with 127 additions and 46 deletions
				
			
		| 
						 | 
				
			
			@ -5,6 +5,7 @@ package context
 | 
			
		|||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"encoding/xml"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"net/http"
 | 
			
		||||
| 
						 | 
				
			
			@ -136,6 +137,15 @@ func (b *Base) JSON(status int, content any) {
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// XML render content as XML
 | 
			
		||||
func (b *Base) XML(status int, content any) {
 | 
			
		||||
	b.Resp.Header().Set("Content-Type", "application/xml;charset=utf-8")
 | 
			
		||||
	b.Resp.WriteHeader(status)
 | 
			
		||||
	if err := xml.NewEncoder(b.Resp).Encode(content); err != nil {
 | 
			
		||||
		log.Error("Render XML failed: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RemoteAddr returns the client machine ip address
 | 
			
		||||
func (b *Base) RemoteAddr() string {
 | 
			
		||||
	return b.Req.RemoteAddr
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -71,34 +71,47 @@ type Dependency struct {
 | 
			
		|||
	Version string `json:"version"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type nuspecPackageType struct {
 | 
			
		||||
	Name string `xml:"name,attr"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type nuspecPackageTypes struct {
 | 
			
		||||
	PackageType []nuspecPackageType `xml:"packageType"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type nuspecRepository struct {
 | 
			
		||||
	URL string `xml:"url,attr"`
 | 
			
		||||
}
 | 
			
		||||
type nuspecDependency struct {
 | 
			
		||||
	ID      string `xml:"id,attr"`
 | 
			
		||||
	Version string `xml:"version,attr"`
 | 
			
		||||
	Exclude string `xml:"exclude,attr"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type nuspecGroup struct {
 | 
			
		||||
	TargetFramework string             `xml:"targetFramework,attr"`
 | 
			
		||||
	Dependency      []nuspecDependency `xml:"dependency"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type nuspecDependencies struct {
 | 
			
		||||
	Group []nuspecGroup `xml:"group"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type nuspeceMetadata struct {
 | 
			
		||||
	ID                       string             `xml:"id"`
 | 
			
		||||
	Version                  string             `xml:"version"`
 | 
			
		||||
	Authors                  string             `xml:"authors"`
 | 
			
		||||
	RequireLicenseAcceptance bool               `xml:"requireLicenseAcceptance"`
 | 
			
		||||
	ProjectURL               string             `xml:"projectUrl"`
 | 
			
		||||
	Description              string             `xml:"description"`
 | 
			
		||||
	ReleaseNotes             string             `xml:"releaseNotes"`
 | 
			
		||||
	PackageTypes             nuspecPackageTypes `xml:"packageTypes"`
 | 
			
		||||
	Repository               nuspecRepository   `xml:"repository"`
 | 
			
		||||
	Dependencies             nuspecDependencies `xml:"dependencies"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type nuspecPackage struct {
 | 
			
		||||
	Metadata struct {
 | 
			
		||||
		ID                       string `xml:"id"`
 | 
			
		||||
		Version                  string `xml:"version"`
 | 
			
		||||
		Authors                  string `xml:"authors"`
 | 
			
		||||
		RequireLicenseAcceptance bool   `xml:"requireLicenseAcceptance"`
 | 
			
		||||
		ProjectURL               string `xml:"projectUrl"`
 | 
			
		||||
		Description              string `xml:"description"`
 | 
			
		||||
		ReleaseNotes             string `xml:"releaseNotes"`
 | 
			
		||||
		PackageTypes             struct {
 | 
			
		||||
			PackageType []struct {
 | 
			
		||||
				Name string `xml:"name,attr"`
 | 
			
		||||
			} `xml:"packageType"`
 | 
			
		||||
		} `xml:"packageTypes"`
 | 
			
		||||
		Repository struct {
 | 
			
		||||
			URL string `xml:"url,attr"`
 | 
			
		||||
		} `xml:"repository"`
 | 
			
		||||
		Dependencies struct {
 | 
			
		||||
			Group []struct {
 | 
			
		||||
				TargetFramework string `xml:"targetFramework,attr"`
 | 
			
		||||
				Dependency      []struct {
 | 
			
		||||
					ID      string `xml:"id,attr"`
 | 
			
		||||
					Version string `xml:"version,attr"`
 | 
			
		||||
					Exclude string `xml:"exclude,attr"`
 | 
			
		||||
				} `xml:"dependency"`
 | 
			
		||||
			} `xml:"group"`
 | 
			
		||||
		} `xml:"dependencies"`
 | 
			
		||||
	} `xml:"metadata"`
 | 
			
		||||
	Metadata nuspeceMetadata `xml:"metadata"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ParsePackageMetaData parses the metadata of a Nuget package file
 | 
			
		||||
| 
						 | 
				
			
			@ -204,3 +217,36 @@ func toNormalizedVersion(v *version.Version) string {
 | 
			
		|||
	}
 | 
			
		||||
	return buf.String()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// returning any here because we use a private type and we don't need the type for xml marshalling
 | 
			
		||||
func GenerateNuspec(pd *Package) any {
 | 
			
		||||
	m := nuspeceMetadata{
 | 
			
		||||
		ID:                       pd.ID,
 | 
			
		||||
		Version:                  pd.Version,
 | 
			
		||||
		Authors:                  pd.Metadata.Authors,
 | 
			
		||||
		ProjectURL:               pd.Metadata.ProjectURL,
 | 
			
		||||
		Repository:               nuspecRepository{URL: pd.Metadata.RepositoryURL},
 | 
			
		||||
		RequireLicenseAcceptance: pd.Metadata.RequireLicenseAcceptance,
 | 
			
		||||
		Dependencies: nuspecDependencies{
 | 
			
		||||
			Group: make([]nuspecGroup, len(pd.Metadata.Dependencies)),
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for tgf, deps := range pd.Metadata.Dependencies {
 | 
			
		||||
		gDeps := make([]nuspecDependency, len(deps))
 | 
			
		||||
		for i, dep := range deps {
 | 
			
		||||
			gDeps[i] = nuspecDependency{
 | 
			
		||||
				ID:      dep.ID,
 | 
			
		||||
				Version: dep.Version,
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		m.Dependencies.Group = append(m.Dependencies.Group, nuspecGroup{
 | 
			
		||||
			TargetFramework: tgf,
 | 
			
		||||
			Dependency:      gDeps,
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &nuspecPackage{
 | 
			
		||||
		Metadata: m,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -387,34 +387,59 @@ func EnumeratePackageVersionsV3(ctx *context.Context) {
 | 
			
		|||
	ctx.JSON(http.StatusOK, resp)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// https://docs.microsoft.com/en-us/nuget/api/package-base-address-resource#download-package-content-nupkg
 | 
			
		||||
// https://learn.microsoft.com/en-us/nuget/api/package-base-address-resource#download-package-manifest-nuspec
 | 
			
		||||
// https://learn.microsoft.com/en-us/nuget/api/package-base-address-resource#download-package-content-nupkg
 | 
			
		||||
func DownloadPackageFile(ctx *context.Context) {
 | 
			
		||||
	packageName := ctx.Params("id")
 | 
			
		||||
	packageVersion := ctx.Params("version")
 | 
			
		||||
	filename := ctx.Params("filename")
 | 
			
		||||
 | 
			
		||||
	s, u, pf, err := packages_service.GetFileStreamByPackageNameAndVersion(
 | 
			
		||||
		ctx,
 | 
			
		||||
		&packages_service.PackageInfo{
 | 
			
		||||
			Owner:       ctx.Package.Owner,
 | 
			
		||||
			PackageType: packages_model.TypeNuGet,
 | 
			
		||||
			Name:        packageName,
 | 
			
		||||
			Version:     packageVersion,
 | 
			
		||||
		},
 | 
			
		||||
		&packages_service.PackageFileInfo{
 | 
			
		||||
			Filename: filename,
 | 
			
		||||
		},
 | 
			
		||||
	)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if err == packages_model.ErrPackageNotExist || err == packages_model.ErrPackageFileNotExist {
 | 
			
		||||
	if filename == fmt.Sprintf("%s.nuspec", packageName) {
 | 
			
		||||
		pv, err := packages_model.GetVersionByNameAndVersion(ctx, ctx.Package.Owner.ID, packages_model.TypeNuGet, packageName, packageVersion)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			apiError(ctx, http.StatusNotFound, err)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		apiError(ctx, http.StatusInternalServerError, err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	helper.ServePackageFile(ctx, s, u, pf)
 | 
			
		||||
		pd, err := packages_model.GetPackageDescriptor(ctx, pv)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			apiError(ctx, http.StatusInternalServerError, err)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		pkg := &nuget_module.Package{
 | 
			
		||||
			ID:       pd.Package.Name,
 | 
			
		||||
			Version:  packageVersion,
 | 
			
		||||
			Metadata: pd.Metadata.(*nuget_module.Metadata),
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		ctx.XML(http.StatusOK, nuget_module.GenerateNuspec(pkg))
 | 
			
		||||
	} else if filename == fmt.Sprintf("%s.%s.nupkg", packageName, packageVersion) {
 | 
			
		||||
 | 
			
		||||
		s, u, pf, err := packages_service.GetFileStreamByPackageNameAndVersion(
 | 
			
		||||
			ctx,
 | 
			
		||||
			&packages_service.PackageInfo{
 | 
			
		||||
				Owner:       ctx.Package.Owner,
 | 
			
		||||
				PackageType: packages_model.TypeNuGet,
 | 
			
		||||
				Name:        packageName,
 | 
			
		||||
				Version:     packageVersion,
 | 
			
		||||
			},
 | 
			
		||||
			&packages_service.PackageFileInfo{
 | 
			
		||||
				Filename: filename,
 | 
			
		||||
			},
 | 
			
		||||
		)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			if err == packages_model.ErrPackageNotExist || err == packages_model.ErrPackageFileNotExist {
 | 
			
		||||
				apiError(ctx, http.StatusNotFound, err)
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			apiError(ctx, http.StatusInternalServerError, err)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		helper.ServePackageFile(ctx, s, u, pf)
 | 
			
		||||
	} else {
 | 
			
		||||
		apiError(ctx, http.StatusInternalServerError, "Invalid filename")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UploadPackage creates a new package with the metadata contained in the uploaded nupgk file
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue