code.gitea.io/gitea@v1.21.7/routers/api/v1/repo/release_attachment.go (about) 1 // Copyright 2018 The Gitea Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 package repo 5 6 import ( 7 "net/http" 8 9 repo_model "code.gitea.io/gitea/models/repo" 10 "code.gitea.io/gitea/modules/context" 11 "code.gitea.io/gitea/modules/log" 12 "code.gitea.io/gitea/modules/setting" 13 api "code.gitea.io/gitea/modules/structs" 14 "code.gitea.io/gitea/modules/upload" 15 "code.gitea.io/gitea/modules/web" 16 "code.gitea.io/gitea/services/attachment" 17 "code.gitea.io/gitea/services/convert" 18 ) 19 20 func checkReleaseMatchRepo(ctx *context.APIContext, releaseID int64) bool { 21 release, err := repo_model.GetReleaseByID(ctx, releaseID) 22 if err != nil { 23 if repo_model.IsErrReleaseNotExist(err) { 24 ctx.NotFound() 25 return false 26 } 27 ctx.Error(http.StatusInternalServerError, "GetReleaseByID", err) 28 return false 29 } 30 if release.RepoID != ctx.Repo.Repository.ID { 31 ctx.NotFound() 32 return false 33 } 34 return true 35 } 36 37 // GetReleaseAttachment gets a single attachment of the release 38 func GetReleaseAttachment(ctx *context.APIContext) { 39 // swagger:operation GET /repos/{owner}/{repo}/releases/{id}/assets/{attachment_id} repository repoGetReleaseAttachment 40 // --- 41 // summary: Get a release attachment 42 // produces: 43 // - application/json 44 // parameters: 45 // - name: owner 46 // in: path 47 // description: owner of the repo 48 // type: string 49 // required: true 50 // - name: repo 51 // in: path 52 // description: name of the repo 53 // type: string 54 // required: true 55 // - name: id 56 // in: path 57 // description: id of the release 58 // type: integer 59 // format: int64 60 // required: true 61 // - name: attachment_id 62 // in: path 63 // description: id of the attachment to get 64 // type: integer 65 // format: int64 66 // required: true 67 // responses: 68 // "200": 69 // "$ref": "#/responses/Attachment" 70 // "404": 71 // "$ref": "#/responses/notFound" 72 73 releaseID := ctx.ParamsInt64(":id") 74 if !checkReleaseMatchRepo(ctx, releaseID) { 75 return 76 } 77 78 attachID := ctx.ParamsInt64(":attachment_id") 79 attach, err := repo_model.GetAttachmentByID(ctx, attachID) 80 if err != nil { 81 if repo_model.IsErrAttachmentNotExist(err) { 82 ctx.NotFound() 83 return 84 } 85 ctx.Error(http.StatusInternalServerError, "GetAttachmentByID", err) 86 return 87 } 88 if attach.ReleaseID != releaseID { 89 log.Info("User requested attachment is not in release, release_id %v, attachment_id: %v", releaseID, attachID) 90 ctx.NotFound() 91 return 92 } 93 // FIXME Should prove the existence of the given repo, but results in unnecessary database requests 94 ctx.JSON(http.StatusOK, convert.ToAPIAttachment(ctx.Repo.Repository, attach)) 95 } 96 97 // ListReleaseAttachments lists all attachments of the release 98 func ListReleaseAttachments(ctx *context.APIContext) { 99 // swagger:operation GET /repos/{owner}/{repo}/releases/{id}/assets repository repoListReleaseAttachments 100 // --- 101 // summary: List release's attachments 102 // produces: 103 // - application/json 104 // parameters: 105 // - name: owner 106 // in: path 107 // description: owner of the repo 108 // type: string 109 // required: true 110 // - name: repo 111 // in: path 112 // description: name of the repo 113 // type: string 114 // required: true 115 // - name: id 116 // in: path 117 // description: id of the release 118 // type: integer 119 // format: int64 120 // required: true 121 // responses: 122 // "200": 123 // "$ref": "#/responses/AttachmentList" 124 // "404": 125 // "$ref": "#/responses/notFound" 126 127 releaseID := ctx.ParamsInt64(":id") 128 release, err := repo_model.GetReleaseByID(ctx, releaseID) 129 if err != nil { 130 if repo_model.IsErrReleaseNotExist(err) { 131 ctx.NotFound() 132 return 133 } 134 ctx.Error(http.StatusInternalServerError, "GetReleaseByID", err) 135 return 136 } 137 if release.RepoID != ctx.Repo.Repository.ID { 138 ctx.NotFound() 139 return 140 } 141 if err := release.LoadAttributes(ctx); err != nil { 142 ctx.Error(http.StatusInternalServerError, "LoadAttributes", err) 143 return 144 } 145 ctx.JSON(http.StatusOK, convert.ToAPIRelease(ctx, ctx.Repo.Repository, release).Attachments) 146 } 147 148 // CreateReleaseAttachment creates an attachment and saves the given file 149 func CreateReleaseAttachment(ctx *context.APIContext) { 150 // swagger:operation POST /repos/{owner}/{repo}/releases/{id}/assets repository repoCreateReleaseAttachment 151 // --- 152 // summary: Create a release attachment 153 // produces: 154 // - application/json 155 // consumes: 156 // - multipart/form-data 157 // parameters: 158 // - name: owner 159 // in: path 160 // description: owner of the repo 161 // type: string 162 // required: true 163 // - name: repo 164 // in: path 165 // description: name of the repo 166 // type: string 167 // required: true 168 // - name: id 169 // in: path 170 // description: id of the release 171 // type: integer 172 // format: int64 173 // required: true 174 // - name: name 175 // in: query 176 // description: name of the attachment 177 // type: string 178 // required: false 179 // - name: attachment 180 // in: formData 181 // description: attachment to upload 182 // type: file 183 // required: true 184 // responses: 185 // "201": 186 // "$ref": "#/responses/Attachment" 187 // "400": 188 // "$ref": "#/responses/error" 189 // "404": 190 // "$ref": "#/responses/notFound" 191 192 // Check if attachments are enabled 193 if !setting.Attachment.Enabled { 194 ctx.NotFound("Attachment is not enabled") 195 return 196 } 197 198 // Check if release exists an load release 199 releaseID := ctx.ParamsInt64(":id") 200 if !checkReleaseMatchRepo(ctx, releaseID) { 201 return 202 } 203 204 // Get uploaded file from request 205 file, header, err := ctx.Req.FormFile("attachment") 206 if err != nil { 207 ctx.Error(http.StatusInternalServerError, "GetFile", err) 208 return 209 } 210 defer file.Close() 211 212 filename := header.Filename 213 if query := ctx.FormString("name"); query != "" { 214 filename = query 215 } 216 217 // Create a new attachment and save the file 218 attach, err := attachment.UploadAttachment(file, setting.Repository.Release.AllowedTypes, header.Size, &repo_model.Attachment{ 219 Name: filename, 220 UploaderID: ctx.Doer.ID, 221 RepoID: ctx.Repo.Repository.ID, 222 ReleaseID: releaseID, 223 }) 224 if err != nil { 225 if upload.IsErrFileTypeForbidden(err) { 226 ctx.Error(http.StatusBadRequest, "DetectContentType", err) 227 return 228 } 229 ctx.Error(http.StatusInternalServerError, "NewAttachment", err) 230 return 231 } 232 233 ctx.JSON(http.StatusCreated, convert.ToAPIAttachment(ctx.Repo.Repository, attach)) 234 } 235 236 // EditReleaseAttachment updates the given attachment 237 func EditReleaseAttachment(ctx *context.APIContext) { 238 // swagger:operation PATCH /repos/{owner}/{repo}/releases/{id}/assets/{attachment_id} repository repoEditReleaseAttachment 239 // --- 240 // summary: Edit a release attachment 241 // produces: 242 // - application/json 243 // consumes: 244 // - application/json 245 // parameters: 246 // - name: owner 247 // in: path 248 // description: owner of the repo 249 // type: string 250 // required: true 251 // - name: repo 252 // in: path 253 // description: name of the repo 254 // type: string 255 // required: true 256 // - name: id 257 // in: path 258 // description: id of the release 259 // type: integer 260 // format: int64 261 // required: true 262 // - name: attachment_id 263 // in: path 264 // description: id of the attachment to edit 265 // type: integer 266 // format: int64 267 // required: true 268 // - name: body 269 // in: body 270 // schema: 271 // "$ref": "#/definitions/EditAttachmentOptions" 272 // responses: 273 // "201": 274 // "$ref": "#/responses/Attachment" 275 // "404": 276 // "$ref": "#/responses/notFound" 277 278 form := web.GetForm(ctx).(*api.EditAttachmentOptions) 279 280 // Check if release exists an load release 281 releaseID := ctx.ParamsInt64(":id") 282 if !checkReleaseMatchRepo(ctx, releaseID) { 283 return 284 } 285 286 attachID := ctx.ParamsInt64(":attachment_id") 287 attach, err := repo_model.GetAttachmentByID(ctx, attachID) 288 if err != nil { 289 if repo_model.IsErrAttachmentNotExist(err) { 290 ctx.NotFound() 291 return 292 } 293 ctx.Error(http.StatusInternalServerError, "GetAttachmentByID", err) 294 return 295 } 296 if attach.ReleaseID != releaseID { 297 log.Info("User requested attachment is not in release, release_id %v, attachment_id: %v", releaseID, attachID) 298 ctx.NotFound() 299 return 300 } 301 // FIXME Should prove the existence of the given repo, but results in unnecessary database requests 302 if form.Name != "" { 303 attach.Name = form.Name 304 } 305 306 if err := repo_model.UpdateAttachment(ctx, attach); err != nil { 307 ctx.Error(http.StatusInternalServerError, "UpdateAttachment", attach) 308 } 309 ctx.JSON(http.StatusCreated, convert.ToAPIAttachment(ctx.Repo.Repository, attach)) 310 } 311 312 // DeleteReleaseAttachment delete a given attachment 313 func DeleteReleaseAttachment(ctx *context.APIContext) { 314 // swagger:operation DELETE /repos/{owner}/{repo}/releases/{id}/assets/{attachment_id} repository repoDeleteReleaseAttachment 315 // --- 316 // summary: Delete a release attachment 317 // produces: 318 // - application/json 319 // parameters: 320 // - name: owner 321 // in: path 322 // description: owner of the repo 323 // type: string 324 // required: true 325 // - name: repo 326 // in: path 327 // description: name of the repo 328 // type: string 329 // required: true 330 // - name: id 331 // in: path 332 // description: id of the release 333 // type: integer 334 // format: int64 335 // required: true 336 // - name: attachment_id 337 // in: path 338 // description: id of the attachment to delete 339 // type: integer 340 // format: int64 341 // required: true 342 // responses: 343 // "204": 344 // "$ref": "#/responses/empty" 345 // "404": 346 // "$ref": "#/responses/notFound" 347 348 // Check if release exists an load release 349 releaseID := ctx.ParamsInt64(":id") 350 if !checkReleaseMatchRepo(ctx, releaseID) { 351 return 352 } 353 354 attachID := ctx.ParamsInt64(":attachment_id") 355 attach, err := repo_model.GetAttachmentByID(ctx, attachID) 356 if err != nil { 357 if repo_model.IsErrAttachmentNotExist(err) { 358 ctx.NotFound() 359 return 360 } 361 ctx.Error(http.StatusInternalServerError, "GetAttachmentByID", err) 362 return 363 } 364 if attach.ReleaseID != releaseID { 365 log.Info("User requested attachment is not in release, release_id %v, attachment_id: %v", releaseID, attachID) 366 ctx.NotFound() 367 return 368 } 369 // FIXME Should prove the existence of the given repo, but results in unnecessary database requests 370 371 if err := repo_model.DeleteAttachment(ctx, attach, true); err != nil { 372 ctx.Error(http.StatusInternalServerError, "DeleteAttachment", err) 373 return 374 } 375 ctx.Status(http.StatusNoContent) 376 }