code.gitea.io/gitea@v1.22.3/routers/api/packages/goproxy/goproxy.go (about)

     1  // Copyright 2023 The Gitea Authors. All rights reserved.
     2  // SPDX-License-Identifier: MIT
     3  
     4  package goproxy
     5  
     6  import (
     7  	"errors"
     8  	"fmt"
     9  	"io"
    10  	"net/http"
    11  	"sort"
    12  	"time"
    13  
    14  	packages_model "code.gitea.io/gitea/models/packages"
    15  	"code.gitea.io/gitea/modules/optional"
    16  	packages_module "code.gitea.io/gitea/modules/packages"
    17  	goproxy_module "code.gitea.io/gitea/modules/packages/goproxy"
    18  	"code.gitea.io/gitea/modules/util"
    19  	"code.gitea.io/gitea/routers/api/packages/helper"
    20  	"code.gitea.io/gitea/services/context"
    21  	packages_service "code.gitea.io/gitea/services/packages"
    22  )
    23  
    24  func apiError(ctx *context.Context, status int, obj any) {
    25  	helper.LogAndProcessError(ctx, status, obj, func(message string) {
    26  		ctx.PlainText(status, message)
    27  	})
    28  }
    29  
    30  func EnumeratePackageVersions(ctx *context.Context) {
    31  	pvs, err := packages_model.GetVersionsByPackageName(ctx, ctx.Package.Owner.ID, packages_model.TypeGo, ctx.Params("name"))
    32  	if err != nil {
    33  		apiError(ctx, http.StatusInternalServerError, err)
    34  		return
    35  	}
    36  	if len(pvs) == 0 {
    37  		apiError(ctx, http.StatusNotFound, err)
    38  		return
    39  	}
    40  
    41  	sort.Slice(pvs, func(i, j int) bool {
    42  		return pvs[i].CreatedUnix < pvs[j].CreatedUnix
    43  	})
    44  
    45  	ctx.Resp.Header().Set("Content-Type", "text/plain;charset=utf-8")
    46  
    47  	for _, pv := range pvs {
    48  		fmt.Fprintln(ctx.Resp, pv.Version)
    49  	}
    50  }
    51  
    52  func PackageVersionMetadata(ctx *context.Context) {
    53  	pv, err := resolvePackage(ctx, ctx.Package.Owner.ID, ctx.Params("name"), ctx.Params("version"))
    54  	if err != nil {
    55  		if errors.Is(err, util.ErrNotExist) {
    56  			apiError(ctx, http.StatusNotFound, err)
    57  		} else {
    58  			apiError(ctx, http.StatusInternalServerError, err)
    59  		}
    60  		return
    61  	}
    62  
    63  	ctx.JSON(http.StatusOK, struct {
    64  		Version string    `json:"Version"`
    65  		Time    time.Time `json:"Time"`
    66  	}{
    67  		Version: pv.Version,
    68  		Time:    pv.CreatedUnix.AsLocalTime(),
    69  	})
    70  }
    71  
    72  func PackageVersionGoModContent(ctx *context.Context) {
    73  	pv, err := resolvePackage(ctx, ctx.Package.Owner.ID, ctx.Params("name"), ctx.Params("version"))
    74  	if err != nil {
    75  		if errors.Is(err, util.ErrNotExist) {
    76  			apiError(ctx, http.StatusNotFound, err)
    77  		} else {
    78  			apiError(ctx, http.StatusInternalServerError, err)
    79  		}
    80  		return
    81  	}
    82  
    83  	pps, err := packages_model.GetPropertiesByName(ctx, packages_model.PropertyTypeVersion, pv.ID, goproxy_module.PropertyGoMod)
    84  	if err != nil || len(pps) != 1 {
    85  		apiError(ctx, http.StatusInternalServerError, err)
    86  		return
    87  	}
    88  
    89  	ctx.PlainText(http.StatusOK, pps[0].Value)
    90  }
    91  
    92  func DownloadPackageFile(ctx *context.Context) {
    93  	pv, err := resolvePackage(ctx, ctx.Package.Owner.ID, ctx.Params("name"), ctx.Params("version"))
    94  	if err != nil {
    95  		if errors.Is(err, util.ErrNotExist) {
    96  			apiError(ctx, http.StatusNotFound, err)
    97  		} else {
    98  			apiError(ctx, http.StatusInternalServerError, err)
    99  		}
   100  		return
   101  	}
   102  
   103  	pfs, err := packages_model.GetFilesByVersionID(ctx, pv.ID)
   104  	if err != nil || len(pfs) != 1 {
   105  		apiError(ctx, http.StatusInternalServerError, err)
   106  		return
   107  	}
   108  
   109  	s, u, _, err := packages_service.GetPackageFileStream(ctx, pfs[0])
   110  	if err != nil {
   111  		if errors.Is(err, util.ErrNotExist) {
   112  			apiError(ctx, http.StatusNotFound, err)
   113  		} else {
   114  			apiError(ctx, http.StatusInternalServerError, err)
   115  		}
   116  		return
   117  	}
   118  
   119  	helper.ServePackageFile(ctx, s, u, pfs[0])
   120  }
   121  
   122  func resolvePackage(ctx *context.Context, ownerID int64, name, version string) (*packages_model.PackageVersion, error) {
   123  	var pv *packages_model.PackageVersion
   124  
   125  	if version == "latest" {
   126  		pvs, _, err := packages_model.SearchLatestVersions(ctx, &packages_model.PackageSearchOptions{
   127  			OwnerID: ownerID,
   128  			Type:    packages_model.TypeGo,
   129  			Name: packages_model.SearchValue{
   130  				Value:      name,
   131  				ExactMatch: true,
   132  			},
   133  			IsInternal: optional.Some(false),
   134  			Sort:       packages_model.SortCreatedDesc,
   135  		})
   136  		if err != nil {
   137  			return nil, err
   138  		}
   139  
   140  		if len(pvs) != 1 {
   141  			return nil, packages_model.ErrPackageNotExist
   142  		}
   143  
   144  		pv = pvs[0]
   145  	} else {
   146  		var err error
   147  		pv, err = packages_model.GetVersionByNameAndVersion(ctx, ownerID, packages_model.TypeGo, name, version)
   148  		if err != nil {
   149  			return nil, err
   150  		}
   151  	}
   152  
   153  	return pv, nil
   154  }
   155  
   156  func UploadPackage(ctx *context.Context) {
   157  	upload, needToClose, err := ctx.UploadStream()
   158  	if err != nil {
   159  		apiError(ctx, http.StatusInternalServerError, err)
   160  		return
   161  	}
   162  	if needToClose {
   163  		defer upload.Close()
   164  	}
   165  
   166  	buf, err := packages_module.CreateHashedBufferFromReader(upload)
   167  	if err != nil {
   168  		apiError(ctx, http.StatusInternalServerError, err)
   169  		return
   170  	}
   171  	defer buf.Close()
   172  
   173  	pck, err := goproxy_module.ParsePackage(buf, buf.Size())
   174  	if err != nil {
   175  		if errors.Is(err, util.ErrInvalidArgument) {
   176  			apiError(ctx, http.StatusBadRequest, err)
   177  		} else {
   178  			apiError(ctx, http.StatusInternalServerError, err)
   179  		}
   180  		return
   181  	}
   182  
   183  	if _, err := buf.Seek(0, io.SeekStart); err != nil {
   184  		apiError(ctx, http.StatusInternalServerError, err)
   185  		return
   186  	}
   187  
   188  	_, _, err = packages_service.CreatePackageAndAddFile(
   189  		ctx,
   190  		&packages_service.PackageCreationInfo{
   191  			PackageInfo: packages_service.PackageInfo{
   192  				Owner:       ctx.Package.Owner,
   193  				PackageType: packages_model.TypeGo,
   194  				Name:        pck.Name,
   195  				Version:     pck.Version,
   196  			},
   197  			Creator: ctx.Doer,
   198  			VersionProperties: map[string]string{
   199  				goproxy_module.PropertyGoMod: pck.GoMod,
   200  			},
   201  		},
   202  		&packages_service.PackageFileCreationInfo{
   203  			PackageFileInfo: packages_service.PackageFileInfo{
   204  				Filename: fmt.Sprintf("%v.zip", pck.Version),
   205  			},
   206  			Creator: ctx.Doer,
   207  			Data:    buf,
   208  			IsLead:  true,
   209  		},
   210  	)
   211  	if err != nil {
   212  		switch err {
   213  		case packages_model.ErrDuplicatePackageVersion:
   214  			apiError(ctx, http.StatusConflict, err)
   215  		case packages_service.ErrQuotaTotalCount, packages_service.ErrQuotaTypeSize, packages_service.ErrQuotaTotalSize:
   216  			apiError(ctx, http.StatusForbidden, err)
   217  		default:
   218  			apiError(ctx, http.StatusInternalServerError, err)
   219  		}
   220  		return
   221  	}
   222  
   223  	ctx.Status(http.StatusCreated)
   224  }