Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add repo_id for attachment #16958

Merged
merged 19 commits into from
Sep 8, 2021
Merged
Show file tree
Hide file tree
Changes from 2 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
25 changes: 3 additions & 22 deletions models/attachment.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,23 @@
package models

import (
"bytes"
"fmt"
"io"
"path"

"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/storage"
"code.gitea.io/gitea/modules/timeutil"

gouuid "github.com/google/uuid"
"xorm.io/xorm"
)

// Attachment represent a attachment of issue/comment/release.
type Attachment struct {
ID int64 `xorm:"pk autoincr"`
UUID string `xorm:"uuid UNIQUE"`
IssueID int64 `xorm:"INDEX"`
ReleaseID int64 `xorm:"INDEX"`
RepoID int64 `xorm:"INDEX"` // this should not be zero
IssueID int64 `xorm:"INDEX"` // maybe zero when creating
ReleaseID int64 `xorm:"INDEX"` // maybe zero when creating
UploaderID int64 `xorm:"INDEX DEFAULT 0"` // Notice: will be zero before this column added
CommentID int64
Name string
Expand Down Expand Up @@ -81,23 +79,6 @@ func (a *Attachment) LinkedRepository() (*Repository, UnitType, error) {
return nil, -1, nil
}

// NewAttachment creates a new attachment object.
func NewAttachment(attach *Attachment, buf []byte, file io.Reader) (_ *Attachment, err error) {
attach.UUID = gouuid.New().String()

size, err := storage.Attachments.Save(attach.RelativePath(), io.MultiReader(bytes.NewReader(buf), file), -1)
if err != nil {
return nil, fmt.Errorf("Create: %v", err)
}
attach.Size = size

if _, err := x.Insert(attach); err != nil {
return nil, err
}

return attach, nil
}

// GetAttachmentByID returns attachment by given id
func GetAttachmentByID(id int64) (*Attachment, error) {
return getAttachmentByID(x, id)
Expand Down
1 change: 1 addition & 0 deletions models/attachment_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ func TestUploadAttachment(t *testing.T) {
buf = buf[:n]

attach, err := NewAttachment(&Attachment{
RepoID: 1,
UploaderID: user.ID,
Name: filepath.Base(fPath),
}, buf, f)
Expand Down
6 changes: 6 additions & 0 deletions models/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,9 @@ func Iterate(ctx DBContext, tableBean interface{}, cond builder.Cond, fun func(i
BufferSize(setting.Database.IterateBufferSize).
Iterate(tableBean, fun)
}

// Insert inserts records into database
func Insert(ctx DBContext, beans ...interface{}) error {
_, err := ctx.e.Insert(beans...)
return err
}
2 changes: 2 additions & 0 deletions models/migrations/migrations.go
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,8 @@ var migrations = []Migration{
NewMigration("Alter issue/comment table TEXT fields to LONGTEXT", alterIssueAndCommentTextFieldsToLongText),
// v192 -> v193
NewMigration("RecreateIssueResourceIndexTable to have a primary key instead of an unique index", recreateIssueResourceIndexTable),
// v193 -> v194
NewMigration("Add repo id column for attachment table", addRepoIDForAttachment),
}

// GetCurrentDBVersion returns the current db version
Expand Down
33 changes: 33 additions & 0 deletions models/migrations/v193.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package migrations

import (
"xorm.io/xorm"
)

func addRepoIDForAttachment(x *xorm.Engine) error {
type Attachment struct {
ID int64 `xorm:"pk autoincr"`
UUID string `xorm:"uuid UNIQUE"`
RepoID int64 `xorm:"INDEX"` // this should not be zero
IssueID int64 `xorm:"INDEX"` // maybe zero when creating
ReleaseID int64 `xorm:"INDEX"` // maybe zero when creating
UploaderID int64 `xorm:"INDEX DEFAULT 0"`
}
if err := x.Sync2(new(Attachment)); err != nil {
return err
}

if _, err := x.Exec("UPDATE attachment set repo_id = (SELECT repo_id FROM issue WHERE issue.id = issue_id) WHERE issue_id > 0"); err != nil {
6543 marked this conversation as resolved.
Show resolved Hide resolved
6543 marked this conversation as resolved.
Show resolved Hide resolved
return err
}

if _, err := x.Exec("UPDATE attachment set repo_id = (SELECT repo_id FROM release WHERE release.id = release_id) WHERE release_id > 0"); err != nil {
6543 marked this conversation as resolved.
Show resolved Hide resolved
return err
}

return nil
}
65 changes: 48 additions & 17 deletions models/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -530,12 +530,7 @@ func (repo *Repository) DeleteWiki() error {
}

func (repo *Repository) deleteWiki(e Engine) error {
lunny marked this conversation as resolved.
Show resolved Hide resolved
wikiPaths := []string{repo.WikiPath()}
for _, wikiPath := range wikiPaths {
removeAllWithNotice(e, "Delete repository wiki", wikiPath)
}

_, err := e.Where("repo_id = ?", repo.ID).And("type = ?", UnitTypeWiki).Delete(new(RepoUnit))
_, err := x.Where("repo_id = ?", repo.ID).And("type = ?", UnitTypeWiki).Delete(new(RepoUnit))
return err
}

Expand Down Expand Up @@ -1497,11 +1492,6 @@ func DeleteRepository(doer *User, uid, repoID int64) error {
releaseAttachments = append(releaseAttachments, attachments[i].RelativePath())
}

if _, err = sess.In("release_id", builder.Select("id").From("`release`").Where(builder.Eq{"`release`.repo_id": repoID})).
Delete(&Attachment{}); err != nil {
return err
}

if _, err := sess.Exec("UPDATE `user` SET num_stars=num_stars-1 WHERE id IN (SELECT `uid` FROM `star` WHERE repo_id = ?)", repo.ID); err != nil {
return err
}
Expand Down Expand Up @@ -1579,10 +1569,6 @@ func DeleteRepository(doer *User, uid, repoID int64) error {
}
}

// FIXME: Remove repository files should be executed after transaction succeed.
repoPath := repo.RepoPath()
removeAllWithNotice(sess, "Delete repository files", repoPath)

err = repo.deleteWiki(sess)
if err != nil {
return err
Expand All @@ -1594,6 +1580,7 @@ func DeleteRepository(doer *User, uid, repoID int64) error {
return err
}

var lfsPaths = make([]string, 0, len(lfsObjects))
for _, v := range lfsObjects {
count, err := sess.Count(&LFSMetaObject{Pointer: lfs.Pointer{Oid: v.Oid}})
if err != nil {
Expand All @@ -1603,7 +1590,7 @@ func DeleteRepository(doer *User, uid, repoID int64) error {
continue
}

removeStorageWithNotice(sess, storage.LFS, "Delete orphaned LFS file", v.RelativePath())
lfsPaths = append(lfsPaths, v.RelativePath())
}

if _, err := sess.Delete(&LFSMetaObject{RepositoryID: repoID}); err != nil {
Expand All @@ -1616,10 +1603,11 @@ func DeleteRepository(doer *User, uid, repoID int64) error {
return err
}

var archivePaths = make([]string, 0, len(archives))
for _, v := range archives {
v.Repo = repo
p, _ := v.RelativePath()
removeStorageWithNotice(sess, storage.RepoArchives, "Delete repo archive file", p)
archivePaths = append(archivePaths, p)
}

if _, err := sess.Delete(&RepoArchiver{RepoID: repoID}); err != nil {
Expand All @@ -1632,6 +1620,25 @@ func DeleteRepository(doer *User, uid, repoID int64) error {
}
}

// Get all attachments with both issue_id and release_id are zero
var newAttachments []*Attachment
if err := sess.Where(builder.Eq{
"repo_id": repo.ID,
"issue_id": 0,
"release_id": 0,
}).Find(&newAttachments); err != nil {
return err
}

var newAttachmentPaths = make([]string, 0, len(newAttachments))
for _, attach := range newAttachments {
newAttachmentPaths = append(newAttachmentPaths, attach.RelativePath())
}

if _, err := sess.Where("repo_id=?", repo.ID).Delete(new(Attachment)); err != nil {
return err
}

if err = sess.Commit(); err != nil {
return err
}
Expand All @@ -1641,6 +1648,25 @@ func DeleteRepository(doer *User, uid, repoID int64) error {
// We should always delete the files after the database transaction succeed. If
// we delete the file but the database rollback, the repository will be broken.

// Remove repository files.
repoPath := repo.RepoPath()
removeAllWithNotice(x, "Delete repository files", repoPath)

// Remove wiki files
if repo.HasWiki() {
removeAllWithNotice(x, "Delete repository wiki", repo.WikiPath())
}

// Remove archives
for i := range archivePaths {
removeStorageWithNotice(x, storage.RepoArchives, "Delete repo archive file", archivePaths[i])
}

// Remove lfs objects
for i := range lfsPaths {
removeStorageWithNotice(x, storage.LFS, "Delete orphaned LFS file", lfsPaths[i])
}

// Remove issue attachment files.
for i := range attachmentPaths {
RemoveStorageWithNotice(storage.Attachments, "Delete issue attachment", attachmentPaths[i])
Expand All @@ -1651,6 +1677,11 @@ func DeleteRepository(doer *User, uid, repoID int64) error {
RemoveStorageWithNotice(storage.Attachments, "Delete release attachment", releaseAttachments[i])
}

// Remove attachment with no issue_id and release_id.
for i := range newAttachmentPaths {
RemoveStorageWithNotice(storage.Attachments, "Delete issue attachment", attachmentPaths[i])
}

if len(repo.Avatar) > 0 {
if err := storage.RepoAvatars.Delete(repo.CustomAvatarRelativePath()); err != nil {
return fmt.Errorf("Failed to remove %s: %v", repo.Avatar, err)
Expand Down
4 changes: 3 additions & 1 deletion routers/api/v1/repo/release_attachment.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/upload"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/services/attachment"
)

// GetReleaseAttachment gets a single attachment of the release
Expand Down Expand Up @@ -195,7 +196,8 @@ func CreateReleaseAttachment(ctx *context.APIContext) {
}

// Create a new attachment and save the file
attach, err := models.NewAttachment(&models.Attachment{
attach, err := attachment.NewAttachment(&models.Attachment{
RepoID: release.RepoID,
UploaderID: ctx.User.ID,
Name: filename,
ReleaseID: release.ID,
Expand Down
10 changes: 6 additions & 4 deletions routers/web/repo/attachment.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,21 @@ import (
"code.gitea.io/gitea/modules/storage"
"code.gitea.io/gitea/modules/upload"
"code.gitea.io/gitea/routers/common"
"code.gitea.io/gitea/services/attachment"
)

// UploadIssueAttachment response for Issue/PR attachments
func UploadIssueAttachment(ctx *context.Context) {
uploadAttachment(ctx, setting.Attachment.AllowedTypes)
uploadAttachment(ctx, ctx.Repo.Repository.ID, setting.Attachment.AllowedTypes)
}

// UploadReleaseAttachment response for uploading release attachments
func UploadReleaseAttachment(ctx *context.Context) {
uploadAttachment(ctx, setting.Repository.Release.AllowedTypes)
uploadAttachment(ctx, ctx.Repo.Repository.ID, setting.Repository.Release.AllowedTypes)
}

// UploadAttachment response for uploading attachments
func uploadAttachment(ctx *context.Context, allowedTypes string) {
func uploadAttachment(ctx *context.Context, repoID int64, allowedTypes string) {
if !setting.Attachment.Enabled {
6543 marked this conversation as resolved.
Show resolved Hide resolved
ctx.Error(http.StatusNotFound, "attachment is not enabled")
return
Expand All @@ -54,7 +55,8 @@ func uploadAttachment(ctx *context.Context, allowedTypes string) {
return
}

attach, err := models.NewAttachment(&models.Attachment{
attach, err := attachment.NewAttachment(&models.Attachment{
RepoID: repoID,
UploaderID: ctx.User.ID,
Name: header.Filename,
}, buf, file)
Expand Down
3 changes: 2 additions & 1 deletion routers/web/repo/setting.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import (
"code.gitea.io/gitea/services/mailer"
mirror_service "code.gitea.io/gitea/services/mirror"
repo_service "code.gitea.io/gitea/services/repository"
wiki_service "code.gitea.io/gitea/services/wiki"
)

const (
Expand Down Expand Up @@ -666,7 +667,7 @@ func SettingsPost(ctx *context.Context) {
return
}

err := repo.DeleteWiki()
err := wiki_service.DeleteWiki(repo)
if err != nil {
log.Error("Delete Wiki: %v", err.Error())
}
Expand Down
36 changes: 36 additions & 0 deletions services/attachment/attachment.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package attachment

import (
"bytes"
"fmt"
"io"

"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/storage"

"github.com/google/uuid"
)

// NewAttachment creates a new attachment object.
func NewAttachment(attach *models.Attachment, buf []byte, file io.Reader) (*models.Attachment, error) {
if attach.RepoID == 0 {
return nil, fmt.Errorf("attachment %s should belong to a repository", attach.Name)
}

err := models.WithTx(func(ctx models.DBContext) error {
attach.UUID = uuid.New().String()
size, err := storage.Attachments.Save(attach.RelativePath(), io.MultiReader(bytes.NewReader(buf), file), -1)
if err != nil {
return fmt.Errorf("Create: %v", err)
}
attach.Size = size

return models.Insert(ctx, attach)
})

return attach, err
}
10 changes: 10 additions & 0 deletions services/wiki/wiki.go
Original file line number Diff line number Diff line change
Expand Up @@ -366,3 +366,13 @@ func DeleteWikiPage(doer *models.User, repo *models.Repository, wikiName string)

return nil
}

// DeleteWiki removes the actual and local copy of repository wiki.
func DeleteWiki(repo *models.Repository) error {
if err := repo.DeleteWiki(); err != nil {
lunny marked this conversation as resolved.
Show resolved Hide resolved
return err
}

models.RemoveAllWithNotice("Delete repository wiki", repo.WikiPath())
return nil
}