@@ -18,18 +18,22 @@ import (
1818
1919// Attachment represent a attachment of issue/comment/release.
2020type Attachment struct {
21- ID int64 `xorm:"pk autoincr"`
22- UUID string `xorm:"uuid UNIQUE"`
23- RepoID int64 `xorm:"INDEX"` // this should not be zero
24- IssueID int64 `xorm:"INDEX"` // maybe zero when creating
25- ReleaseID int64 `xorm:"INDEX"` // maybe zero when creating
26- UploaderID int64 `xorm:"INDEX DEFAULT 0"` // Notice: will be zero before this column added
27- CommentID int64 `xorm:"INDEX"`
28- Name string
29- DownloadCount int64 `xorm:"DEFAULT 0"`
30- Size int64 `xorm:"DEFAULT 0"`
31- CreatedUnix timeutil.TimeStamp `xorm:"created"`
32- CustomDownloadURL string `xorm:"-"`
21+ ID int64 `xorm:"pk autoincr"`
22+ UUID string `xorm:"uuid UNIQUE"`
23+ RepoID int64 `xorm:"INDEX"` // this should not be zero
24+ IssueID int64 `xorm:"INDEX"` // maybe zero when creating
25+ ReleaseID int64 `xorm:"INDEX"` // maybe zero when creating
26+ UploaderID int64 `xorm:"INDEX DEFAULT 0"` // Notice: will be zero before this column added
27+ CommentID int64 `xorm:"INDEX"`
28+ Name string
29+ DownloadCount int64 `xorm:"DEFAULT 0"`
30+ Status db.FileStatus `xorm:"INDEX DEFAULT 0"`
31+ DeleteFailedCount int `xorm:"DEFAULT 0"` // Number of times the deletion failed, used to prevent infinite loop
32+ LastDeleteFailedReason string `xorm:"TEXT"` // Last reason the deletion failed, used to prevent infinite loop
33+ LastDeleteFailedTime timeutil.TimeStamp // Last time the deletion failed, used to prevent infinite loop
34+ Size int64 `xorm:"DEFAULT 0"`
35+ CreatedUnix timeutil.TimeStamp `xorm:"created"`
36+ CustomDownloadURL string `xorm:"-"`
3337}
3438
3539func init () {
@@ -88,7 +92,9 @@ func (err ErrAttachmentNotExist) Unwrap() error {
8892// GetAttachmentByID returns attachment by given id
8993func GetAttachmentByID (ctx context.Context , id int64 ) (* Attachment , error ) {
9094 attach := & Attachment {}
91- if has , err := db .GetEngine (ctx ).ID (id ).Get (attach ); err != nil {
95+ if has , err := db .GetEngine (ctx ).ID (id ).
96+ And ("status = ?" , db .FileStatusNormal ).
97+ Get (attach ); err != nil {
9298 return nil , err
9399 } else if ! has {
94100 return nil , ErrAttachmentNotExist {ID : id , UUID : "" }
@@ -99,7 +105,9 @@ func GetAttachmentByID(ctx context.Context, id int64) (*Attachment, error) {
99105// GetAttachmentByUUID returns attachment by given UUID.
100106func GetAttachmentByUUID (ctx context.Context , uuid string ) (* Attachment , error ) {
101107 attach := & Attachment {}
102- has , err := db .GetEngine (ctx ).Where ("uuid=?" , uuid ).Get (attach )
108+ has , err := db .GetEngine (ctx ).Where ("uuid=?" , uuid ).
109+ And ("status = ?" , db .FileStatusNormal ).
110+ Get (attach )
103111 if err != nil {
104112 return nil , err
105113 } else if ! has {
@@ -116,18 +124,24 @@ func GetAttachmentsByUUIDs(ctx context.Context, uuids []string) ([]*Attachment,
116124
117125 // Silently drop invalid uuids.
118126 attachments := make ([]* Attachment , 0 , len (uuids ))
119- return attachments , db .GetEngine (ctx ).In ("uuid" , uuids ).Find (& attachments )
127+ return attachments , db .GetEngine (ctx ).In ("uuid" , uuids ).
128+ And ("status = ?" , db .FileStatusNormal ).
129+ Find (& attachments )
120130}
121131
122132// ExistAttachmentsByUUID returns true if attachment exists with the given UUID
123133func ExistAttachmentsByUUID (ctx context.Context , uuid string ) (bool , error ) {
124- return db .GetEngine (ctx ).Where ("`uuid`=?" , uuid ).Exist (new (Attachment ))
134+ return db .GetEngine (ctx ).Where ("`uuid`=?" , uuid ).
135+ And ("status = ?" , db .FileStatusNormal ).
136+ Exist (new (Attachment ))
125137}
126138
127139// GetAttachmentsByIssueID returns all attachments of an issue.
128140func GetAttachmentsByIssueID (ctx context.Context , issueID int64 ) ([]* Attachment , error ) {
129141 attachments := make ([]* Attachment , 0 , 10 )
130- return attachments , db .GetEngine (ctx ).Where ("issue_id = ? AND comment_id = 0" , issueID ).Find (& attachments )
142+ return attachments , db .GetEngine (ctx ).Where ("issue_id = ? AND comment_id = 0" , issueID ).
143+ And ("status = ?" , db .FileStatusNormal ).
144+ Find (& attachments )
131145}
132146
133147// GetAttachmentsByIssueIDImagesLatest returns the latest image attachments of an issue.
@@ -142,19 +156,23 @@ func GetAttachmentsByIssueIDImagesLatest(ctx context.Context, issueID int64) ([]
142156 OR name like '%.jxl'
143157 OR name like '%.png'
144158 OR name like '%.svg'
145- OR name like '%.webp')` , issueID ).Desc ("comment_id" ).Limit (5 ).Find (& attachments )
159+ OR name like '%.webp')` , issueID ).
160+ And ("status = ?" , db .FileStatusNormal ).
161+ Desc ("comment_id" ).Limit (5 ).Find (& attachments )
146162}
147163
148164// GetAttachmentsByCommentID returns all attachments if comment by given ID.
149165func GetAttachmentsByCommentID (ctx context.Context , commentID int64 ) ([]* Attachment , error ) {
150166 attachments := make ([]* Attachment , 0 , 10 )
151- return attachments , db .GetEngine (ctx ).Where ("comment_id=?" , commentID ).Find (& attachments )
167+ return attachments , db .GetEngine (ctx ).Where ("comment_id=?" , commentID ).
168+ And ("status = ?" , db .FileStatusNormal ).
169+ Find (& attachments )
152170}
153171
154172// GetAttachmentByReleaseIDFileName returns attachment by given releaseId and fileName.
155173func GetAttachmentByReleaseIDFileName (ctx context.Context , releaseID int64 , fileName string ) (* Attachment , error ) {
156174 attach := & Attachment {ReleaseID : releaseID , Name : fileName }
157- has , err := db .GetEngine (ctx ).Get (attach )
175+ has , err := db .GetEngine (ctx ).Where ( "status = ?" , db . FileStatusNormal ). Get (attach )
158176 if err != nil {
159177 return nil , err
160178 } else if ! has {
@@ -185,7 +203,8 @@ func UpdateAttachment(ctx context.Context, atta *Attachment) error {
185203 return err
186204}
187205
188- func DeleteAttachments (ctx context.Context , attachments []* Attachment ) (int64 , error ) {
206+ // MarkAttachmentsDeleted marks the given attachments as deleted
207+ func MarkAttachmentsDeleted (ctx context.Context , attachments []* Attachment ) (int64 , error ) {
189208 if len (attachments ) == 0 {
190209 return 0 , nil
191210 }
@@ -195,15 +214,41 @@ func DeleteAttachments(ctx context.Context, attachments []*Attachment) (int64, e
195214 ids = append (ids , a .ID )
196215 }
197216
198- return db .GetEngine (ctx ).In ("id" , ids ).NoAutoCondition ().Delete (attachments [0 ])
217+ return db .GetEngine (ctx ).Table ("attachment" ).In ("id" , ids ).Update (map [string ]any {
218+ "status" : db .FileStatusToBeDeleted ,
219+ })
199220}
200221
201- // DeleteAttachmentsByRelease deletes all attachments associated with the given release.
202- func DeleteAttachmentsByRelease (ctx context.Context , releaseID int64 ) error {
203- _ , err := db .GetEngine (ctx ).Where ("release_id = ?" , releaseID ).Delete (& Attachment {})
222+ // MarkAttachmentsDeletedByRelease marks all attachments associated with the given release as deleted.
223+ func MarkAttachmentsDeletedByRelease (ctx context.Context , releaseID int64 ) error {
224+ _ , err := db .GetEngine (ctx ).Table ("attachment" ).Where ("release_id = ?" , releaseID ).Update (map [string ]any {
225+ "status" : db .FileStatusToBeDeleted ,
226+ })
204227 return err
205228}
206229
230+ // DeleteAttachmentByID deletes the attachment which has been marked as deleted by given id
231+ func DeleteAttachmentByID (ctx context.Context , id int64 ) error {
232+ cnt , err := db .GetEngine (ctx ).ID (id ).Where ("status = ?" , db .FileStatusToBeDeleted ).Delete (new (Attachment ))
233+ if err != nil {
234+ return fmt .Errorf ("delete attachment by id: %w" , err )
235+ }
236+ if cnt != 1 {
237+ return fmt .Errorf ("the attachment with id %d was not found or is not marked for deletion" , id )
238+ }
239+ return nil
240+ }
241+
242+ func UpdateAttachmentFailure (ctx context.Context , attachment * Attachment , err error ) error {
243+ attachment .DeleteFailedCount ++
244+ _ , updateErr := db .GetEngine (ctx ).Table ("attachment" ).ID (attachment .ID ).Update (map [string ]any {
245+ "delete_failed_count" : attachment .DeleteFailedCount ,
246+ "last_delete_failed_reason" : err .Error (),
247+ "last_delete_failed_time" : timeutil .TimeStampNow (),
248+ })
249+ return updateErr
250+ }
251+
207252// CountOrphanedAttachments returns the number of bad attachments
208253func CountOrphanedAttachments (ctx context.Context ) (int64 , error ) {
209254 return db .GetEngine (ctx ).Where ("(issue_id > 0 and issue_id not in (select id from issue)) or (release_id > 0 and release_id not in (select id from `release`))" ).
0 commit comments