code.gitea.io/gitea@v1.21.7/routers/api/packages/generic/generic.go (about) 1 // Copyright 2021 The Gitea Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 package generic 5 6 import ( 7 "errors" 8 "net/http" 9 "regexp" 10 "strings" 11 12 packages_model "code.gitea.io/gitea/models/packages" 13 "code.gitea.io/gitea/modules/context" 14 "code.gitea.io/gitea/modules/log" 15 packages_module "code.gitea.io/gitea/modules/packages" 16 "code.gitea.io/gitea/routers/api/packages/helper" 17 packages_service "code.gitea.io/gitea/services/packages" 18 ) 19 20 var ( 21 packageNameRegex = regexp.MustCompile(`\A[A-Za-z0-9\.\_\-\+]+\z`) 22 filenameRegex = packageNameRegex 23 ) 24 25 func apiError(ctx *context.Context, status int, obj any) { 26 helper.LogAndProcessError(ctx, status, obj, func(message string) { 27 ctx.PlainText(status, message) 28 }) 29 } 30 31 // DownloadPackageFile serves the specific generic package. 32 func DownloadPackageFile(ctx *context.Context) { 33 s, u, pf, err := packages_service.GetFileStreamByPackageNameAndVersion( 34 ctx, 35 &packages_service.PackageInfo{ 36 Owner: ctx.Package.Owner, 37 PackageType: packages_model.TypeGeneric, 38 Name: ctx.Params("packagename"), 39 Version: ctx.Params("packageversion"), 40 }, 41 &packages_service.PackageFileInfo{ 42 Filename: ctx.Params("filename"), 43 }, 44 ) 45 if err != nil { 46 if err == packages_model.ErrPackageNotExist || err == packages_model.ErrPackageFileNotExist { 47 apiError(ctx, http.StatusNotFound, err) 48 return 49 } 50 apiError(ctx, http.StatusInternalServerError, err) 51 return 52 } 53 54 helper.ServePackageFile(ctx, s, u, pf) 55 } 56 57 // UploadPackage uploads the specific generic package. 58 // Duplicated packages get rejected. 59 func UploadPackage(ctx *context.Context) { 60 packageName := ctx.Params("packagename") 61 filename := ctx.Params("filename") 62 63 if !packageNameRegex.MatchString(packageName) || !filenameRegex.MatchString(filename) { 64 apiError(ctx, http.StatusBadRequest, errors.New("Invalid package name or filename")) 65 return 66 } 67 68 packageVersion := ctx.Params("packageversion") 69 if packageVersion != strings.TrimSpace(packageVersion) { 70 apiError(ctx, http.StatusBadRequest, errors.New("Invalid package version")) 71 return 72 } 73 74 upload, close, err := ctx.UploadStream() 75 if err != nil { 76 apiError(ctx, http.StatusInternalServerError, err) 77 return 78 } 79 if close { 80 defer upload.Close() 81 } 82 83 buf, err := packages_module.CreateHashedBufferFromReader(upload) 84 if err != nil { 85 log.Error("Error creating hashed buffer: %v", err) 86 apiError(ctx, http.StatusInternalServerError, err) 87 return 88 } 89 defer buf.Close() 90 91 _, _, err = packages_service.CreatePackageOrAddFileToExisting( 92 ctx, 93 &packages_service.PackageCreationInfo{ 94 PackageInfo: packages_service.PackageInfo{ 95 Owner: ctx.Package.Owner, 96 PackageType: packages_model.TypeGeneric, 97 Name: packageName, 98 Version: packageVersion, 99 }, 100 Creator: ctx.Doer, 101 }, 102 &packages_service.PackageFileCreationInfo{ 103 PackageFileInfo: packages_service.PackageFileInfo{ 104 Filename: filename, 105 }, 106 Creator: ctx.Doer, 107 Data: buf, 108 IsLead: true, 109 }, 110 ) 111 if err != nil { 112 switch err { 113 case packages_model.ErrDuplicatePackageFile: 114 apiError(ctx, http.StatusConflict, err) 115 case packages_service.ErrQuotaTotalCount, packages_service.ErrQuotaTypeSize, packages_service.ErrQuotaTotalSize: 116 apiError(ctx, http.StatusForbidden, err) 117 default: 118 apiError(ctx, http.StatusInternalServerError, err) 119 } 120 return 121 } 122 123 ctx.Status(http.StatusCreated) 124 } 125 126 // DeletePackage deletes the specific generic package. 127 func DeletePackage(ctx *context.Context) { 128 err := packages_service.RemovePackageVersionByNameAndVersion( 129 ctx, 130 ctx.Doer, 131 &packages_service.PackageInfo{ 132 Owner: ctx.Package.Owner, 133 PackageType: packages_model.TypeGeneric, 134 Name: ctx.Params("packagename"), 135 Version: ctx.Params("packageversion"), 136 }, 137 ) 138 if err != nil { 139 if err == packages_model.ErrPackageNotExist { 140 apiError(ctx, http.StatusNotFound, err) 141 return 142 } 143 apiError(ctx, http.StatusInternalServerError, err) 144 return 145 } 146 147 ctx.Status(http.StatusNoContent) 148 } 149 150 // DeletePackageFile deletes the specific file of a generic package. 151 func DeletePackageFile(ctx *context.Context) { 152 pv, pf, err := func() (*packages_model.PackageVersion, *packages_model.PackageFile, error) { 153 pv, err := packages_model.GetVersionByNameAndVersion(ctx, ctx.Package.Owner.ID, packages_model.TypeGeneric, ctx.Params("packagename"), ctx.Params("packageversion")) 154 if err != nil { 155 return nil, nil, err 156 } 157 158 pf, err := packages_model.GetFileForVersionByName(ctx, pv.ID, ctx.Params("filename"), packages_model.EmptyFileKey) 159 if err != nil { 160 return nil, nil, err 161 } 162 163 return pv, pf, nil 164 }() 165 if err != nil { 166 if err == packages_model.ErrPackageNotExist || err == packages_model.ErrPackageFileNotExist { 167 apiError(ctx, http.StatusNotFound, err) 168 return 169 } 170 apiError(ctx, http.StatusInternalServerError, err) 171 return 172 } 173 174 pfs, err := packages_model.GetFilesByVersionID(ctx, pv.ID) 175 if err != nil { 176 apiError(ctx, http.StatusInternalServerError, err) 177 return 178 } 179 180 if len(pfs) == 1 { 181 if err := packages_service.RemovePackageVersion(ctx, ctx.Doer, pv); err != nil { 182 apiError(ctx, http.StatusInternalServerError, err) 183 return 184 } 185 } else { 186 if err := packages_service.DeletePackageFile(ctx, pf); err != nil { 187 apiError(ctx, http.StatusInternalServerError, err) 188 return 189 } 190 } 191 192 ctx.Status(http.StatusNoContent) 193 }