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  }