code.gitea.io/gitea@v1.22.3/models/repo/archiver.go (about) 1 // Copyright 2021 The Gitea Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 package repo 5 6 import ( 7 "context" 8 "fmt" 9 "strconv" 10 "strings" 11 "time" 12 13 "code.gitea.io/gitea/models/db" 14 "code.gitea.io/gitea/modules/git" 15 "code.gitea.io/gitea/modules/timeutil" 16 "code.gitea.io/gitea/modules/util" 17 18 "xorm.io/builder" 19 ) 20 21 // ArchiverStatus represents repo archive status 22 type ArchiverStatus int 23 24 // enumerate all repo archive statuses 25 const ( 26 ArchiverGenerating = iota // the archiver is generating 27 ArchiverReady // it's ready 28 ) 29 30 // RepoArchiver represents all archivers 31 type RepoArchiver struct { //revive:disable-line:exported 32 ID int64 `xorm:"pk autoincr"` 33 RepoID int64 `xorm:"index unique(s)"` 34 Type git.ArchiveType `xorm:"unique(s)"` 35 Status ArchiverStatus 36 CommitID string `xorm:"VARCHAR(64) unique(s)"` 37 CreatedUnix timeutil.TimeStamp `xorm:"INDEX NOT NULL created"` 38 } 39 40 func init() { 41 db.RegisterModel(new(RepoArchiver)) 42 } 43 44 // RelativePath returns the archive path relative to the archive storage root. 45 func (archiver *RepoArchiver) RelativePath() string { 46 return fmt.Sprintf("%d/%s/%s.%s", archiver.RepoID, archiver.CommitID[:2], archiver.CommitID, archiver.Type.String()) 47 } 48 49 // repoArchiverForRelativePath takes a relativePath created from (archiver *RepoArchiver) RelativePath() and creates a shell repoArchiver struct representing it 50 func repoArchiverForRelativePath(relativePath string) (*RepoArchiver, error) { 51 parts := strings.SplitN(relativePath, "/", 3) 52 if len(parts) != 3 { 53 return nil, util.SilentWrap{Message: fmt.Sprintf("invalid storage path: %s", relativePath), Err: util.ErrInvalidArgument} 54 } 55 repoID, err := strconv.ParseInt(parts[0], 10, 64) 56 if err != nil { 57 return nil, util.SilentWrap{Message: fmt.Sprintf("invalid storage path: %s", relativePath), Err: util.ErrInvalidArgument} 58 } 59 nameExts := strings.SplitN(parts[2], ".", 2) 60 if len(nameExts) != 2 { 61 return nil, util.SilentWrap{Message: fmt.Sprintf("invalid storage path: %s", relativePath), Err: util.ErrInvalidArgument} 62 } 63 64 return &RepoArchiver{ 65 RepoID: repoID, 66 CommitID: parts[1] + nameExts[0], 67 Type: git.ToArchiveType(nameExts[1]), 68 }, nil 69 } 70 71 // GetRepoArchiver get an archiver 72 func GetRepoArchiver(ctx context.Context, repoID int64, tp git.ArchiveType, commitID string) (*RepoArchiver, error) { 73 var archiver RepoArchiver 74 has, err := db.GetEngine(ctx).Where("repo_id=?", repoID).And("`type`=?", tp).And("commit_id=?", commitID).Get(&archiver) 75 if err != nil { 76 return nil, err 77 } 78 if has { 79 return &archiver, nil 80 } 81 return nil, nil 82 } 83 84 // ExistsRepoArchiverWithStoragePath checks if there is a RepoArchiver for a given storage path 85 func ExistsRepoArchiverWithStoragePath(ctx context.Context, storagePath string) (bool, error) { 86 // We need to invert the path provided func (archiver *RepoArchiver) RelativePath() above 87 archiver, err := repoArchiverForRelativePath(storagePath) 88 if err != nil { 89 return false, err 90 } 91 92 return db.GetEngine(ctx).Exist(archiver) 93 } 94 95 // UpdateRepoArchiverStatus updates archiver's status 96 func UpdateRepoArchiverStatus(ctx context.Context, archiver *RepoArchiver) error { 97 _, err := db.GetEngine(ctx).ID(archiver.ID).Cols("status").Update(archiver) 98 return err 99 } 100 101 // DeleteAllRepoArchives deletes all repo archives records 102 func DeleteAllRepoArchives(ctx context.Context) error { 103 // 1=1 to enforce delete all data, otherwise it will delete nothing 104 _, err := db.GetEngine(ctx).Where("1=1").Delete(new(RepoArchiver)) 105 return err 106 } 107 108 // FindRepoArchiversOption represents an archiver options 109 type FindRepoArchiversOption struct { 110 db.ListOptions 111 OlderThan time.Duration 112 } 113 114 func (opts FindRepoArchiversOption) ToConds() builder.Cond { 115 cond := builder.NewCond() 116 if opts.OlderThan > 0 { 117 cond = cond.And(builder.Lt{"created_unix": time.Now().Add(-opts.OlderThan).Unix()}) 118 } 119 return cond 120 } 121 122 func (opts FindRepoArchiversOption) ToOrders() string { 123 return "created_unix ASC" 124 } 125 126 // SetArchiveRepoState sets if a repo is archived 127 func SetArchiveRepoState(ctx context.Context, repo *Repository, isArchived bool) (err error) { 128 repo.IsArchived = isArchived 129 130 if isArchived { 131 repo.ArchivedUnix = timeutil.TimeStampNow() 132 } else { 133 repo.ArchivedUnix = timeutil.TimeStamp(0) 134 } 135 136 _, err = db.GetEngine(ctx).ID(repo.ID).Cols("is_archived", "archived_unix").NoAutoTime().Update(repo) 137 return err 138 }