code.gitea.io/gitea@v1.21.7/routers/api/packages/alpine/alpine.go (about) 1 // Copyright 2023 The Gitea Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 package alpine 5 6 import ( 7 "crypto/x509" 8 "encoding/hex" 9 "encoding/pem" 10 "errors" 11 "fmt" 12 "io" 13 "net/http" 14 "strings" 15 16 packages_model "code.gitea.io/gitea/models/packages" 17 "code.gitea.io/gitea/modules/context" 18 "code.gitea.io/gitea/modules/json" 19 packages_module "code.gitea.io/gitea/modules/packages" 20 alpine_module "code.gitea.io/gitea/modules/packages/alpine" 21 "code.gitea.io/gitea/modules/util" 22 "code.gitea.io/gitea/routers/api/packages/helper" 23 packages_service "code.gitea.io/gitea/services/packages" 24 alpine_service "code.gitea.io/gitea/services/packages/alpine" 25 ) 26 27 func apiError(ctx *context.Context, status int, obj any) { 28 helper.LogAndProcessError(ctx, status, obj, func(message string) { 29 ctx.PlainText(status, message) 30 }) 31 } 32 33 func GetRepositoryKey(ctx *context.Context) { 34 _, pub, err := alpine_service.GetOrCreateKeyPair(ctx, ctx.Package.Owner.ID) 35 if err != nil { 36 apiError(ctx, http.StatusInternalServerError, err) 37 return 38 } 39 40 pubPem, _ := pem.Decode([]byte(pub)) 41 if pubPem == nil { 42 apiError(ctx, http.StatusInternalServerError, "failed to decode private key pem") 43 return 44 } 45 46 pubKey, err := x509.ParsePKIXPublicKey(pubPem.Bytes) 47 if err != nil { 48 apiError(ctx, http.StatusInternalServerError, err) 49 return 50 } 51 52 fingerprint, err := util.CreatePublicKeyFingerprint(pubKey) 53 if err != nil { 54 apiError(ctx, http.StatusInternalServerError, err) 55 return 56 } 57 58 ctx.ServeContent(strings.NewReader(pub), &context.ServeHeaderOptions{ 59 ContentType: "application/x-pem-file", 60 Filename: fmt.Sprintf("%s@%s.rsa.pub", ctx.Package.Owner.LowerName, hex.EncodeToString(fingerprint)), 61 }) 62 } 63 64 func GetRepositoryFile(ctx *context.Context) { 65 pv, err := alpine_service.GetOrCreateRepositoryVersion(ctx, ctx.Package.Owner.ID) 66 if err != nil { 67 apiError(ctx, http.StatusInternalServerError, err) 68 return 69 } 70 71 s, u, pf, err := packages_service.GetFileStreamByPackageVersion( 72 ctx, 73 pv, 74 &packages_service.PackageFileInfo{ 75 Filename: alpine_service.IndexFilename, 76 CompositeKey: fmt.Sprintf("%s|%s|%s", ctx.Params("branch"), ctx.Params("repository"), ctx.Params("architecture")), 77 }, 78 ) 79 if err != nil { 80 if errors.Is(err, util.ErrNotExist) { 81 apiError(ctx, http.StatusNotFound, err) 82 } else { 83 apiError(ctx, http.StatusInternalServerError, err) 84 } 85 return 86 } 87 88 helper.ServePackageFile(ctx, s, u, pf) 89 } 90 91 func UploadPackageFile(ctx *context.Context) { 92 branch := strings.TrimSpace(ctx.Params("branch")) 93 repository := strings.TrimSpace(ctx.Params("repository")) 94 if branch == "" || repository == "" { 95 apiError(ctx, http.StatusBadRequest, "invalid branch or repository") 96 return 97 } 98 99 upload, close, err := ctx.UploadStream() 100 if err != nil { 101 apiError(ctx, http.StatusInternalServerError, err) 102 return 103 } 104 if close { 105 defer upload.Close() 106 } 107 108 buf, err := packages_module.CreateHashedBufferFromReader(upload) 109 if err != nil { 110 apiError(ctx, http.StatusInternalServerError, err) 111 return 112 } 113 defer buf.Close() 114 115 pck, err := alpine_module.ParsePackage(buf) 116 if err != nil { 117 if errors.Is(err, util.ErrInvalidArgument) || err == io.EOF { 118 apiError(ctx, http.StatusBadRequest, err) 119 } else { 120 apiError(ctx, http.StatusInternalServerError, err) 121 } 122 return 123 } 124 125 if _, err := buf.Seek(0, io.SeekStart); err != nil { 126 apiError(ctx, http.StatusInternalServerError, err) 127 return 128 } 129 130 fileMetadataRaw, err := json.Marshal(pck.FileMetadata) 131 if err != nil { 132 apiError(ctx, http.StatusInternalServerError, err) 133 return 134 } 135 136 _, _, err = packages_service.CreatePackageOrAddFileToExisting( 137 ctx, 138 &packages_service.PackageCreationInfo{ 139 PackageInfo: packages_service.PackageInfo{ 140 Owner: ctx.Package.Owner, 141 PackageType: packages_model.TypeAlpine, 142 Name: pck.Name, 143 Version: pck.Version, 144 }, 145 Creator: ctx.Doer, 146 Metadata: pck.VersionMetadata, 147 }, 148 &packages_service.PackageFileCreationInfo{ 149 PackageFileInfo: packages_service.PackageFileInfo{ 150 Filename: fmt.Sprintf("%s-%s.apk", pck.Name, pck.Version), 151 CompositeKey: fmt.Sprintf("%s|%s|%s", branch, repository, pck.FileMetadata.Architecture), 152 }, 153 Creator: ctx.Doer, 154 Data: buf, 155 IsLead: true, 156 Properties: map[string]string{ 157 alpine_module.PropertyBranch: branch, 158 alpine_module.PropertyRepository: repository, 159 alpine_module.PropertyArchitecture: pck.FileMetadata.Architecture, 160 alpine_module.PropertyMetadata: string(fileMetadataRaw), 161 }, 162 }, 163 ) 164 if err != nil { 165 switch err { 166 case packages_model.ErrDuplicatePackageVersion, packages_model.ErrDuplicatePackageFile: 167 apiError(ctx, http.StatusBadRequest, err) 168 case packages_service.ErrQuotaTotalCount, packages_service.ErrQuotaTypeSize, packages_service.ErrQuotaTotalSize: 169 apiError(ctx, http.StatusForbidden, err) 170 default: 171 apiError(ctx, http.StatusInternalServerError, err) 172 } 173 return 174 } 175 176 if err := alpine_service.BuildSpecificRepositoryFiles(ctx, ctx.Package.Owner.ID, branch, repository, pck.FileMetadata.Architecture); err != nil { 177 apiError(ctx, http.StatusInternalServerError, err) 178 return 179 } 180 181 ctx.Status(http.StatusCreated) 182 } 183 184 func DownloadPackageFile(ctx *context.Context) { 185 pfs, _, err := packages_model.SearchFiles(ctx, &packages_model.PackageFileSearchOptions{ 186 OwnerID: ctx.Package.Owner.ID, 187 PackageType: packages_model.TypeAlpine, 188 Query: ctx.Params("filename"), 189 CompositeKey: fmt.Sprintf("%s|%s|%s", ctx.Params("branch"), ctx.Params("repository"), ctx.Params("architecture")), 190 }) 191 if err != nil { 192 apiError(ctx, http.StatusInternalServerError, err) 193 return 194 } 195 if len(pfs) != 1 { 196 apiError(ctx, http.StatusNotFound, nil) 197 return 198 } 199 200 s, u, pf, err := packages_service.GetPackageFileStream(ctx, pfs[0]) 201 if err != nil { 202 if errors.Is(err, util.ErrNotExist) { 203 apiError(ctx, http.StatusNotFound, err) 204 } else { 205 apiError(ctx, http.StatusInternalServerError, err) 206 } 207 return 208 } 209 210 helper.ServePackageFile(ctx, s, u, pf) 211 } 212 213 func DeletePackageFile(ctx *context.Context) { 214 branch, repository, architecture := ctx.Params("branch"), ctx.Params("repository"), ctx.Params("architecture") 215 216 pfs, _, err := packages_model.SearchFiles(ctx, &packages_model.PackageFileSearchOptions{ 217 OwnerID: ctx.Package.Owner.ID, 218 PackageType: packages_model.TypeAlpine, 219 Query: ctx.Params("filename"), 220 CompositeKey: fmt.Sprintf("%s|%s|%s", branch, repository, architecture), 221 }) 222 if err != nil { 223 apiError(ctx, http.StatusInternalServerError, err) 224 return 225 } 226 if len(pfs) != 1 { 227 apiError(ctx, http.StatusNotFound, nil) 228 return 229 } 230 231 if err := packages_service.RemovePackageFileAndVersionIfUnreferenced(ctx, ctx.Doer, pfs[0]); err != nil { 232 if errors.Is(err, util.ErrNotExist) { 233 apiError(ctx, http.StatusNotFound, err) 234 } else { 235 apiError(ctx, http.StatusInternalServerError, err) 236 } 237 return 238 } 239 240 if err := alpine_service.BuildSpecificRepositoryFiles(ctx, ctx.Package.Owner.ID, branch, repository, architecture); err != nil { 241 apiError(ctx, http.StatusInternalServerError, err) 242 return 243 } 244 245 ctx.Status(http.StatusNoContent) 246 }