Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions custom/conf/app.example.ini
Original file line number Diff line number Diff line change
Expand Up @@ -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.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Expand Down
54 changes: 53 additions & 1 deletion models/repo/attachment.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{}
Expand Down Expand Up @@ -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)
}

Expand All @@ -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
}
Expand Down
41 changes: 33 additions & 8 deletions modules/setting/repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
}
21 changes: 21 additions & 0 deletions modules/storage/storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -192,6 +195,7 @@ func Init() error {
initRepoArchives,
initPackages,
initActions,
InitReleaseAttachmentsStorage,
} {
if err := f(); err != nil {
return err
Expand Down Expand Up @@ -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")
Expand Down
4 changes: 2 additions & 2 deletions routers/api/v1/repo/issue_attachment.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
4 changes: 2 additions & 2 deletions routers/api/v1/repo/issue_comment_attachment.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
32 changes: 9 additions & 23 deletions routers/api/v1/repo/release_attachment.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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()
Expand All @@ -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))
}
Expand Down Expand Up @@ -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
}

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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()
Expand All @@ -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
}
Expand Down Expand Up @@ -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()
Expand All @@ -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)
Expand Down
Loading
Loading