mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-01-31 14:08:06 +08:00
feat: 服务重启重新拉起定时任务
This commit is contained in:
parent
fdcfdd7963
commit
7d14d37057
@ -111,6 +111,24 @@ func (b *BaseApi) UpdateCronjob(c *gin.Context) {
|
|||||||
helper.SuccessWithData(c, nil)
|
helper.SuccessWithData(c, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *BaseApi) UpdateCronjobStatus(c *gin.Context) {
|
||||||
|
var req dto.CronjobUpdateStatus
|
||||||
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
|
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := global.VALID.Struct(req); err != nil {
|
||||||
|
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := cronjobService.UpdateStatus(req.ID, req.Status); err != nil {
|
||||||
|
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.SuccessWithData(c, nil)
|
||||||
|
}
|
||||||
|
|
||||||
func (b *BaseApi) LoadRecordDetail(c *gin.Context) {
|
func (b *BaseApi) LoadRecordDetail(c *gin.Context) {
|
||||||
var req dto.DetailFile
|
var req dto.DetailFile
|
||||||
if err := c.ShouldBindJSON(&req); err != nil {
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
|
@ -37,8 +37,11 @@ type CronjobUpdate struct {
|
|||||||
SourceDir string `json:"sourceDir"`
|
SourceDir string `json:"sourceDir"`
|
||||||
TargetDirID int `json:"targetDirID"`
|
TargetDirID int `json:"targetDirID"`
|
||||||
RetainCopies int `json:"retainCopies" validate:"number,min=1"`
|
RetainCopies int `json:"retainCopies" validate:"number,min=1"`
|
||||||
|
}
|
||||||
|
|
||||||
Status string `json:"status"`
|
type CronjobUpdateStatus struct {
|
||||||
|
ID uint `json:"id" validate:"required"`
|
||||||
|
Status string `json:"status" validate:"required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type DetailFile struct {
|
type DetailFile struct {
|
||||||
|
@ -13,6 +13,7 @@ type CronjobRepo struct{}
|
|||||||
|
|
||||||
type ICronjobRepo interface {
|
type ICronjobRepo interface {
|
||||||
Get(opts ...DBOption) (model.Cronjob, error)
|
Get(opts ...DBOption) (model.Cronjob, error)
|
||||||
|
List(opts ...DBOption) ([]model.Cronjob, error)
|
||||||
Page(limit, offset int, opts ...DBOption) (int64, []model.Cronjob, error)
|
Page(limit, offset int, opts ...DBOption) (int64, []model.Cronjob, error)
|
||||||
Create(cronjob *model.Cronjob) error
|
Create(cronjob *model.Cronjob) error
|
||||||
WithByDate(startTime, endTime time.Time) DBOption
|
WithByDate(startTime, endTime time.Time) DBOption
|
||||||
@ -20,6 +21,7 @@ type ICronjobRepo interface {
|
|||||||
Save(id uint, cronjob model.Cronjob) error
|
Save(id uint, cronjob model.Cronjob) error
|
||||||
Update(id uint, vars map[string]interface{}) error
|
Update(id uint, vars map[string]interface{}) error
|
||||||
Delete(opts ...DBOption) error
|
Delete(opts ...DBOption) error
|
||||||
|
DeleteRecord(jobID uint) error
|
||||||
StartRecords(cronjobID uint, targetPath string) model.JobRecords
|
StartRecords(cronjobID uint, targetPath string) model.JobRecords
|
||||||
EndRecords(record model.JobRecords, status, message, records string)
|
EndRecords(record model.JobRecords, status, message, records string)
|
||||||
}
|
}
|
||||||
@ -38,28 +40,40 @@ func (u *CronjobRepo) Get(opts ...DBOption) (model.Cronjob, error) {
|
|||||||
return cronjob, err
|
return cronjob, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *CronjobRepo) Page(page, size int, opts ...DBOption) (int64, []model.Cronjob, error) {
|
func (u *CronjobRepo) List(opts ...DBOption) ([]model.Cronjob, error) {
|
||||||
var users []model.Cronjob
|
var cronjobs []model.Cronjob
|
||||||
db := global.DB.Model(&model.Cronjob{})
|
db := global.DB.Model(&model.Cronjob{})
|
||||||
for _, opt := range opts {
|
for _, opt := range opts {
|
||||||
db = opt(db)
|
db = opt(db)
|
||||||
}
|
}
|
||||||
count := int64(0)
|
count := int64(0)
|
||||||
db = db.Count(&count)
|
db = db.Count(&count)
|
||||||
err := db.Limit(size).Offset(size * (page - 1)).Find(&users).Error
|
err := db.Find(&cronjobs).Error
|
||||||
return count, users, err
|
return cronjobs, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *CronjobRepo) Page(page, size int, opts ...DBOption) (int64, []model.Cronjob, error) {
|
||||||
|
var cronjobs []model.Cronjob
|
||||||
|
db := global.DB.Model(&model.Cronjob{})
|
||||||
|
for _, opt := range opts {
|
||||||
|
db = opt(db)
|
||||||
|
}
|
||||||
|
count := int64(0)
|
||||||
|
db = db.Count(&count)
|
||||||
|
err := db.Order("created_at").Limit(size).Offset(size * (page - 1)).Find(&cronjobs).Error
|
||||||
|
return count, cronjobs, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *CronjobRepo) PageRecords(page, size int, opts ...DBOption) (int64, []model.JobRecords, error) {
|
func (u *CronjobRepo) PageRecords(page, size int, opts ...DBOption) (int64, []model.JobRecords, error) {
|
||||||
var users []model.JobRecords
|
var cronjobs []model.JobRecords
|
||||||
db := global.DB.Model(&model.JobRecords{})
|
db := global.DB.Model(&model.JobRecords{})
|
||||||
for _, opt := range opts {
|
for _, opt := range opts {
|
||||||
db = opt(db)
|
db = opt(db)
|
||||||
}
|
}
|
||||||
count := int64(0)
|
count := int64(0)
|
||||||
db = db.Count(&count)
|
db = db.Count(&count)
|
||||||
err := db.Limit(size).Offset(size * (page - 1)).Find(&users).Error
|
err := db.Order("created_at").Limit(size).Offset(size * (page - 1)).Find(&cronjobs).Error
|
||||||
return count, users, err
|
return count, cronjobs, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *CronjobRepo) Create(cronjob *model.Cronjob) error {
|
func (u *CronjobRepo) Create(cronjob *model.Cronjob) error {
|
||||||
@ -112,3 +126,6 @@ func (u *CronjobRepo) Delete(opts ...DBOption) error {
|
|||||||
}
|
}
|
||||||
return db.Delete(&model.Cronjob{}).Error
|
return db.Delete(&model.Cronjob{}).Error
|
||||||
}
|
}
|
||||||
|
func (u *CronjobRepo) DeleteRecord(jobID uint) error {
|
||||||
|
return global.DB.Where("cronjob_id = ?", jobID).Delete(&model.JobRecords{}).Error
|
||||||
|
}
|
||||||
|
@ -19,11 +19,13 @@ import (
|
|||||||
"github.com/1Panel-dev/1Panel/utils/cloud_storage"
|
"github.com/1Panel-dev/1Panel/utils/cloud_storage"
|
||||||
"github.com/jinzhu/copier"
|
"github.com/jinzhu/copier"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
"github.com/robfig/cron/v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
errRecord = "errRecord"
|
errRecord = "errRecord"
|
||||||
errHandle = "errHandle"
|
errHandle = "errHandle"
|
||||||
|
noRecord = "noRecord"
|
||||||
)
|
)
|
||||||
|
|
||||||
type CronjobService struct{}
|
type CronjobService struct{}
|
||||||
@ -33,6 +35,7 @@ type ICronjobService interface {
|
|||||||
SearchRecords(search dto.SearchRecord) (int64, interface{}, error)
|
SearchRecords(search dto.SearchRecord) (int64, interface{}, error)
|
||||||
Create(cronjobDto dto.CronjobCreate) error
|
Create(cronjobDto dto.CronjobCreate) error
|
||||||
Save(id uint, req dto.CronjobUpdate) error
|
Save(id uint, req dto.CronjobUpdate) error
|
||||||
|
UpdateStatus(id uint, status string) error
|
||||||
Delete(ids []uint) error
|
Delete(ids []uint) error
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,19 +96,32 @@ func (u *CronjobService) Create(cronjobDto dto.CronjobCreate) error {
|
|||||||
if err := cronjobRepo.Create(&cronjob); err != nil {
|
if err := cronjobRepo.Create(&cronjob); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if err := u.StartJob(&cronjob); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *CronjobService) StartJob(cronjob *model.Cronjob) error {
|
||||||
|
global.Cron.Remove(cron.EntryID(cronjob.EntryID))
|
||||||
var (
|
var (
|
||||||
entryID int
|
entryID int
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
switch cronjobDto.Type {
|
switch cronjob.Type {
|
||||||
case "shell":
|
case "shell":
|
||||||
entryID, err = u.AddShellJob(&cronjob)
|
entryID, err = u.AddShellJob(cronjob)
|
||||||
case "curl":
|
case "curl":
|
||||||
entryID, err = u.AddCurlJob(&cronjob)
|
entryID, err = u.AddCurlJob(cronjob)
|
||||||
case "directory":
|
case "directory":
|
||||||
entryID, err = u.AddDirectoryJob(&cronjob)
|
entryID, err = u.AddDirectoryJob(cronjob)
|
||||||
|
case "website":
|
||||||
|
entryID, err = u.AddWebSiteJob(cronjob)
|
||||||
|
case "database":
|
||||||
|
entryID, err = u.AddDatabaseJob(cronjob)
|
||||||
|
default:
|
||||||
|
entryID, err = u.AddShellJob(cronjob)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -119,8 +135,25 @@ func (u *CronjobService) Delete(ids []uint) error {
|
|||||||
if cronjob.ID == 0 {
|
if cronjob.ID == 0 {
|
||||||
return constant.ErrRecordNotFound
|
return constant.ErrRecordNotFound
|
||||||
}
|
}
|
||||||
|
global.Cron.Remove(cron.EntryID(cronjob.EntryID))
|
||||||
|
_ = cronjobRepo.DeleteRecord(ids[0])
|
||||||
|
|
||||||
|
if err := os.RemoveAll(fmt.Sprintf("%s/%s/%s-%v", constant.TaskDir, cronjob.Type, cronjob.Name, cronjob.ID)); err != nil {
|
||||||
|
global.LOG.Errorf("rm file %s/%s/%s-%v failed, err: %v", constant.TaskDir, cronjob.Type, cronjob.Name, cronjob.ID, err)
|
||||||
|
}
|
||||||
return cronjobRepo.Delete(commonRepo.WithByID(ids[0]))
|
return cronjobRepo.Delete(commonRepo.WithByID(ids[0]))
|
||||||
}
|
}
|
||||||
|
cronjobs, err := cronjobRepo.List(commonRepo.WithIdsIn(ids))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for i := range cronjobs {
|
||||||
|
global.Cron.Remove(cron.EntryID(cronjobs[i].EntryID))
|
||||||
|
_ = cronjobRepo.DeleteRecord(cronjobs[i].ID)
|
||||||
|
if err := os.RemoveAll(fmt.Sprintf("%s/%s/%s-%v", constant.TaskDir, cronjobs[i].Type, cronjobs[i].Name, cronjobs[i].ID)); err != nil {
|
||||||
|
global.LOG.Errorf("rm file %s/%s/%s-%v failed, err: %v", constant.TaskDir, cronjobs[i].Type, cronjobs[i].Name, cronjobs[i].ID, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
return cronjobRepo.Delete(commonRepo.WithIdsIn(ids))
|
return cronjobRepo.Delete(commonRepo.WithIdsIn(ids))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,15 +162,33 @@ func (u *CronjobService) Save(id uint, req dto.CronjobUpdate) error {
|
|||||||
if err := copier.Copy(&cronjob, &req); err != nil {
|
if err := copier.Copy(&cronjob, &req); err != nil {
|
||||||
return errors.WithMessage(constant.ErrStructTransform, err.Error())
|
return errors.WithMessage(constant.ErrStructTransform, err.Error())
|
||||||
}
|
}
|
||||||
|
if err := u.StartJob(&cronjob); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return cronjobRepo.Save(id, cronjob)
|
return cronjobRepo.Save(id, cronjob)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *CronjobService) UpdateStatus(id uint, status string) error {
|
||||||
|
cronjob, _ := cronjobRepo.Get(commonRepo.WithByID(id))
|
||||||
|
if cronjob.ID == 0 {
|
||||||
|
return errors.WithMessage(constant.ErrRecordNotFound, "record not found")
|
||||||
|
}
|
||||||
|
if status == constant.StatusEnable {
|
||||||
|
if err := u.StartJob(&cronjob); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
global.Cron.Remove(cron.EntryID(cronjob.EntryID))
|
||||||
|
}
|
||||||
|
return cronjobRepo.Update(cronjob.ID, map[string]interface{}{"status": status})
|
||||||
|
}
|
||||||
|
|
||||||
func (u *CronjobService) AddShellJob(cronjob *model.Cronjob) (int, error) {
|
func (u *CronjobService) AddShellJob(cronjob *model.Cronjob) (int, error) {
|
||||||
addFunc := func() {
|
addFunc := func() {
|
||||||
record := cronjobRepo.StartRecords(cronjob.ID, "")
|
record := cronjobRepo.StartRecords(cronjob.ID, "")
|
||||||
|
|
||||||
cmd := exec.Command(cronjob.Script)
|
cmd := exec.Command(cronjob.Script)
|
||||||
stdout, err := cmd.Output()
|
stdout, err := cmd.CombinedOutput()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
record.Records = errHandle
|
record.Records = errHandle
|
||||||
cronjobRepo.EndRecords(record, constant.StatusFailed, err.Error(), errHandle)
|
cronjobRepo.EndRecords(record, constant.StatusFailed, err.Error(), errHandle)
|
||||||
@ -150,6 +201,7 @@ func (u *CronjobService) AddShellJob(cronjob *model.Cronjob) (int, error) {
|
|||||||
}
|
}
|
||||||
cronjobRepo.EndRecords(record, constant.StatusSuccess, "", record.Records)
|
cronjobRepo.EndRecords(record, constant.StatusSuccess, "", record.Records)
|
||||||
}
|
}
|
||||||
|
global.LOG.Infof("add %s job %s successful", cronjob.Type, cronjob.Name)
|
||||||
entryID, err := global.Cron.AddFunc(cronjob.Spec, addFunc)
|
entryID, err := global.Cron.AddFunc(cronjob.Spec, addFunc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
@ -183,6 +235,7 @@ func (u *CronjobService) AddCurlJob(cronjob *model.Cronjob) (int, error) {
|
|||||||
}
|
}
|
||||||
cronjobRepo.EndRecords(record, constant.StatusSuccess, "", record.Records)
|
cronjobRepo.EndRecords(record, constant.StatusSuccess, "", record.Records)
|
||||||
}
|
}
|
||||||
|
global.LOG.Infof("add %s job %s successful", cronjob.Type, cronjob.Name)
|
||||||
entryID, err := global.Cron.AddFunc(cronjob.Spec, addFunc)
|
entryID, err := global.Cron.AddFunc(cronjob.Spec, addFunc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
@ -209,6 +262,7 @@ func (u *CronjobService) AddDirectoryJob(cronjob *model.Cronjob) (int, error) {
|
|||||||
}
|
}
|
||||||
cronjobRepo.EndRecords(record, constant.StatusSuccess, "", record.Records)
|
cronjobRepo.EndRecords(record, constant.StatusSuccess, "", record.Records)
|
||||||
}
|
}
|
||||||
|
global.LOG.Infof("add %s job %s successful", cronjob.Type, cronjob.Name)
|
||||||
entryID, err := global.Cron.AddFunc(cronjob.Spec, addFunc)
|
entryID, err := global.Cron.AddFunc(cronjob.Spec, addFunc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
@ -228,6 +282,11 @@ func (u *CronjobService) AddWebSiteJob(cronjob *model.Cronjob) (int, error) {
|
|||||||
cronjobRepo.EndRecords(record, constant.StatusFailed, err.Error(), errHandle)
|
cronjobRepo.EndRecords(record, constant.StatusFailed, err.Error(), errHandle)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if len(message) == 0 {
|
||||||
|
record.Records = noRecord
|
||||||
|
cronjobRepo.EndRecords(record, constant.StatusSuccess, "", record.Records)
|
||||||
|
return
|
||||||
|
}
|
||||||
record.Records, err = mkdirAndWriteFile(cronjob, record.StartTime, message)
|
record.Records, err = mkdirAndWriteFile(cronjob, record.StartTime, message)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
record.Records = errRecord
|
record.Records = errRecord
|
||||||
@ -235,6 +294,34 @@ func (u *CronjobService) AddWebSiteJob(cronjob *model.Cronjob) (int, error) {
|
|||||||
}
|
}
|
||||||
cronjobRepo.EndRecords(record, constant.StatusSuccess, "", record.Records)
|
cronjobRepo.EndRecords(record, constant.StatusSuccess, "", record.Records)
|
||||||
}
|
}
|
||||||
|
global.LOG.Infof("add %s job %s successful", cronjob.Type, cronjob.Name)
|
||||||
|
entryID, err := global.Cron.AddFunc(cronjob.Spec, addFunc)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return int(entryID), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *CronjobService) AddDatabaseJob(cronjob *model.Cronjob) (int, error) {
|
||||||
|
addFunc := func() {
|
||||||
|
record := cronjobRepo.StartRecords(cronjob.ID, "")
|
||||||
|
if len(cronjob.URL) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
message, err := tarWithExclude(cronjob, record.StartTime)
|
||||||
|
if err != nil {
|
||||||
|
record.Records = errHandle
|
||||||
|
cronjobRepo.EndRecords(record, constant.StatusFailed, err.Error(), errHandle)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
record.Records, err = mkdirAndWriteFile(cronjob, record.StartTime, message)
|
||||||
|
if err != nil {
|
||||||
|
record.Records = errRecord
|
||||||
|
global.LOG.Errorf("save file %s failed, err: %v", record.Records, err)
|
||||||
|
}
|
||||||
|
cronjobRepo.EndRecords(record, constant.StatusSuccess, "", record.Records)
|
||||||
|
}
|
||||||
|
global.LOG.Infof("add %s job %s successful", cronjob.Type, cronjob.Name)
|
||||||
entryID, err := global.Cron.AddFunc(cronjob.Spec, addFunc)
|
entryID, err := global.Cron.AddFunc(cronjob.Spec, addFunc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
@ -268,18 +355,27 @@ func tarWithExclude(cronjob *model.Cronjob, startTime time.Time) ([]byte, error)
|
|||||||
return nil, fmt.Errorf("load target dir failed, err: %v", err)
|
return nil, fmt.Errorf("load target dir failed, err: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
excludes := strings.Split(cronjob.ExclusionRules, ";")
|
exStr := []string{}
|
||||||
name := fmt.Sprintf("%s/%s.tar.gz", targetdir, startTime.Format("20060102150405"))
|
name := ""
|
||||||
exStr := []string{"-zcvPf", name}
|
if cronjob.Type == "database" {
|
||||||
for _, exclude := range excludes {
|
exStr = append(exStr, "-zvPf")
|
||||||
exStr = append(exStr, "--exclude")
|
name = fmt.Sprintf("%s/%s.gz", targetdir, startTime.Format("20060102150405"))
|
||||||
exStr = append(exStr, exclude)
|
exStr = append(exStr, name)
|
||||||
|
} else {
|
||||||
|
exStr = append(exStr, "-zcvPf")
|
||||||
|
name = fmt.Sprintf("%s/%s.tar.gz", targetdir, startTime.Format("20060102150405"))
|
||||||
|
exStr = append(exStr, name)
|
||||||
|
excludes := strings.Split(cronjob.ExclusionRules, ";")
|
||||||
|
for _, exclude := range excludes {
|
||||||
|
exStr = append(exStr, "--exclude")
|
||||||
|
exStr = append(exStr, exclude)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
exStr = append(exStr, cronjob.SourceDir)
|
exStr = append(exStr, cronjob.SourceDir)
|
||||||
cmd := exec.Command("tar", exStr...)
|
cmd := exec.Command("tar", exStr...)
|
||||||
stdout, err := cmd.Output()
|
stdout, err := cmd.CombinedOutput()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("tar zcvPf failed, err: %v", err)
|
return nil, fmt.Errorf("tar zcPf failed, err: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if varMaps["type"] != "LOCAL" {
|
if varMaps["type"] != "LOCAL" {
|
||||||
@ -291,6 +387,9 @@ func tarWithExclude(cronjob *model.Cronjob, startTime time.Time) ([]byte, error)
|
|||||||
if !isOK {
|
if !isOK {
|
||||||
return nil, fmt.Errorf("cloud storage upload failed, err: %v", err)
|
return nil, fmt.Errorf("cloud storage upload failed, err: %v", err)
|
||||||
}
|
}
|
||||||
|
if err := os.RemoveAll(fmt.Sprintf("%s/%s/%s-%v", constant.TmpDir, cronjob.Type, cronjob.Name, cronjob.ID)); err != nil {
|
||||||
|
global.LOG.Errorf("rm file %s/%s/%s-%v failed, err: %v", constant.TaskDir, cronjob.Type, cronjob.Name, cronjob.ID, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return stdout, nil
|
return stdout, nil
|
||||||
}
|
}
|
||||||
@ -305,8 +404,8 @@ func loadTargetInfo(cronjob *model.Cronjob) (map[string]interface{}, string, err
|
|||||||
return nil, "", err
|
return nil, "", err
|
||||||
}
|
}
|
||||||
dir := ""
|
dir := ""
|
||||||
|
varMap["type"] = backup.Type
|
||||||
if backup.Type != "LOCAL" {
|
if backup.Type != "LOCAL" {
|
||||||
varMap["type"] = backup.Type
|
|
||||||
varMap["bucket"] = backup.Bucket
|
varMap["bucket"] = backup.Bucket
|
||||||
switch backup.Type {
|
switch backup.Type {
|
||||||
case constant.Sftp:
|
case constant.Sftp:
|
||||||
|
@ -3,6 +3,9 @@ package cron
|
|||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/1Panel-dev/1Panel/app/model"
|
||||||
|
"github.com/1Panel-dev/1Panel/app/service"
|
||||||
|
"github.com/1Panel-dev/1Panel/constant"
|
||||||
"github.com/1Panel-dev/1Panel/cron/job"
|
"github.com/1Panel-dev/1Panel/cron/job"
|
||||||
"github.com/1Panel-dev/1Panel/global"
|
"github.com/1Panel-dev/1Panel/global"
|
||||||
"github.com/robfig/cron/v3"
|
"github.com/robfig/cron/v3"
|
||||||
@ -10,15 +13,7 @@ import (
|
|||||||
|
|
||||||
func Run() {
|
func Run() {
|
||||||
nyc, _ := time.LoadLocation("Asia/Shanghai")
|
nyc, _ := time.LoadLocation("Asia/Shanghai")
|
||||||
Cron := cron.New(cron.WithLocation(nyc))
|
Cron := cron.New(cron.WithLocation(nyc), cron.WithChain(cron.Recover(cron.DefaultLogger)), cron.WithChain(cron.DelayIfStillRunning(cron.DefaultLogger)))
|
||||||
|
|
||||||
// var Cronjobs []model.Cronjob
|
|
||||||
// if err := global.DB.Where("status = ?", constant.StatusEnable).Find(&Cronjobs).Error; err != nil {
|
|
||||||
// global.LOG.Errorf("start my cronjob failed, err: %v", err)
|
|
||||||
// }
|
|
||||||
// for _, cronjob := range Cronjobs {
|
|
||||||
// switch cronjob.Type {}
|
|
||||||
// }
|
|
||||||
_, err := Cron.AddJob("@every 1m", job.NewMonitorJob())
|
_, err := Cron.AddJob("@every 1m", job.NewMonitorJob())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
global.LOG.Errorf("can not add corn job: %s", err.Error())
|
global.LOG.Errorf("can not add corn job: %s", err.Error())
|
||||||
@ -26,4 +21,14 @@ func Run() {
|
|||||||
Cron.Start()
|
Cron.Start()
|
||||||
|
|
||||||
global.Cron = Cron
|
global.Cron = Cron
|
||||||
|
|
||||||
|
var Cronjobs []model.Cronjob
|
||||||
|
if err := global.DB.Where("status = ?", constant.StatusEnable).Find(&Cronjobs).Error; err != nil {
|
||||||
|
global.LOG.Errorf("start my cronjob failed, err: %v", err)
|
||||||
|
}
|
||||||
|
for _, cronjob := range Cronjobs {
|
||||||
|
if err := service.ServiceGroupApp.StartJob(&cronjob); err != nil {
|
||||||
|
global.LOG.Errorf("start %s job %s failed, err: %v", cronjob.Type, cronjob.Name, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@ func (s *CronjobRouter) InitCronjobRouter(Router *gin.RouterGroup) {
|
|||||||
withRecordRouter.POST("", baseApi.CreateCronjob)
|
withRecordRouter.POST("", baseApi.CreateCronjob)
|
||||||
withRecordRouter.POST("/del", baseApi.DeleteCronjob)
|
withRecordRouter.POST("/del", baseApi.DeleteCronjob)
|
||||||
withRecordRouter.PUT(":id", baseApi.UpdateCronjob)
|
withRecordRouter.PUT(":id", baseApi.UpdateCronjob)
|
||||||
|
withRecordRouter.POST("/status", baseApi.UpdateCronjobStatus)
|
||||||
cmdRouter.POST("/search", baseApi.SearchCronjob)
|
cmdRouter.POST("/search", baseApi.SearchCronjob)
|
||||||
cmdRouter.POST("/search/records", baseApi.SearchJobRecords)
|
cmdRouter.POST("/search/records", baseApi.SearchJobRecords)
|
||||||
cmdRouter.POST("/search/detail", baseApi.LoadRecordDetail)
|
cmdRouter.POST("/search/detail", baseApi.LoadRecordDetail)
|
||||||
|
@ -56,6 +56,9 @@ export namespace Cronjob {
|
|||||||
sourceDir: string;
|
sourceDir: string;
|
||||||
targetDirID: number;
|
targetDirID: number;
|
||||||
retainCopies: number;
|
retainCopies: number;
|
||||||
|
}
|
||||||
|
export interface UpdateStatus {
|
||||||
|
id: number;
|
||||||
status: string;
|
status: string;
|
||||||
}
|
}
|
||||||
export interface SearchRecord extends ReqPage {
|
export interface SearchRecord extends ReqPage {
|
||||||
|
@ -25,3 +25,7 @@ export const searchRecords = (params: Cronjob.SearchRecord) => {
|
|||||||
export const getRecordDetail = (params: string) => {
|
export const getRecordDetail = (params: string) => {
|
||||||
return http.post<string>(`cronjobs/search/detail`, { path: params });
|
return http.post<string>(`cronjobs/search/detail`, { path: params });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const updateStatus = (params: Cronjob.UpdateStatus) => {
|
||||||
|
return http.post(`cronjobs/status`, params);
|
||||||
|
};
|
||||||
|
@ -183,6 +183,9 @@ export default {
|
|||||||
saturday: '周六',
|
saturday: '周六',
|
||||||
sunday: '周日',
|
sunday: '周日',
|
||||||
shellContent: '脚本内容',
|
shellContent: '脚本内容',
|
||||||
|
errRecord: '错误的日志记录',
|
||||||
|
errHandle: '任务执行失败',
|
||||||
|
noRecord: '执行未产生任何日志',
|
||||||
},
|
},
|
||||||
monitor: {
|
monitor: {
|
||||||
avgLoad: '平均负载',
|
avgLoad: '平均负载',
|
||||||
|
@ -72,7 +72,7 @@ import RecordDialog from '@/views/cronjob/record/index.vue';
|
|||||||
import { loadZero } from '@/views/cronjob/options';
|
import { loadZero } from '@/views/cronjob/options';
|
||||||
import { onMounted, reactive, ref } from 'vue';
|
import { onMounted, reactive, ref } from 'vue';
|
||||||
import { loadBackupName } from '@/views/setting/helper';
|
import { loadBackupName } from '@/views/setting/helper';
|
||||||
import { deleteCronjob, editCronjob, getCronjobPage } from '@/api/modules/cronjob';
|
import { deleteCronjob, getCronjobPage, updateStatus } from '@/api/modules/cronjob';
|
||||||
import { loadWeek } from './options';
|
import { loadWeek } from './options';
|
||||||
import i18n from '@/lang';
|
import i18n from '@/lang';
|
||||||
import { Cronjob } from '@/api/interface/cronjob';
|
import { Cronjob } from '@/api/interface/cronjob';
|
||||||
@ -149,7 +149,7 @@ const beforeChangeStatus = () => {
|
|||||||
};
|
};
|
||||||
const onChangeStatus = async (row: Cronjob.CronjobInfo) => {
|
const onChangeStatus = async (row: Cronjob.CronjobInfo) => {
|
||||||
if (switchState.value) {
|
if (switchState.value) {
|
||||||
await editCronjob(row);
|
await updateStatus({ id: row.id, status: row.status });
|
||||||
ElMessage.success(i18n.global.t('commons.msg.operationSuccess'));
|
ElMessage.success(i18n.global.t('commons.msg.operationSuccess'));
|
||||||
search();
|
search();
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
<el-form ref="formRef" :model="dialogData.rowData" label-position="left" :rules="rules" label-width="120px">
|
<el-form ref="formRef" :model="dialogData.rowData" label-position="left" :rules="rules" label-width="120px">
|
||||||
<el-form-item :label="$t('cronjob.taskType')" prop="type">
|
<el-form-item :label="$t('cronjob.taskType')" prop="type">
|
||||||
<el-select
|
<el-select
|
||||||
@change="changeName(true, dialogData.rowData!.type, dialogData.rowData!.website)"
|
@change="changeName(true, dialogData.rowData!.type)"
|
||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
v-model="dialogData.rowData!.type"
|
v-model="dialogData.rowData!.type"
|
||||||
>
|
>
|
||||||
@ -17,12 +17,7 @@
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<el-form-item :label="$t('cronjob.taskName')" prop="name">
|
<el-form-item :label="$t('cronjob.taskName')" prop="name">
|
||||||
<el-input
|
<el-input style="width: 100%" clearable v-model="dialogData.rowData!.name" />
|
||||||
:disabled="dialogData.rowData!.type === 'website' || dialogData.rowData!.type === 'database'"
|
|
||||||
style="width: 100%"
|
|
||||||
clearable
|
|
||||||
v-model="dialogData.rowData!.name"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<el-form-item :label="$t('cronjob.cronSpec')" prop="spec">
|
<el-form-item :label="$t('cronjob.cronSpec')" prop="spec">
|
||||||
@ -56,12 +51,18 @@
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<el-form-item v-if="hasScript()" :label="$t('cronjob.shellContent')" prop="script">
|
<el-form-item v-if="hasScript()" :label="$t('cronjob.shellContent')" prop="script">
|
||||||
<el-input style="width: 100%" clearable type="textarea" v-model="dialogData.rowData!.script" />
|
<el-input
|
||||||
|
style="width: 100%"
|
||||||
|
clearable
|
||||||
|
type="textarea"
|
||||||
|
:autosize="{ minRows: 3, maxRows: 6 }"
|
||||||
|
v-model="dialogData.rowData!.script"
|
||||||
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<el-form-item v-if="dialogData.rowData!.type === 'website'" :label="$t('cronjob.website')" prop="website">
|
<el-form-item v-if="dialogData.rowData!.type === 'website'" :label="$t('cronjob.website')" prop="website">
|
||||||
<el-select
|
<el-select
|
||||||
@change="changeName(false, dialogData.rowData!.type, dialogData.rowData!.website)"
|
@change="changeName(false, dialogData.rowData!.type)"
|
||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
v-model="dialogData.rowData!.website"
|
v-model="dialogData.rowData!.website"
|
||||||
>
|
>
|
||||||
@ -86,7 +87,7 @@
|
|||||||
prop="sourceDir"
|
prop="sourceDir"
|
||||||
>
|
>
|
||||||
<el-input
|
<el-input
|
||||||
@input="changeName(false, dialogData.rowData!.type, dialogData.rowData!.website)"
|
@input="changeName(false, dialogData.rowData!.type)"
|
||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
clearable
|
clearable
|
||||||
v-model="dialogData.rowData!.sourceDir"
|
v-model="dialogData.rowData!.sourceDir"
|
||||||
@ -120,7 +121,13 @@
|
|||||||
:label="$t('cronjob.exclusionRules')"
|
:label="$t('cronjob.exclusionRules')"
|
||||||
prop="exclusionRules"
|
prop="exclusionRules"
|
||||||
>
|
>
|
||||||
<el-input style="width: 100%" type="textarea" clearable v-model="dialogData.rowData!.exclusionRules" />
|
<el-input
|
||||||
|
style="width: 100%"
|
||||||
|
type="textarea"
|
||||||
|
:autosize="{ minRows: 3, maxRows: 6 }"
|
||||||
|
clearable
|
||||||
|
v-model="dialogData.rowData!.exclusionRules"
|
||||||
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
@ -216,7 +223,7 @@ const varifySpec = (rule: any, value: any, callback: any) => {
|
|||||||
callback();
|
callback();
|
||||||
};
|
};
|
||||||
const rules = reactive({
|
const rules = reactive({
|
||||||
name: [Rules.requiredInput],
|
name: [Rules.requiredInput, Rules.name],
|
||||||
type: [Rules.requiredSelect],
|
type: [Rules.requiredSelect],
|
||||||
specType: [Rules.requiredSelect],
|
specType: [Rules.requiredSelect],
|
||||||
spec: [
|
spec: [
|
||||||
@ -232,7 +239,7 @@ const rules = reactive({
|
|||||||
website: [Rules.requiredSelect],
|
website: [Rules.requiredSelect],
|
||||||
database: [Rules.requiredSelect],
|
database: [Rules.requiredSelect],
|
||||||
url: [Rules.requiredInput],
|
url: [Rules.requiredInput],
|
||||||
sourceDir: [Rules.requiredInput],
|
sourceDir: [Rules.requiredSelect],
|
||||||
targetDirID: [Rules.requiredSelect, Rules.number],
|
targetDirID: [Rules.requiredSelect, Rules.number],
|
||||||
retainCopies: [Rules.number],
|
retainCopies: [Rules.number],
|
||||||
});
|
});
|
||||||
@ -261,42 +268,15 @@ function isBackup() {
|
|||||||
function hasScript() {
|
function hasScript() {
|
||||||
return dialogData.value.rowData!.type === 'shell' || dialogData.value.rowData!.type === 'sync';
|
return dialogData.value.rowData!.type === 'shell' || dialogData.value.rowData!.type === 'sync';
|
||||||
}
|
}
|
||||||
function changeName(isChangeType: boolean, type: string, targetName: string) {
|
function changeName(isChangeType: boolean, type: string) {
|
||||||
if (isChangeType) {
|
if (isChangeType) {
|
||||||
targetName = '';
|
|
||||||
if (isBackup()) {
|
if (isBackup()) {
|
||||||
if (backupOptions.value.length === 0) {
|
if (backupOptions.value.length === 0) {
|
||||||
ElMessage.error(i18n.global.t('cronjob.missBackupAccount'));
|
ElMessage.error(i18n.global.t('cronjob.missBackupAccount'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
switch (type) {
|
dialogData.value.rowData!.name = `${type}-test`;
|
||||||
case 'website':
|
|
||||||
targetName = targetName ? targetName : i18n.global.t('cronjob.all');
|
|
||||||
dialogData.value.rowData!.name = `${i18n.global.t('cronjob.website')} [ ${targetName} ]`;
|
|
||||||
break;
|
|
||||||
case 'database':
|
|
||||||
targetName = targetName ? targetName : i18n.global.t('cronjob.all');
|
|
||||||
dialogData.value.rowData!.name = `${i18n.global.t('cronjob.database')} [ ${targetName} ]`;
|
|
||||||
break;
|
|
||||||
case 'directory':
|
|
||||||
targetName = targetName ? targetName : '/etc/1panel';
|
|
||||||
dialogData.value.rowData!.name = `${i18n.global.t('cronjob.directory')} [ ${targetName} ]`;
|
|
||||||
break;
|
|
||||||
case 'sync':
|
|
||||||
dialogData.value.rowData!.name = i18n.global.t('cronjob.syncDateName');
|
|
||||||
break;
|
|
||||||
case 'release':
|
|
||||||
dialogData.value.rowData!.name = i18n.global.t('cronjob.releaseMemory');
|
|
||||||
break;
|
|
||||||
case 'curl':
|
|
||||||
dialogData.value.rowData!.name = i18n.global.t('cronjob.curl');
|
|
||||||
dialogData.value.rowData!.url = 'http://';
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
dialogData.value.rowData!.name = '';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
function restForm() {
|
function restForm() {
|
||||||
if (formRef.value) {
|
if (formRef.value) {
|
||||||
|
@ -95,7 +95,7 @@
|
|||||||
trigger="click"
|
trigger="click"
|
||||||
style="white-space: pre-wrap"
|
style="white-space: pre-wrap"
|
||||||
>
|
>
|
||||||
<div style="margin-left: 20px; height: 400px; overflow: auto">
|
<div style="margin-left: 20px; max-height: 400px; overflow: auto">
|
||||||
<span style="white-space: pre-wrap">{{ dialogData.rowData!.script }}</span>
|
<span style="white-space: pre-wrap">{{ dialogData.rowData!.script }}</span>
|
||||||
</div>
|
</div>
|
||||||
<template #reference>
|
<template #reference>
|
||||||
@ -121,7 +121,7 @@
|
|||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="8" v-if="isBackup()">
|
<el-col :span="8" v-if="isBackup()">
|
||||||
<el-form-item :label="$t('cronjob.target')">
|
<el-form-item :label="$t('cronjob.target')">
|
||||||
{{ dialogData.rowData!.targetDirID }}
|
{{ loadBackupName(dialogData.rowData!.targetDir) }}
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="8" v-if="isBackup()">
|
<el-col :span="8" v-if="isBackup()">
|
||||||
@ -139,7 +139,9 @@
|
|||||||
v-if="dialogData.rowData!.type === 'website' || dialogData.rowData!.type === 'directory'"
|
v-if="dialogData.rowData!.type === 'website' || dialogData.rowData!.type === 'directory'"
|
||||||
>
|
>
|
||||||
<el-form-item :label="$t('cronjob.exclusionRules')">
|
<el-form-item :label="$t('cronjob.exclusionRules')">
|
||||||
{{ dialogData.rowData!.exclusionRules }}
|
<div v-for="item in dialogData.rowData!.exclusionRules.split(';')" :key="item">
|
||||||
|
<el-tag>{{ item }}</el-tag>
|
||||||
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
@ -176,13 +178,18 @@
|
|||||||
<el-row>
|
<el-row>
|
||||||
<el-col :span="24">
|
<el-col :span="24">
|
||||||
<el-form-item :label="$t('commons.table.records')">
|
<el-form-item :label="$t('commons.table.records')">
|
||||||
|
<span
|
||||||
|
v-if="currentRecord?.records! === 'errRecord' || currentRecord?.records! === 'errHandle'|| currentRecord?.records! === 'noRecord'"
|
||||||
|
>
|
||||||
|
{{ $t('cronjob.' + currentRecord?.records!) }}
|
||||||
|
</span>
|
||||||
<el-popover
|
<el-popover
|
||||||
placement="right"
|
placement="right"
|
||||||
:width="600"
|
:width="600"
|
||||||
trigger="click"
|
trigger="click"
|
||||||
style="white-space: pre-wrap"
|
style="white-space: pre-wrap"
|
||||||
>
|
>
|
||||||
<div style="margin-left: 20px; height: 400px; overflow: auto">
|
<div style="margin-left: 20px; max-height: 400px; overflow: auto">
|
||||||
<span style="white-space: pre-wrap">
|
<span style="white-space: pre-wrap">
|
||||||
{{ currentRecordDetail }}
|
{{ currentRecordDetail }}
|
||||||
</span>
|
</span>
|
||||||
@ -212,6 +219,7 @@
|
|||||||
import { reactive, ref } from 'vue';
|
import { reactive, ref } from 'vue';
|
||||||
import { Cronjob } from '@/api/interface/cronjob';
|
import { Cronjob } from '@/api/interface/cronjob';
|
||||||
import { loadZero, loadWeek } from '@/views/cronjob/options';
|
import { loadZero, loadWeek } from '@/views/cronjob/options';
|
||||||
|
import { loadBackupName } from '@/views/setting/helper';
|
||||||
import { searchRecords, getRecordDetail } from '@/api/modules/cronjob';
|
import { searchRecords, getRecordDetail } from '@/api/modules/cronjob';
|
||||||
import { dateFromat } from '@/utils/util';
|
import { dateFromat } from '@/utils/util';
|
||||||
import i18n from '@/lang';
|
import i18n from '@/lang';
|
||||||
|
Loading…
x
Reference in New Issue
Block a user