code.gitea.io/gitea@v1.21.7/models/admin/task.go (about) 1 // Copyright 2019 Gitea. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 package admin 5 6 import ( 7 "context" 8 "fmt" 9 10 "code.gitea.io/gitea/models/db" 11 repo_model "code.gitea.io/gitea/models/repo" 12 user_model "code.gitea.io/gitea/models/user" 13 "code.gitea.io/gitea/modules/json" 14 "code.gitea.io/gitea/modules/migration" 15 "code.gitea.io/gitea/modules/secret" 16 "code.gitea.io/gitea/modules/setting" 17 "code.gitea.io/gitea/modules/structs" 18 "code.gitea.io/gitea/modules/timeutil" 19 "code.gitea.io/gitea/modules/util" 20 ) 21 22 // Task represents a task 23 type Task struct { 24 ID int64 25 DoerID int64 `xorm:"index"` // operator 26 Doer *user_model.User `xorm:"-"` 27 OwnerID int64 `xorm:"index"` // repo owner id, when creating, the repoID maybe zero 28 Owner *user_model.User `xorm:"-"` 29 RepoID int64 `xorm:"index"` 30 Repo *repo_model.Repository `xorm:"-"` 31 Type structs.TaskType 32 Status structs.TaskStatus `xorm:"index"` 33 StartTime timeutil.TimeStamp 34 EndTime timeutil.TimeStamp 35 PayloadContent string `xorm:"TEXT"` 36 Message string `xorm:"TEXT"` // if task failed, saved the error reason, it could be a JSON string of TranslatableMessage or a plain message 37 Created timeutil.TimeStamp `xorm:"created"` 38 } 39 40 func init() { 41 db.RegisterModel(new(Task)) 42 } 43 44 // TranslatableMessage represents JSON struct that can be translated with a Locale 45 type TranslatableMessage struct { 46 Format string 47 Args []any `json:"omitempty"` 48 } 49 50 // LoadRepo loads repository of the task 51 func (task *Task) LoadRepo(ctx context.Context) error { 52 if task.Repo != nil { 53 return nil 54 } 55 var repo repo_model.Repository 56 has, err := db.GetEngine(ctx).ID(task.RepoID).Get(&repo) 57 if err != nil { 58 return err 59 } else if !has { 60 return repo_model.ErrRepoNotExist{ 61 ID: task.RepoID, 62 } 63 } 64 task.Repo = &repo 65 return nil 66 } 67 68 // LoadDoer loads do user 69 func (task *Task) LoadDoer(ctx context.Context) error { 70 if task.Doer != nil { 71 return nil 72 } 73 74 var doer user_model.User 75 has, err := db.GetEngine(ctx).ID(task.DoerID).Get(&doer) 76 if err != nil { 77 return err 78 } else if !has { 79 return user_model.ErrUserNotExist{ 80 UID: task.DoerID, 81 } 82 } 83 task.Doer = &doer 84 85 return nil 86 } 87 88 // LoadOwner loads owner user 89 func (task *Task) LoadOwner(ctx context.Context) error { 90 if task.Owner != nil { 91 return nil 92 } 93 94 var owner user_model.User 95 has, err := db.GetEngine(ctx).ID(task.OwnerID).Get(&owner) 96 if err != nil { 97 return err 98 } else if !has { 99 return user_model.ErrUserNotExist{ 100 UID: task.OwnerID, 101 } 102 } 103 task.Owner = &owner 104 105 return nil 106 } 107 108 // UpdateCols updates some columns 109 func (task *Task) UpdateCols(ctx context.Context, cols ...string) error { 110 _, err := db.GetEngine(ctx).ID(task.ID).Cols(cols...).Update(task) 111 return err 112 } 113 114 // MigrateConfig returns task config when migrate repository 115 func (task *Task) MigrateConfig() (*migration.MigrateOptions, error) { 116 if task.Type == structs.TaskTypeMigrateRepo { 117 var opts migration.MigrateOptions 118 err := json.Unmarshal([]byte(task.PayloadContent), &opts) 119 if err != nil { 120 return nil, err 121 } 122 123 // decrypt credentials 124 if opts.CloneAddrEncrypted != "" { 125 if opts.CloneAddr, err = secret.DecryptSecret(setting.SecretKey, opts.CloneAddrEncrypted); err != nil { 126 return nil, err 127 } 128 } 129 if opts.AuthPasswordEncrypted != "" { 130 if opts.AuthPassword, err = secret.DecryptSecret(setting.SecretKey, opts.AuthPasswordEncrypted); err != nil { 131 return nil, err 132 } 133 } 134 if opts.AuthTokenEncrypted != "" { 135 if opts.AuthToken, err = secret.DecryptSecret(setting.SecretKey, opts.AuthTokenEncrypted); err != nil { 136 return nil, err 137 } 138 } 139 140 return &opts, nil 141 } 142 return nil, fmt.Errorf("Task type is %s, not Migrate Repo", task.Type.Name()) 143 } 144 145 // ErrTaskDoesNotExist represents a "TaskDoesNotExist" kind of error. 146 type ErrTaskDoesNotExist struct { 147 ID int64 148 RepoID int64 149 Type structs.TaskType 150 } 151 152 // IsErrTaskDoesNotExist checks if an error is a ErrTaskDoesNotExist. 153 func IsErrTaskDoesNotExist(err error) bool { 154 _, ok := err.(ErrTaskDoesNotExist) 155 return ok 156 } 157 158 func (err ErrTaskDoesNotExist) Error() string { 159 return fmt.Sprintf("task does not exist [id: %d, repo_id: %d, type: %d]", 160 err.ID, err.RepoID, err.Type) 161 } 162 163 func (err ErrTaskDoesNotExist) Unwrap() error { 164 return util.ErrNotExist 165 } 166 167 // GetMigratingTask returns the migrating task by repo's id 168 func GetMigratingTask(ctx context.Context, repoID int64) (*Task, error) { 169 task := Task{ 170 RepoID: repoID, 171 Type: structs.TaskTypeMigrateRepo, 172 } 173 has, err := db.GetEngine(ctx).Get(&task) 174 if err != nil { 175 return nil, err 176 } else if !has { 177 return nil, ErrTaskDoesNotExist{0, repoID, task.Type} 178 } 179 return &task, nil 180 } 181 182 // GetMigratingTaskByID returns the migrating task by repo's id 183 func GetMigratingTaskByID(ctx context.Context, id, doerID int64) (*Task, *migration.MigrateOptions, error) { 184 task := Task{ 185 ID: id, 186 DoerID: doerID, 187 Type: structs.TaskTypeMigrateRepo, 188 } 189 has, err := db.GetEngine(ctx).Get(&task) 190 if err != nil { 191 return nil, nil, err 192 } else if !has { 193 return nil, nil, ErrTaskDoesNotExist{id, 0, task.Type} 194 } 195 196 var opts migration.MigrateOptions 197 if err := json.Unmarshal([]byte(task.PayloadContent), &opts); err != nil { 198 return nil, nil, err 199 } 200 return &task, &opts, nil 201 } 202 203 // CreateTask creates a task on database 204 func CreateTask(ctx context.Context, task *Task) error { 205 return db.Insert(ctx, task) 206 } 207 208 // FinishMigrateTask updates database when migrate task finished 209 func FinishMigrateTask(ctx context.Context, task *Task) error { 210 task.Status = structs.TaskStatusFinished 211 task.EndTime = timeutil.TimeStampNow() 212 213 // delete credentials when we're done, they're a liability. 214 conf, err := task.MigrateConfig() 215 if err != nil { 216 return err 217 } 218 conf.AuthPassword = "" 219 conf.AuthToken = "" 220 conf.CloneAddr = util.SanitizeCredentialURLs(conf.CloneAddr) 221 conf.AuthPasswordEncrypted = "" 222 conf.AuthTokenEncrypted = "" 223 conf.CloneAddrEncrypted = "" 224 confBytes, err := json.Marshal(conf) 225 if err != nil { 226 return err 227 } 228 task.PayloadContent = string(confBytes) 229 230 _, err = db.GetEngine(ctx).ID(task.ID).Cols("status", "end_time", "payload_content").Update(task) 231 return err 232 }