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 }