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