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  }