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 }