diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini index 4df50f5cc6c05..60b07c03a73e5 100644 --- a/custom/conf/app.example.ini +++ b/custom/conf/app.example.ini @@ -1189,6 +1189,17 @@ LEVEL = Info ;; ;; Max number of files per upload. Defaults to 5 ;MAX_FILES = 5 +;; +;; Enable attachment storage +;ENABLE_ATTACHMENT = `[attachment].ENABLED` + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;[repository.release.attachment] +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; The config options are same as [attachment], if this section not set, +;; it will use the storage related configs in [attachment] section. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/models/repo/attachment.go b/models/repo/attachment.go index 27856f2d2e2d6..6ed2cd2e0f78f 100644 --- a/models/repo/attachment.go +++ b/models/repo/attachment.go @@ -99,6 +99,18 @@ func GetAttachmentByID(ctx context.Context, id int64) (*Attachment, error) { return attach, nil } +func GetReleaseAttachmentByID(ctx context.Context, releaseID, id int64) (*Attachment, error) { + attach := &Attachment{} + + if has, err := db.GetEngine(ctx).Where("`id` = ? AND `release_id` = ?", id, releaseID).Get(&attach); err != nil { + return nil, err + } else if !has { + return nil, ErrAttachmentNotExist{ID: id, UUID: ""} + } + + return attach, nil +} + // GetAttachmentByUUID returns attachment by given UUID. func GetAttachmentByUUID(ctx context.Context, uuid string) (*Attachment, error) { attach := &Attachment{} @@ -179,12 +191,44 @@ func DeleteAttachment(ctx context.Context, a *Attachment, remove bool) error { // DeleteAttachments deletes the given attachments and optionally the associated files. func DeleteAttachments(ctx context.Context, attachments []*Attachment, remove bool) (int, error) { + return db.WithTx2(ctx, func(ctx context.Context) (int, error) { + return deleteAttachmentsOrReleaseAttachments(ctx, attachments, 0, remove) + }) +} + +func DeleteAttachmentOrReleaseAttachment(ctx context.Context, a *Attachment, remove bool) (err error) { + if a.ReleaseID > 0 { + _, err = db.WithTx2(ctx, func(ctx context.Context) (int, error) { + return deleteAttachmentsOrReleaseAttachments(ctx, []*Attachment{a}, a.ReleaseID, remove) + }) + } else { + _, err = db.WithTx2(ctx, func(ctx context.Context) (int, error) { + return deleteAttachmentsOrReleaseAttachments(ctx, []*Attachment{a}, 0, remove) + }) + } + + return err +} + +// DeleteReleaseAttachments +func DeleteReleaseAttachments(ctx context.Context, assets []*Attachment, releaseID int64, remove bool) (int, error) { + return db.WithTx2(ctx, func(ctx context.Context) (int, error) { + return deleteAttachmentsOrReleaseAttachments(ctx, assets, releaseID, remove) + }) +} + +// deleteAttachmentsOrReleaseAttachments deletes the given attachments and optionally the associated files. +func deleteAttachmentsOrReleaseAttachments(ctx context.Context, attachments []*Attachment, releaseID int64, remove bool) (int, error) { if len(attachments) == 0 { return 0, nil } ids := make([]int64, 0, len(attachments)) for _, a := range attachments { + if a.ReleaseID != releaseID { + return 0, ErrAttachmentNotExist{0, a.UUID} + } + ids = append(ids, a.ID) } @@ -195,7 +239,15 @@ func DeleteAttachments(ctx context.Context, attachments []*Attachment, remove bo if remove { for i, a := range attachments { - if err := storage.Attachments.Delete(a.RelativePath()); err != nil { + var storageChoice storage.ObjectStorage + + if a.ReleaseID > 0 { + storageChoice = storage.ReleaseAttachments + } else { + storageChoice = storage.Attachments + } + + if err := storageChoice.Delete(a.RelativePath()); err != nil { if !errors.Is(err, os.ErrNotExist) { return i, err } diff --git a/modules/setting/repository.go b/modules/setting/repository.go index 9195b7ee5035d..e14ec832f9623 100644 --- a/modules/setting/repository.go +++ b/modules/setting/repository.go @@ -98,10 +98,12 @@ var ( } `ini:"repository.issue"` Release struct { - AllowedTypes string - DefaultPagingNum int - FileMaxSize int64 - MaxFiles int64 + AllowedTypes string + DefaultPagingNum int + FileMaxSize int64 + MaxFiles int64 + EnableAttachment bool + AttachmentStorage *Storage `ini:"-"` } `ini:"repository.release"` Signing struct { @@ -241,15 +243,18 @@ var ( }, Release: struct { - AllowedTypes string - DefaultPagingNum int - FileMaxSize int64 - MaxFiles int64 + AllowedTypes string + DefaultPagingNum int + FileMaxSize int64 + MaxFiles int64 + EnableAttachment bool + AttachmentStorage *Storage `ini:"-"` }{ AllowedTypes: "", DefaultPagingNum: 10, FileMaxSize: 2048, MaxFiles: 5, + EnableAttachment: true, }, // Signing settings @@ -369,4 +374,24 @@ func loadRepositoryFrom(rootCfg ConfigProvider) { if err := loadRepoArchiveFrom(rootCfg); err != nil { log.Fatal("loadRepoArchiveFrom: %v", err) } + + if err := loadReleaseAttachmentsStorageFrom(rootCfg); err != nil { + log.Fatal("loadReleaseStorageFrom: %v", err) + } +} + +func loadReleaseAttachmentsStorageFrom(rootCfg ConfigProvider) (err error) { + if !Repository.Release.EnableAttachment { + return nil + } + + sec, _ := rootCfg.GetSection("repository.release.attachment") + if sec == nil { + Repository.Release.EnableAttachment = Attachment.Enabled + Repository.Release.AttachmentStorage = Attachment.Storage + return err + } + + Repository.Release.AttachmentStorage, err = getStorage(rootCfg, "release_attachments", "", sec) + return err } diff --git a/modules/storage/storage.go b/modules/storage/storage.go index e19c421ba826b..ed60ba33338ab 100644 --- a/modules/storage/storage.go +++ b/modules/storage/storage.go @@ -180,6 +180,9 @@ var ( Actions ObjectStorage = uninitializedStorage // ActionsArtifacts Artifacts represents actions artifacts storage ActionsArtifacts ObjectStorage = uninitializedStorage + + // ReleaseAttachments represents release attachments storage + ReleaseAttachments ObjectStorage = uninitializedStorage ) // Init init the storage @@ -192,6 +195,7 @@ func Init() error { initRepoArchives, initPackages, initActions, + InitReleaseAttachmentsStorage, } { if err := f(); err != nil { return err @@ -229,6 +233,23 @@ func initAttachments() (err error) { return err } +func InitReleaseAttachmentsStorage() (err error) { + if !setting.Repository.Release.EnableAttachment { + ReleaseAttachments = discardStorage("Release storage isn't enabled") + return nil + } + + if setting.Repository.Release.AttachmentStorage == setting.Attachment.Storage { + ReleaseAttachments = Attachments + log.Info("Release attachments storage is the same as Attachment storage, no need to initialize a new one") + return nil + } + + log.Info("Initialising Release attachments storage with type: %s", setting.Repository.Release.AttachmentStorage.Type) + ReleaseAttachments, err = NewStorage(setting.Repository.Release.AttachmentStorage.Type, setting.Repository.Release.AttachmentStorage) + return err +} + func initLFS() (err error) { if !setting.LFS.StartServer { LFS = discardStorage("LFS isn't enabled") diff --git a/routers/api/v1/repo/issue_attachment.go b/routers/api/v1/repo/issue_attachment.go index bfe9c92f1cc00..1a7c8acb63f51 100644 --- a/routers/api/v1/repo/issue_attachment.go +++ b/routers/api/v1/repo/issue_attachment.go @@ -186,12 +186,12 @@ func CreateIssueAttachment(ctx *context.APIContext) { } uploaderFile := attachment_service.NewLimitedUploaderKnownSize(file, header.Size) - attachment, err := attachment_service.UploadAttachmentGeneralSizeLimit(ctx, uploaderFile, setting.Attachment.AllowedTypes, &repo_model.Attachment{ + attachment, err := attachment_service.UploadAttachmentOrReleaseAttachmentSizeLimit(ctx, uploaderFile, &repo_model.Attachment{ Name: filename, UploaderID: ctx.Doer.ID, RepoID: ctx.Repo.Repository.ID, IssueID: issue.ID, - }) + }, false) if err != nil { if upload.IsErrFileTypeForbidden(err) { ctx.APIError(http.StatusUnprocessableEntity, err) diff --git a/routers/api/v1/repo/issue_comment_attachment.go b/routers/api/v1/repo/issue_comment_attachment.go index 3227f5ddee467..27139e46cd95a 100644 --- a/routers/api/v1/repo/issue_comment_attachment.go +++ b/routers/api/v1/repo/issue_comment_attachment.go @@ -193,13 +193,13 @@ func CreateIssueCommentAttachment(ctx *context.APIContext) { } uploaderFile := attachment_service.NewLimitedUploaderKnownSize(file, header.Size) - attachment, err := attachment_service.UploadAttachmentGeneralSizeLimit(ctx, uploaderFile, setting.Attachment.AllowedTypes, &repo_model.Attachment{ + attachment, err := attachment_service.UploadAttachmentOrReleaseAttachmentSizeLimit(ctx, uploaderFile, &repo_model.Attachment{ Name: filename, UploaderID: ctx.Doer.ID, RepoID: ctx.Repo.Repository.ID, IssueID: comment.IssueID, CommentID: comment.ID, - }) + }, false) if err != nil { if upload.IsErrFileTypeForbidden(err) { ctx.APIError(http.StatusUnprocessableEntity, err) diff --git a/routers/api/v1/repo/release_attachment.go b/routers/api/v1/repo/release_attachment.go index 19075961f313b..b2a1b06c39b3c 100644 --- a/routers/api/v1/repo/release_attachment.go +++ b/routers/api/v1/repo/release_attachment.go @@ -9,7 +9,6 @@ import ( "strings" repo_model "code.gitea.io/gitea/models/repo" - "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" @@ -83,7 +82,7 @@ func GetReleaseAttachment(ctx *context.APIContext) { } attachID := ctx.PathParamInt64("attachment_id") - attach, err := repo_model.GetAttachmentByID(ctx, attachID) + attach, err := repo_model.GetReleaseAttachmentByID(ctx, releaseID, attachID) if err != nil { if repo_model.IsErrAttachmentNotExist(err) { ctx.APIErrorNotFound() @@ -92,11 +91,6 @@ func GetReleaseAttachment(ctx *context.APIContext) { ctx.APIErrorInternal(err) return } - if attach.ReleaseID != releaseID { - log.Info("User requested attachment is not in release, release_id %v, attachment_id: %v", releaseID, attachID) - ctx.APIErrorNotFound() - return - } // FIXME Should prove the existence of the given repo, but results in unnecessary database requests ctx.JSON(http.StatusOK, convert.ToAPIAttachment(ctx.Repo.Repository, attach)) } @@ -204,8 +198,8 @@ func CreateReleaseAttachment(ctx *context.APIContext) { // "$ref": "#/responses/error" // Check if attachments are enabled - if !setting.Attachment.Enabled { - ctx.APIErrorNotFound("Attachment is not enabled") + if !setting.Repository.Release.EnableAttachment { + ctx.APIErrorNotFound("Release attachment is not enabled") return } @@ -242,12 +236,12 @@ func CreateReleaseAttachment(ctx *context.APIContext) { } // Create a new attachment and save the file - attach, err := attachment_service.UploadAttachmentReleaseSizeLimit(ctx, uploaderFile, setting.Repository.Release.AllowedTypes, &repo_model.Attachment{ + attach, err := attachment_service.UploadAttachmentOrReleaseAttachmentSizeLimit(ctx, uploaderFile, &repo_model.Attachment{ Name: filename, UploaderID: ctx.Doer.ID, RepoID: ctx.Repo.Repository.ID, ReleaseID: releaseID, - }) + }, true) if err != nil { if upload.IsErrFileTypeForbidden(err) { ctx.APIError(http.StatusBadRequest, err) @@ -319,7 +313,7 @@ func EditReleaseAttachment(ctx *context.APIContext) { } attachID := ctx.PathParamInt64("attachment_id") - attach, err := repo_model.GetAttachmentByID(ctx, attachID) + attach, err := repo_model.GetReleaseAttachmentByID(ctx, releaseID, attachID) if err != nil { if repo_model.IsErrAttachmentNotExist(err) { ctx.APIErrorNotFound() @@ -328,12 +322,9 @@ func EditReleaseAttachment(ctx *context.APIContext) { ctx.APIErrorInternal(err) return } - if attach.ReleaseID != releaseID { - log.Info("User requested attachment is not in release, release_id %v, attachment_id: %v", releaseID, attachID) - ctx.APIErrorNotFound() - return - } + // FIXME Should prove the existence of the given repo, but results in unnecessary database requests + if form.Name != "" { attach.Name = form.Name } @@ -392,7 +383,7 @@ func DeleteReleaseAttachment(ctx *context.APIContext) { } attachID := ctx.PathParamInt64("attachment_id") - attach, err := repo_model.GetAttachmentByID(ctx, attachID) + attach, err := repo_model.GetReleaseAttachmentByID(ctx, releaseID, attachID) if err != nil { if repo_model.IsErrAttachmentNotExist(err) { ctx.APIErrorNotFound() @@ -401,11 +392,6 @@ func DeleteReleaseAttachment(ctx *context.APIContext) { ctx.APIErrorInternal(err) return } - if attach.ReleaseID != releaseID { - log.Info("User requested attachment is not in release, release_id %v, attachment_id: %v", releaseID, attachID) - ctx.APIErrorNotFound() - return - } if err := repo_model.DeleteAttachment(ctx, attach, true); err != nil { ctx.APIErrorInternal(err) diff --git a/routers/web/repo/attachment.go b/routers/web/repo/attachment.go index 8b35f52ed6ca2..f080da0f54918 100644 --- a/routers/web/repo/attachment.go +++ b/routers/web/repo/attachment.go @@ -23,21 +23,26 @@ import ( // UploadIssueAttachment response for Issue/PR attachments func UploadIssueAttachment(ctx *context.Context) { - uploadAttachment(ctx, ctx.Repo.Repository.ID, setting.Attachment.AllowedTypes) + if !setting.Attachment.Enabled { + ctx.HTTPError(http.StatusNotFound, "attachment is not enabled") + return + } + + uploadAttachmentOrReleaseAttachment(ctx, ctx.Repo.Repository.ID, false) } // UploadReleaseAttachment response for uploading release attachments func UploadReleaseAttachment(ctx *context.Context) { - uploadAttachment(ctx, ctx.Repo.Repository.ID, setting.Repository.Release.AllowedTypes) -} - -// UploadAttachment response for uploading attachments -func uploadAttachment(ctx *context.Context, repoID int64, allowedTypes string) { - if !setting.Attachment.Enabled { - ctx.HTTPError(http.StatusNotFound, "attachment is not enabled") + if !setting.Repository.Release.EnableAttachment { + ctx.HTTPError(http.StatusNotFound, "release asset storage is not enabled") return } + uploadAttachmentOrReleaseAttachment(ctx, ctx.Repo.Repository.ID, true) +} + +// UploadAttachment response for uploading attachments +func uploadAttachmentOrReleaseAttachment(ctx *context.Context, repoID int64, isRelease bool) { file, header, err := ctx.Req.FormFile("file") if err != nil { ctx.ServerError("FormFile", err) @@ -46,11 +51,11 @@ func uploadAttachment(ctx *context.Context, repoID int64, allowedTypes string) { defer file.Close() uploaderFile := attachment.NewLimitedUploaderKnownSize(file, header.Size) - attach, err := attachment.UploadAttachmentReleaseSizeLimit(ctx, uploaderFile, allowedTypes, &repo_model.Attachment{ + attach, err := attachment.UploadAttachmentOrReleaseAttachmentSizeLimit(ctx, uploaderFile, &repo_model.Attachment{ Name: header.Filename, UploaderID: ctx.Doer.ID, RepoID: repoID, - }) + }, isRelease) if err != nil { if upload.IsErrFileTypeForbidden(err) { ctx.HTTPError(http.StatusBadRequest, err.Error()) @@ -66,8 +71,8 @@ func uploadAttachment(ctx *context.Context, repoID int64, allowedTypes string) { }) } -// DeleteAttachment response for deleting issue's attachment -func DeleteAttachment(ctx *context.Context) { +// DeleteAttachmentOrReleaseAttachment response for deleting issue's attachment or release asset +func DeleteAttachmentOrReleaseAttachment(ctx *context.Context) { file := ctx.FormString("file") attach, err := repo_model.GetAttachmentByUUID(ctx, file) if err != nil { @@ -109,7 +114,7 @@ func DeleteAttachment(ctx *context.Context) { } } - err = repo_model.DeleteAttachment(ctx, attach, true) + err = repo_model.DeleteAttachmentOrReleaseAttachment(ctx, attach, true) if err != nil { ctx.ServerError("DeleteAttachment", err) return @@ -177,9 +182,30 @@ func ServeAttachment(ctx *context.Context, uuid string) { return } - if setting.Attachment.Storage.ServeDirect() { + var ( + storageSetting *setting.Storage + storageChoice storage.ObjectStorage + storageEnabled bool + ) + + if attach.ReleaseID > 0 { + storageSetting = setting.Repository.Release.AttachmentStorage + storageChoice = storage.ReleaseAttachments + storageEnabled = setting.Repository.Release.EnableAttachment + } else { + storageSetting = setting.Attachment.Storage + storageChoice = storage.Attachments + storageEnabled = setting.Attachment.Enabled + } + + if !storageEnabled { + ctx.NotFound(nil) + return + } + + if storageSetting.ServeDirect() { // If we have a signed url (S3, object storage), redirect to this directly. - u, err := storage.Attachments.ServeDirectURL(attach.RelativePath(), attach.Name, ctx.Req.Method, nil) + u, err := storageChoice.ServeDirectURL(attach.RelativePath(), attach.Name, ctx.Req.Method, nil) if u != nil && err == nil { ctx.Redirect(u.String()) @@ -192,7 +218,7 @@ func ServeAttachment(ctx *context.Context, uuid string) { } // If we have matched and access to release or issue - fr, err := storage.Attachments.Open(attach.RelativePath()) + fr, err := storageChoice.Open(attach.RelativePath()) if err != nil { ctx.ServerError("Open", err) return diff --git a/routers/web/repo/release.go b/routers/web/repo/release.go index 005106a32d8cc..74dbfb72c99e8 100644 --- a/routers/web/repo/release.go +++ b/routers/web/repo/release.go @@ -342,7 +342,7 @@ func newReleaseCommon(ctx *context.Context) { } ctx.Data["Tags"] = tags - ctx.Data["IsAttachmentEnabled"] = setting.Attachment.Enabled + ctx.Data["IsAttachmentEnabled"] = setting.Repository.Release.EnableAttachment assigneeUsers, err := repo_model.GetRepoAssignees(ctx, ctx.Repo.Repository) if err != nil { ctx.ServerError("GetRepoAssignees", err) @@ -495,7 +495,7 @@ func NewReleasePost(ctx *context.Context) { return } - attachmentUUIDs := util.Iif(setting.Attachment.Enabled, form.Files, nil) + attachmentUUIDs := util.Iif(setting.Repository.Release.EnableAttachment, form.Files, nil) // no existing release, create a new release if rel == nil { @@ -631,7 +631,7 @@ func EditReleasePost(ctx *context.Context) { const editPrefix = "attachment-edit-" var addAttachmentUUIDs, delAttachmentUUIDs []string editAttachments := make(map[string]string) // uuid -> new name - if setting.Attachment.Enabled { + if setting.Repository.Release.EnableAttachment { addAttachmentUUIDs = form.Files for k, v := range ctx.Req.Form { if strings.HasPrefix(k, delPrefix) && v[0] == "true" { diff --git a/routers/web/web.go b/routers/web/web.go index e3dcf27cc4afe..fbe9762330176 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -1345,7 +1345,7 @@ func registerWebRoutes(m *web.Router, webAuth *AuthMiddleware) { }) m.Post("/attachments", repo.UploadIssueAttachment) - m.Post("/attachments/remove", repo.DeleteAttachment) + m.Post("/attachments/remove", repo.DeleteAttachmentOrReleaseAttachment) m.Post("/labels", reqRepoIssuesOrPullsWriter, repo.UpdateIssueLabel) m.Post("/milestone", reqRepoIssuesOrPullsWriter, repo.UpdateIssueMilestone) @@ -1470,7 +1470,7 @@ func registerWebRoutes(m *web.Router, webAuth *AuthMiddleware) { m.Post("/generate-notes", web.Bind(forms.GenerateReleaseNotesForm{}), repo.GenerateReleaseNotes) m.Post("/delete", repo.DeleteRelease) m.Post("/attachments", repo.UploadReleaseAttachment) - m.Post("/attachments/remove", repo.DeleteAttachment) + m.Post("/attachments/remove", repo.DeleteAttachmentOrReleaseAttachment) }, reqSignIn, context.RepoMustNotBeArchived(), reqRepoReleaseWriter) m.Group("/releases", func() { m.Get("/edit/*", repo.EditRelease) diff --git a/services/attachment/attachment.go b/services/attachment/attachment.go index d69253dd5977e..6c371f894319d 100644 --- a/services/attachment/attachment.go +++ b/services/attachment/attachment.go @@ -21,15 +21,23 @@ import ( "github.com/google/uuid" ) -// NewAttachment creates a new attachment object, but do not verify. -func NewAttachment(ctx context.Context, attach *repo_model.Attachment, file io.Reader, size int64) (*repo_model.Attachment, error) { +// NewAttachmentOrReleaseAttachment creates a new attachment object, but do not verify. +func NewAttachmentOrReleaseAttachment(ctx context.Context, attach *repo_model.Attachment, file io.Reader, size int64, isRelease bool) (*repo_model.Attachment, error) { if attach.RepoID == 0 { return nil, fmt.Errorf("attachment %s should belong to a repository", attach.Name) } err := db.WithTx(ctx, func(ctx context.Context) error { + var storageChoice storage.ObjectStorage + + if isRelease { + storageChoice = storage.ReleaseAttachments + } else { + storageChoice = storage.Attachments + } + attach.UUID = uuid.New().String() - size, err := storage.Attachments.Save(attach.RelativePath(), file, size) + size, err := storageChoice.Save(attach.RelativePath(), file, size) if err != nil { return fmt.Errorf("Attachments.Save: %w", err) } @@ -54,15 +62,15 @@ func NewLimitedUploaderMaxBytesReader(r io.ReadCloser, w http.ResponseWriter) *U return &UploaderFile{rd: r, size: -1, respWriter: w} } -func UploadAttachmentGeneralSizeLimit(ctx context.Context, file *UploaderFile, allowedTypes string, attach *repo_model.Attachment) (*repo_model.Attachment, error) { - return uploadAttachment(ctx, file, allowedTypes, setting.Attachment.MaxSize<<20, attach) -} +func UploadAttachmentOrReleaseAttachmentSizeLimit(ctx context.Context, file *UploaderFile, attach *repo_model.Attachment, isRelease bool) (*repo_model.Attachment, error) { + if isRelease { + return uploadAttachmentOrReleaseAttachment(ctx, file, setting.Repository.Release.AllowedTypes, setting.Repository.Release.FileMaxSize<<20, attach, isRelease) + } -func UploadAttachmentReleaseSizeLimit(ctx context.Context, file *UploaderFile, allowedTypes string, attach *repo_model.Attachment) (*repo_model.Attachment, error) { - return uploadAttachment(ctx, file, allowedTypes, setting.Repository.Release.FileMaxSize<<20, attach) + return uploadAttachmentOrReleaseAttachment(ctx, file, setting.Attachment.AllowedTypes, setting.Attachment.MaxSize<<20, attach, isRelease) } -func uploadAttachment(ctx context.Context, file *UploaderFile, allowedTypes string, maxFileSize int64, attach *repo_model.Attachment) (*repo_model.Attachment, error) { +func uploadAttachmentOrReleaseAttachment(ctx context.Context, file *UploaderFile, allowedTypes string, maxFileSize int64, attach *repo_model.Attachment, isRelease bool) (*repo_model.Attachment, error) { src := file.rd if file.size < 0 { src = http.MaxBytesReader(file.respWriter, src, maxFileSize) @@ -79,7 +87,7 @@ func uploadAttachment(ctx context.Context, file *UploaderFile, allowedTypes stri return nil, util.ErrorWrap(util.ErrContentTooLarge, "attachment exceeds limit %d", maxFileSize) } - attach, err := NewAttachment(ctx, attach, io.MultiReader(bytes.NewReader(buf), src), file.size) + attach, err := NewAttachmentOrReleaseAttachment(ctx, attach, io.MultiReader(bytes.NewReader(buf), src), file.size, isRelease) var maxBytesError *http.MaxBytesError if errors.As(err, &maxBytesError) { return nil, util.ErrorWrap(util.ErrContentTooLarge, "attachment exceeds limit %d", maxFileSize) diff --git a/services/attachment/attachment_test.go b/services/attachment/attachment_test.go index 8ecac8d7a33ec..7a1b3a0bc0ff0 100644 --- a/services/attachment/attachment_test.go +++ b/services/attachment/attachment_test.go @@ -31,11 +31,11 @@ func TestUploadAttachment(t *testing.T) { assert.NoError(t, err) defer f.Close() - attach, err := NewAttachment(t.Context(), &repo_model.Attachment{ + attach, err := NewAttachmentOrReleaseAttachment(t.Context(), &repo_model.Attachment{ RepoID: 1, UploaderID: user.ID, Name: filepath.Base(fPath), - }, f, -1) + }, f, -1, false) assert.NoError(t, err) attachment, err := repo_model.GetAttachmentByUUID(t.Context(), attach.UUID) diff --git a/services/mailer/incoming/incoming_handler.go b/services/mailer/incoming/incoming_handler.go index dfb7b1244cf40..422ffec979bea 100644 --- a/services/mailer/incoming/incoming_handler.go +++ b/services/mailer/incoming/incoming_handler.go @@ -88,11 +88,11 @@ func (h *ReplyHandler) Handle(ctx context.Context, content *MailContent, doer *u for _, attachment := range content.Attachments { attachmentBuf := bytes.NewReader(attachment.Content) uploaderFile := attachment_service.NewLimitedUploaderKnownSize(attachmentBuf, attachmentBuf.Size()) - a, err := attachment_service.UploadAttachmentGeneralSizeLimit(ctx, uploaderFile, setting.Attachment.AllowedTypes, &repo_model.Attachment{ + a, err := attachment_service.UploadAttachmentOrReleaseAttachmentSizeLimit(ctx, uploaderFile, &repo_model.Attachment{ Name: attachment.Name, UploaderID: doer.ID, RepoID: issue.Repo.ID, - }) + }, false) if err != nil { if upload.IsErrFileTypeForbidden(err) { log.Info("Skipping disallowed attachment type: %s", attachment.Name) diff --git a/services/mailer/mail_test.go b/services/mailer/mail_test.go index caa072725a4e9..ff7a41ce33c3f 100644 --- a/services/mailer/mail_test.go +++ b/services/mailer/mail_test.go @@ -75,22 +75,22 @@ func prepareMailerBase64Test(t *testing.T) (doer *user_model.User, repo *repo_mo user, repo, issue, comment := prepareMailerTest(t) setting.MailService.EmbedAttachmentImages = true - att1, err := attachment.NewAttachment(t.Context(), &repo_model.Attachment{ + att1, err := attachment.NewAttachmentOrReleaseAttachment(t.Context(), &repo_model.Attachment{ RepoID: repo.ID, IssueID: issue.ID, UploaderID: user.ID, CommentID: comment.ID, Name: "test.png", - }, bytes.NewReader([]byte("\x89\x50\x4e\x47\x0d\x0a\x1a\x0a")), 8) + }, bytes.NewReader([]byte("\x89\x50\x4e\x47\x0d\x0a\x1a\x0a")), 8, false) require.NoError(t, err) - att2, err = attachment.NewAttachment(t.Context(), &repo_model.Attachment{ + att2, err = attachment.NewAttachmentOrReleaseAttachment(t.Context(), &repo_model.Attachment{ RepoID: repo.ID, IssueID: issue.ID, UploaderID: user.ID, CommentID: comment.ID, Name: "test.png", - }, bytes.NewReader([]byte("\x89\x50\x4e\x47\x0d\x0a\x1a\x0a"+strings.Repeat("\x00", 1024))), 8+1024) + }, bytes.NewReader([]byte("\x89\x50\x4e\x47\x0d\x0a\x1a\x0a"+strings.Repeat("\x00", 1024))), 8+1024, false) require.NoError(t, err) return user, repo, issue, att1, att2 diff --git a/services/release/release.go b/services/release/release.go index a482501164019..618a18cf0d8c2 100644 --- a/services/release/release.go +++ b/services/release/release.go @@ -297,8 +297,8 @@ func UpdateRelease(ctx context.Context, doer *user_model.User, gitRepo *git.Repo deletedUUIDs.Add(attach.UUID) } - if _, err := repo_model.DeleteAttachments(ctx, attachments, true); err != nil { - return fmt.Errorf("DeleteAttachments [uuids: %v]: %w", delAttachmentUUIDs, err) + if _, err := repo_model.DeleteReleaseAttachments(ctx, attachments, rel.ID, true); err != nil { + return fmt.Errorf("DeleteReleaseAttachments [uuids: %v]: %w", delAttachmentUUIDs, err) } } diff --git a/services/release/release_test.go b/services/release/release_test.go index ef495632a757c..2a6833ccc2b75 100644 --- a/services/release/release_test.go +++ b/services/release/release_test.go @@ -105,11 +105,11 @@ func TestRelease_Create(t *testing.T) { testPlayload := "testtest" - attach, err := attachment.NewAttachment(t.Context(), &repo_model.Attachment{ + attach, err := attachment.NewAttachmentOrReleaseAttachment(t.Context(), &repo_model.Attachment{ RepoID: repo.ID, UploaderID: user.ID, Name: "test.txt", - }, strings.NewReader(testPlayload), int64(len([]byte(testPlayload)))) + }, strings.NewReader(testPlayload), int64(len([]byte(testPlayload))), true) assert.NoError(t, err) release := repo_model.Release{ @@ -238,11 +238,11 @@ func TestRelease_Update(t *testing.T) { // Add new attachments samplePayload := "testtest" - attach, err := attachment.NewAttachment(t.Context(), &repo_model.Attachment{ + attach, err := attachment.NewAttachmentOrReleaseAttachment(t.Context(), &repo_model.Attachment{ RepoID: repo.ID, UploaderID: user.ID, Name: "test.txt", - }, strings.NewReader(samplePayload), int64(len([]byte(samplePayload)))) + }, strings.NewReader(samplePayload), int64(len([]byte(samplePayload))), true) assert.NoError(t, err) assert.NoError(t, UpdateRelease(t.Context(), user, gitRepo, release, []string{attach.UUID}, nil, nil))