mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-01-18 22:22:59 +08:00
feat: shell 脚本定时任务功能完成
This commit is contained in:
parent
f0a7ce855a
commit
ada58ecb8c
@ -1,6 +1,9 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/app/api/v1/helper"
|
||||
"github.com/1Panel-dev/1Panel/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/constant"
|
||||
@ -44,6 +47,29 @@ func (b *BaseApi) SearchCronjob(c *gin.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
func (b *BaseApi) SearchJobRecords(c *gin.Context) {
|
||||
var req dto.SearchRecord
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||
return
|
||||
}
|
||||
if global.CONF.System.DbType == "sqlite" {
|
||||
req.StartTime = req.StartTime.Add(8 * time.Hour)
|
||||
req.EndTime = req.EndTime.Add(8 * time.Hour)
|
||||
}
|
||||
|
||||
total, list, err := cronjobService.SearchRecords(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, dto.PageResult{
|
||||
Items: list,
|
||||
Total: total,
|
||||
})
|
||||
}
|
||||
|
||||
func (b *BaseApi) DeleteCronjob(c *gin.Context) {
|
||||
var req dto.BatchDeleteReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
@ -78,25 +104,33 @@ func (b *BaseApi) UpdateCronjob(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
upMap := make(map[string]interface{})
|
||||
upMap["name"] = req.Name
|
||||
upMap["spec_type"] = req.SpecType
|
||||
upMap["week"] = req.Week
|
||||
upMap["day"] = req.Day
|
||||
upMap["hour"] = req.Hour
|
||||
upMap["minute"] = req.Minute
|
||||
|
||||
upMap["script"] = req.Script
|
||||
upMap["website"] = req.Website
|
||||
upMap["database"] = req.Database
|
||||
upMap["source_dir"] = req.SourceDir
|
||||
upMap["target_dir_id"] = req.TargetDirID
|
||||
upMap["exclusion_rules"] = req.ExclusionRules
|
||||
upMap["retain_copies"] = req.RetainCopies
|
||||
upMap["status"] = req.Status
|
||||
if err := cronjobService.Update(id, upMap); err != nil {
|
||||
if err := cronjobService.Save(id, req); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
func (b *BaseApi) LoadRecordDetail(c *gin.Context) {
|
||||
var req dto.DetailFile
|
||||
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
|
||||
}
|
||||
file, err := os.Open(req.Path)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
buf := make([]byte, 1024*2)
|
||||
if _, err := file.Read(buf); err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, string(buf))
|
||||
}
|
||||
|
@ -80,7 +80,7 @@ func SuccessWithMsg(ctx *gin.Context, msg string) {
|
||||
func GetParamID(c *gin.Context) (uint, error) {
|
||||
idParam, ok := c.Params.Get("id")
|
||||
if !ok {
|
||||
return 0, errors.New("error name")
|
||||
return 0, errors.New("error id in path")
|
||||
}
|
||||
intNum, _ := strconv.Atoi(idParam)
|
||||
return uint(intNum), nil
|
||||
|
@ -1,5 +1,7 @@
|
||||
package dto
|
||||
|
||||
import "time"
|
||||
|
||||
type CronjobCreate struct {
|
||||
Name string `json:"name" validate:"required"`
|
||||
Type string `json:"type" validate:"required"`
|
||||
@ -39,6 +41,10 @@ type CronjobUpdate struct {
|
||||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
type DetailFile struct {
|
||||
Path string `json:"path" validate:"required"`
|
||||
}
|
||||
|
||||
type CronjobInfo struct {
|
||||
ID uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
@ -61,3 +67,21 @@ type CronjobInfo struct {
|
||||
|
||||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
type SearchRecord struct {
|
||||
PageInfo
|
||||
CronjobID int `json:"cronjobID"`
|
||||
StartTime time.Time `json:"startTime"`
|
||||
EndTime time.Time `json:"endTime"`
|
||||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
type Record struct {
|
||||
ID uint `json:"id"`
|
||||
StartTime time.Time `json:"startTime"`
|
||||
Records string `json:"records"`
|
||||
Status string `json:"status"`
|
||||
Message string `json:"message"`
|
||||
TargetPath string `json:"targetPath"`
|
||||
Interval int `json:"interval"`
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ package model
|
||||
import "time"
|
||||
|
||||
type BaseModel struct {
|
||||
ID uint `gorm:"primarykey"`
|
||||
ID uint `gorm:"primarykey;AUTO_INCREMENT"`
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
package model
|
||||
|
||||
import "time"
|
||||
|
||||
type Cronjob struct {
|
||||
BaseModel
|
||||
|
||||
@ -21,5 +23,19 @@ type Cronjob struct {
|
||||
ExclusionRules string `gorm:"longtext" json:"exclusionRules"`
|
||||
RetainCopies uint64 `gorm:"type:decimal" json:"retainCopies"`
|
||||
|
||||
Status string `gorm:"type:varchar(64)" json:"status"`
|
||||
Status string `gorm:"type:varchar(64)" json:"status"`
|
||||
EntryID uint64 `gorm:"type:decimal" json:"entryID"`
|
||||
Records []JobRecords `json:"records"`
|
||||
}
|
||||
|
||||
type JobRecords struct {
|
||||
BaseModel
|
||||
|
||||
CronjobID uint `gorm:"type:varchar(64);not null" json:"cronjobID"`
|
||||
StartTime time.Time `gorm:"type:datetime" json:"startTime"`
|
||||
Interval float64 `gorm:"type:float" json:"interval"`
|
||||
Records string `gorm:"longtext" json:"records"`
|
||||
Status string `gorm:"type:varchar(64)" json:"status"`
|
||||
Message string `gorm:"longtext" json:"message"`
|
||||
TargetPath string `gorm:"type:varchar(256)" json:"targetPath"`
|
||||
}
|
||||
|
@ -15,17 +15,17 @@ type MonitorBase struct {
|
||||
type MonitorIO struct {
|
||||
BaseModel
|
||||
Name string `json:"name"`
|
||||
ReadCount uint64 `gorm:"type:decimal" json:"readCount"`
|
||||
WriteCount uint64 `gorm:"type:decimal" json:"writeCount"`
|
||||
ReadTime uint64 `gorm:"type:decimal" json:"readTime"`
|
||||
WriteTime uint64 `gorm:"type:decimal" json:"writeTime"`
|
||||
ReadCount uint64 `json:"readCount"`
|
||||
WriteCount uint64 `json:"writeCount"`
|
||||
ReadTime uint64 `json:"readTime"`
|
||||
WriteTime uint64 `json:"writeTime"`
|
||||
ReadByte uint64 `gorm:"type:decimal(32)" json:"readByte"`
|
||||
WriteByte uint64 `gorm:"type:decimal(32)" json:"writeByte"`
|
||||
|
||||
Read uint64 `gorm:"type:decimal" json:"read"`
|
||||
Write uint64 `gorm:"type:decimal" json:"write"`
|
||||
Count uint64 `gorm:"type:decimal" json:"count"`
|
||||
Time uint64 `gorm:"type:decimal" json:"time"`
|
||||
Read uint64 `json:"read"`
|
||||
Write uint64 `json:"write"`
|
||||
Count uint64 `json:"count"`
|
||||
Time uint64 `json:"time"`
|
||||
}
|
||||
|
||||
type MonitorNetwork struct {
|
||||
|
@ -32,6 +32,15 @@ func (c *CommonRepo) WithByType(name string) DBOption {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *CommonRepo) WithByStatus(status string) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
if len(status) == 0 {
|
||||
return g
|
||||
}
|
||||
return g.Where("status = ?", status)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *CommonRepo) WithLikeName(name string) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("name like ?", "%"+name+"%")
|
||||
|
@ -1,8 +1,12 @@
|
||||
package repo
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/app/model"
|
||||
"github.com/1Panel-dev/1Panel/constant"
|
||||
"github.com/1Panel-dev/1Panel/global"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type CronjobRepo struct{}
|
||||
@ -11,8 +15,13 @@ type ICronjobRepo interface {
|
||||
Get(opts ...DBOption) (model.Cronjob, error)
|
||||
Page(limit, offset int, opts ...DBOption) (int64, []model.Cronjob, error)
|
||||
Create(cronjob *model.Cronjob) error
|
||||
WithByDate(startTime, endTime time.Time) DBOption
|
||||
WithByJobID(id int) DBOption
|
||||
Save(id uint, cronjob model.Cronjob) error
|
||||
Update(id uint, vars map[string]interface{}) error
|
||||
Delete(opts ...DBOption) error
|
||||
StartRecords(cronjobID uint, targetPath string) model.JobRecords
|
||||
EndRecords(record model.JobRecords, status, message, records string)
|
||||
}
|
||||
|
||||
func NewICronjobService() ICronjobRepo {
|
||||
@ -41,10 +50,57 @@ func (u *CronjobRepo) Page(page, size int, opts ...DBOption) (int64, []model.Cro
|
||||
return count, users, err
|
||||
}
|
||||
|
||||
func (u *CronjobRepo) PageRecords(page, size int, opts ...DBOption) (int64, []model.JobRecords, error) {
|
||||
var users []model.JobRecords
|
||||
db := global.DB.Model(&model.JobRecords{})
|
||||
for _, opt := range opts {
|
||||
db = opt(db)
|
||||
}
|
||||
count := int64(0)
|
||||
db = db.Count(&count)
|
||||
err := db.Limit(size).Offset(size * (page - 1)).Find(&users).Error
|
||||
return count, users, err
|
||||
}
|
||||
|
||||
func (u *CronjobRepo) Create(cronjob *model.Cronjob) error {
|
||||
return global.DB.Create(cronjob).Error
|
||||
}
|
||||
|
||||
func (c *CronjobRepo) WithByDate(startTime, endTime time.Time) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("start_time > ? AND start_time < ?", startTime, endTime)
|
||||
}
|
||||
}
|
||||
func (c *CronjobRepo) WithByJobID(id int) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("cronjob_id = ?", id)
|
||||
}
|
||||
}
|
||||
|
||||
func (u *CronjobRepo) StartRecords(cronjobID uint, targetPath string) model.JobRecords {
|
||||
var record model.JobRecords
|
||||
record.StartTime = time.Now()
|
||||
record.CronjobID = cronjobID
|
||||
record.Status = constant.StatusRunning
|
||||
if err := global.DB.Create(&record).Error; err != nil {
|
||||
global.LOG.Errorf("create record status failed, err: %v", err)
|
||||
}
|
||||
return record
|
||||
}
|
||||
func (u *CronjobRepo) EndRecords(record model.JobRecords, status, message, records string) {
|
||||
errMap := make(map[string]interface{})
|
||||
errMap["records"] = records
|
||||
errMap["status"] = status
|
||||
errMap["message"] = message
|
||||
errMap["interval"] = time.Since(record.StartTime).Milliseconds()
|
||||
if err := global.DB.Model(&model.JobRecords{}).Where("id = ?", record.ID).Updates(errMap).Error; err != nil {
|
||||
global.LOG.Errorf("update record status failed, err: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (u *CronjobRepo) Save(id uint, cronjob model.Cronjob) error {
|
||||
return global.DB.Model(&model.Cronjob{}).Where("id = ?", id).Save(&cronjob).Error
|
||||
}
|
||||
func (u *CronjobRepo) Update(id uint, vars map[string]interface{}) error {
|
||||
return global.DB.Model(&model.Cronjob{}).Where("id = ?", id).Updates(vars).Error
|
||||
}
|
||||
|
@ -1,10 +1,16 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/app/model"
|
||||
"github.com/1Panel-dev/1Panel/constant"
|
||||
"github.com/1Panel-dev/1Panel/global"
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
@ -13,8 +19,9 @@ type CronjobService struct{}
|
||||
|
||||
type ICronjobService interface {
|
||||
SearchWithPage(search dto.SearchWithPage) (int64, interface{}, error)
|
||||
SearchRecords(search dto.SearchRecord) (int64, interface{}, error)
|
||||
Create(cronjobDto dto.CronjobCreate) error
|
||||
Update(id uint, upMap map[string]interface{}) error
|
||||
Save(id uint, req dto.CronjobUpdate) error
|
||||
Delete(ids []uint) error
|
||||
}
|
||||
|
||||
@ -43,6 +50,24 @@ func (u *CronjobService) SearchWithPage(search dto.SearchWithPage) (int64, inter
|
||||
return total, dtoCronjobs, err
|
||||
}
|
||||
|
||||
func (u *CronjobService) SearchRecords(search dto.SearchRecord) (int64, interface{}, error) {
|
||||
total, records, err := cronjobRepo.PageRecords(
|
||||
search.Page,
|
||||
search.PageSize,
|
||||
commonRepo.WithByStatus(search.Status),
|
||||
cronjobRepo.WithByJobID(search.CronjobID),
|
||||
cronjobRepo.WithByDate(search.StartTime, search.EndTime))
|
||||
var dtoCronjobs []dto.Record
|
||||
for _, record := range records {
|
||||
var item dto.Record
|
||||
if err := copier.Copy(&item, &record); err != nil {
|
||||
return 0, nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
|
||||
}
|
||||
dtoCronjobs = append(dtoCronjobs, item)
|
||||
}
|
||||
return total, dtoCronjobs, err
|
||||
}
|
||||
|
||||
func (u *CronjobService) Create(cronjobDto dto.CronjobCreate) error {
|
||||
cronjob, _ := cronjobRepo.Get(commonRepo.WithByName(cronjobDto.Name))
|
||||
if cronjob.ID != 0 {
|
||||
@ -51,23 +76,20 @@ func (u *CronjobService) Create(cronjobDto dto.CronjobCreate) error {
|
||||
if err := copier.Copy(&cronjob, &cronjobDto); err != nil {
|
||||
return errors.WithMessage(constant.ErrStructTransform, err.Error())
|
||||
}
|
||||
switch cronjobDto.SpecType {
|
||||
case "perMonth":
|
||||
cronjob.Spec = fmt.Sprintf("%v %v %v * *", cronjobDto.Minute, cronjobDto.Hour, cronjobDto.Day)
|
||||
case "perWeek":
|
||||
cronjob.Spec = fmt.Sprintf("%v %v * * %v", cronjobDto.Minute, cronjobDto.Hour, cronjobDto.Week)
|
||||
case "perNDay":
|
||||
cronjob.Spec = fmt.Sprintf("%v %v */%v * *", cronjobDto.Minute, cronjobDto.Hour, cronjobDto.Day)
|
||||
case "perNHour":
|
||||
cronjob.Spec = fmt.Sprintf("%v */%v * * *", cronjobDto.Minute, cronjobDto.Hour)
|
||||
case "perHour":
|
||||
cronjob.Spec = fmt.Sprintf("%v * * * *", cronjobDto.Minute)
|
||||
case "perNMinute":
|
||||
cronjob.Spec = fmt.Sprintf("@every %vm", cronjobDto.Minute)
|
||||
}
|
||||
cronjob.Status = constant.StatusEnable
|
||||
cronjob.Spec = loadSpec(cronjob)
|
||||
|
||||
if err := cronjobRepo.Create(&cronjob); err != nil {
|
||||
return err
|
||||
}
|
||||
switch cronjobDto.Type {
|
||||
case "shell":
|
||||
entryID, err := u.AddShellJob(&cronjob)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_ = cronjobRepo.Update(cronjob.ID, map[string]interface{}{"entry_id": entryID})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -82,6 +104,73 @@ func (u *CronjobService) Delete(ids []uint) error {
|
||||
return cronjobRepo.Delete(commonRepo.WithIdsIn(ids))
|
||||
}
|
||||
|
||||
func (u *CronjobService) Update(id uint, upMap map[string]interface{}) error {
|
||||
return cronjobRepo.Update(id, upMap)
|
||||
func (u *CronjobService) Save(id uint, req dto.CronjobUpdate) error {
|
||||
var cronjob model.Cronjob
|
||||
if err := copier.Copy(&cronjob, &req); err != nil {
|
||||
return errors.WithMessage(constant.ErrStructTransform, err.Error())
|
||||
}
|
||||
return cronjobRepo.Save(id, cronjob)
|
||||
}
|
||||
|
||||
func (u *CronjobService) AddShellJob(cronjob *model.Cronjob) (int, error) {
|
||||
addFunc := func() {
|
||||
record := cronjobRepo.StartRecords(cronjob.ID, "")
|
||||
|
||||
cmd := exec.Command(cronjob.Script)
|
||||
stdout, err := cmd.Output()
|
||||
if err != nil {
|
||||
cronjobRepo.EndRecords(record, constant.StatusFailed, err.Error(), "ERR_GENERAGE_STDOUT")
|
||||
return
|
||||
}
|
||||
record.Records, err = mkdirAndWriteFile(cronjob.ID, cronjob.Name, record.StartTime, stdout)
|
||||
if err != nil {
|
||||
record.Records = "ERR_CREATE_FILE"
|
||||
global.LOG.Errorf("save file %s failed, err: %v", record.Records, err)
|
||||
}
|
||||
cronjobRepo.EndRecords(record, constant.StatusSuccess, "", record.Records)
|
||||
}
|
||||
entryID, err := global.Cron.AddFunc(cronjob.Spec, addFunc)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return int(entryID), nil
|
||||
}
|
||||
|
||||
func mkdirAndWriteFile(id uint, name string, startTime time.Time, msg []byte) (string, error) {
|
||||
dir := fmt.Sprintf("/opt/1Panel/data/cron/%s%v", name, id)
|
||||
if _, err := os.Stat(dir); err != nil && os.IsNotExist(err) {
|
||||
if err = os.MkdirAll(dir, os.ModePerm); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
path := fmt.Sprintf("%s/%s", dir, startTime.Format("20060102150405"))
|
||||
file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0666)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer file.Close()
|
||||
write := bufio.NewWriter(file)
|
||||
_, _ = write.WriteString(string(msg))
|
||||
write.Flush()
|
||||
return path, nil
|
||||
}
|
||||
|
||||
func loadSpec(cronjob model.Cronjob) string {
|
||||
switch cronjob.SpecType {
|
||||
case "perMonth":
|
||||
return fmt.Sprintf("%v %v %v * *", cronjob.Minute, cronjob.Hour, cronjob.Day)
|
||||
case "perWeek":
|
||||
return fmt.Sprintf("%v %v * * %v", cronjob.Minute, cronjob.Hour, cronjob.Week)
|
||||
case "perNDay":
|
||||
return fmt.Sprintf("%v %v */%v * *", cronjob.Minute, cronjob.Hour, cronjob.Day)
|
||||
case "perNHour":
|
||||
return fmt.Sprintf("%v */%v * * *", cronjob.Minute, cronjob.Hour)
|
||||
case "perHour":
|
||||
return fmt.Sprintf("%v * * * *", cronjob.Minute)
|
||||
case "perNMinute":
|
||||
return fmt.Sprintf("@every %vm", cronjob.Minute)
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
11
backend/constant/status.go
Normal file
11
backend/constant/status.go
Normal file
@ -0,0 +1,11 @@
|
||||
package constant
|
||||
|
||||
const (
|
||||
StatusRunning = "Running"
|
||||
StatusStoped = "Stoped"
|
||||
StatusWaiting = "Waiting"
|
||||
StatusSuccess = "Success"
|
||||
StatusFailed = "Failed"
|
||||
StatusEnable = "Enable"
|
||||
StatusDisable = "Disable"
|
||||
)
|
@ -11,6 +11,14 @@ import (
|
||||
func Run() {
|
||||
nyc, _ := time.LoadLocation("Asia/Shanghai")
|
||||
Cron := cron.New(cron.WithLocation(nyc))
|
||||
|
||||
// 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())
|
||||
if err != nil {
|
||||
global.LOG.Errorf("can not add corn job: %s", err.Error())
|
||||
|
@ -140,7 +140,6 @@ var AddTableBackupAccount = &gormigrate.Migration{
|
||||
var AddTableCronjob = &gormigrate.Migration{
|
||||
ID: "20200921-add-table-cronjob",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
return tx.AutoMigrate(&model.Cronjob{})
|
||||
|
||||
return tx.AutoMigrate(&model.Cronjob{}, &model.JobRecords{})
|
||||
},
|
||||
}
|
||||
|
@ -18,5 +18,7 @@ func (s *CronjobRouter) InitCronjobRouter(Router *gin.RouterGroup) {
|
||||
withRecordRouter.POST("/del", baseApi.DeleteCronjob)
|
||||
withRecordRouter.PUT(":id", baseApi.UpdateCronjob)
|
||||
cmdRouter.POST("/search", baseApi.SearchCronjob)
|
||||
cmdRouter.POST("/search/records", baseApi.SearchJobRecords)
|
||||
cmdRouter.POST("/search/detail", baseApi.LoadRecordDetail)
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
import { ReqPage } from '.';
|
||||
|
||||
export namespace Cronjob {
|
||||
export interface CronjobInfo {
|
||||
id: number;
|
||||
@ -56,4 +58,20 @@ export namespace Cronjob {
|
||||
retainCopies: number;
|
||||
status: string;
|
||||
}
|
||||
export interface SearchRecord extends ReqPage {
|
||||
cronjobID: number;
|
||||
startTime: Date;
|
||||
endTime: Date;
|
||||
status: string;
|
||||
}
|
||||
export interface Record {
|
||||
id: number;
|
||||
startTime: Date;
|
||||
endTime: Date;
|
||||
records: string;
|
||||
status: string;
|
||||
message: string;
|
||||
targetPath: string;
|
||||
interval: number;
|
||||
}
|
||||
}
|
||||
|
@ -17,3 +17,11 @@ export const editCronjob = (params: Cronjob.CronjobUpdate) => {
|
||||
export const deleteCronjob = (params: { ids: number[] }) => {
|
||||
return http.post(`/cronjobs/del`, params);
|
||||
};
|
||||
|
||||
export const searchRecords = (params: Cronjob.SearchRecord) => {
|
||||
return http.post<ResPage<Cronjob.Record>>(`cronjobs/search/records`, params);
|
||||
};
|
||||
|
||||
export const getRecordDetail = (params: string) => {
|
||||
return http.post<string>(`cronjobs/search/detail`, { path: params });
|
||||
};
|
||||
|
@ -18,6 +18,8 @@ export default {
|
||||
login: '登录',
|
||||
close: '关闭',
|
||||
view: '详情',
|
||||
expand: '展开',
|
||||
log: '日志',
|
||||
saveAndEnable: '保存并启用',
|
||||
},
|
||||
search: {
|
||||
@ -31,6 +33,9 @@ export default {
|
||||
name: '名称',
|
||||
type: '类型',
|
||||
status: '状态',
|
||||
statusSuccess: '成功',
|
||||
statusFailed: '失败',
|
||||
records: '任务输出',
|
||||
group: '组',
|
||||
createdAt: '创建时间',
|
||||
date: '时间',
|
||||
@ -38,6 +43,7 @@ export default {
|
||||
operate: '操作',
|
||||
message: '信息',
|
||||
description: '描述信息',
|
||||
interval: '耗时',
|
||||
},
|
||||
msg: {
|
||||
delete: '此操作不可回滚,是否继续',
|
||||
@ -48,6 +54,7 @@ export default {
|
||||
notSupportOperation: '不支持的当前操作',
|
||||
requestTimeout: '请求超时,请稍后重试',
|
||||
infoTitle: '提示',
|
||||
notRecords: '当前任务未产生执行记录',
|
||||
sureLogOut: '您是否确认退出登录?',
|
||||
createSuccess: '新建成功',
|
||||
updateSuccess: '更新成功',
|
||||
@ -139,6 +146,7 @@ export default {
|
||||
taskType: '任务类型',
|
||||
shell: 'Shell 脚本',
|
||||
website: '备份网站',
|
||||
failedFilter: '失败任务过滤',
|
||||
all: '所有',
|
||||
database: '备份数据库',
|
||||
missBackupAccount: '未能找到备份账号',
|
||||
|
@ -13,17 +13,6 @@
|
||||
{{ $t('commons.button.delete') }}
|
||||
</el-button>
|
||||
</template>
|
||||
<el-table-column type="expand">
|
||||
<template #default="{ row }">
|
||||
<ul>
|
||||
<li>{{ row.name }} {{ $t('cronjob.handle') }}记录 1</li>
|
||||
<li>{{ row.name }} {{ $t('cronjob.handle') }}记录 2</li>
|
||||
<li>{{ row.name }} {{ $t('cronjob.handle') }}记录 3</li>
|
||||
<li>{{ row.name }} {{ $t('cronjob.handle') }}记录 4</li>
|
||||
</ul>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column type="selection" fix />
|
||||
<el-table-column :label="$t('cronjob.taskName')" prop="name" />
|
||||
<el-table-column :label="$t('commons.table.status')" prop="status">
|
||||
@ -33,11 +22,12 @@
|
||||
:before-change="beforeChangeStatus"
|
||||
v-model="row.status"
|
||||
inline-prompt
|
||||
size="default"
|
||||
style="--el-switch-on-color: #13ce66; --el-switch-off-color: #ff4949"
|
||||
active-text="Y"
|
||||
inactive-text="N"
|
||||
active-value="running"
|
||||
inactive-value="stoped"
|
||||
active-value="Enable"
|
||||
inactive-value="Disable"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@ -71,12 +61,15 @@
|
||||
</ComplexTable>
|
||||
|
||||
<OperatrDialog @search="search" ref="dialogRef" />
|
||||
<RecordDialog ref="dialogRecordRef" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import ComplexTable from '@/components/complex-table/index.vue';
|
||||
import OperatrDialog from '@/views/cronjob/operate/index.vue';
|
||||
import RecordDialog from '@/views/cronjob/record/index.vue';
|
||||
import { loadZero } from '@/views/cronjob/options';
|
||||
import { onMounted, reactive, ref } from 'vue';
|
||||
import { loadBackupName } from '@/views/setting/helper';
|
||||
import { deleteCronjob, editCronjob, getCronjobPage } from '@/api/modules/cronjob';
|
||||
@ -85,6 +78,7 @@ import i18n from '@/lang';
|
||||
import { Cronjob } from '@/api/interface/cronjob';
|
||||
import { useDeleteData } from '@/hooks/use-delete-data';
|
||||
import { ElMessage } from 'element-plus';
|
||||
|
||||
const selects = ref<any>([]);
|
||||
const switchState = ref<boolean>(false);
|
||||
|
||||
@ -104,7 +98,7 @@ const search = async () => {
|
||||
logSearch.page = paginationConfig.currentPage;
|
||||
logSearch.pageSize = paginationConfig.pageSize;
|
||||
const res = await getCronjobPage(logSearch);
|
||||
data.value = res.data.items;
|
||||
data.value = res.data.items || [];
|
||||
for (const item of data.value) {
|
||||
if (item.targetDir !== '-') {
|
||||
item.targetDir = loadBackupName(item.targetDir);
|
||||
@ -113,6 +107,8 @@ const search = async () => {
|
||||
paginationConfig.total = res.data.total;
|
||||
};
|
||||
|
||||
const dialogRecordRef = ref<DialogExpose>();
|
||||
|
||||
interface DialogExpose {
|
||||
acceptParams: (params: any) => void;
|
||||
}
|
||||
@ -131,7 +127,6 @@ const onOpenDialog = async (
|
||||
let params = {
|
||||
title,
|
||||
rowData: { ...rowData },
|
||||
isView: title === 'view',
|
||||
};
|
||||
dialogRef.value!.acceptParams(params);
|
||||
};
|
||||
@ -154,7 +149,6 @@ const beforeChangeStatus = () => {
|
||||
};
|
||||
const onChangeStatus = async (row: Cronjob.CronjobInfo) => {
|
||||
if (switchState.value) {
|
||||
console.log(row.status);
|
||||
await editCronjob(row);
|
||||
ElMessage.success(i18n.global.t('commons.msg.operationSuccess'));
|
||||
search();
|
||||
@ -169,13 +163,6 @@ const buttons = [
|
||||
onOpenDialog('edit', row);
|
||||
},
|
||||
},
|
||||
{
|
||||
label: i18n.global.t('commons.button.view'),
|
||||
icon: 'View',
|
||||
click: (row: Cronjob.CronjobInfo) => {
|
||||
onOpenDialog('view', row);
|
||||
},
|
||||
},
|
||||
{
|
||||
label: i18n.global.t('commons.button.delete'),
|
||||
icon: 'Delete',
|
||||
@ -183,11 +170,20 @@ const buttons = [
|
||||
onBatchDelete(row);
|
||||
},
|
||||
},
|
||||
{
|
||||
label: i18n.global.t('commons.button.log'),
|
||||
icon: 'Clock',
|
||||
click: (row: Cronjob.CronjobInfo) => {
|
||||
onOpenRecordDialog(row);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
function loadZero(i: number) {
|
||||
return i < 10 ? '0' + i : '' + i;
|
||||
}
|
||||
const onOpenRecordDialog = async (rowData: Partial<Cronjob.CronjobInfo> = {}) => {
|
||||
let params = {
|
||||
rowData: { ...rowData },
|
||||
};
|
||||
dialogRecordRef.value!.acceptParams(params);
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
search();
|
||||
|
@ -5,15 +5,7 @@
|
||||
<span>{{ title }}{{ $t('cronjob.cronTask') }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<el-form
|
||||
:disabled="dialogData.isView"
|
||||
ref="formRef"
|
||||
:model="dialogData.rowData"
|
||||
label-position="left"
|
||||
:rules="rules"
|
||||
label-width="120px"
|
||||
:hide-required-asterisk="dialogData.isView"
|
||||
>
|
||||
<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-select
|
||||
@change="changeName(true, dialogData.rowData!.type, dialogData.rowData!.website)"
|
||||
@ -134,7 +126,7 @@
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="cronjobVisiable = false">{{ $t('commons.button.cancel') }}</el-button>
|
||||
<el-button v-show="!dialogData.isView" type="primary" @click="onSubmit(formRef)">
|
||||
<el-button type="primary" @click="onSubmit(formRef)">
|
||||
{{ $t('commons.button.confirm') }}
|
||||
</el-button>
|
||||
</span>
|
||||
@ -146,7 +138,7 @@
|
||||
import { onMounted, reactive, ref } from 'vue';
|
||||
import { Rules } from '@/global/form-rules';
|
||||
import { loadBackupName } from '@/views/setting/helper';
|
||||
import { typeOptions, specOptions, weekOptions } from '../options';
|
||||
import { typeOptions, specOptions, weekOptions } from '@/views/cronjob/options';
|
||||
import FileList from '@/components/file-list/index.vue';
|
||||
import { getBackupList } from '@/api/modules/backup';
|
||||
import i18n from '@/lang';
|
||||
@ -156,14 +148,12 @@ import { addCronjob, editCronjob } from '@/api/modules/cronjob';
|
||||
|
||||
interface DialogProps {
|
||||
title: string;
|
||||
isView: boolean;
|
||||
rowData?: Cronjob.CronjobInfo;
|
||||
getTableList?: () => Promise<any>;
|
||||
}
|
||||
const title = ref<string>('');
|
||||
const cronjobVisiable = ref(false);
|
||||
const dialogData = ref<DialogProps>({
|
||||
isView: false,
|
||||
title: '',
|
||||
});
|
||||
const acceptParams = (params: DialogProps): void => {
|
||||
|
@ -36,3 +36,6 @@ export const loadWeek = (i: number) => {
|
||||
}
|
||||
return '';
|
||||
};
|
||||
export const loadZero = (i: number) => {
|
||||
return i < 10 ? '0' + i : '' + i;
|
||||
};
|
||||
|
376
frontend/src/views/cronjob/record/index.vue
Normal file
376
frontend/src/views/cronjob/record/index.vue
Normal file
@ -0,0 +1,376 @@
|
||||
<template>
|
||||
<el-dialog v-model="cronjobVisiable" :destroy-on-close="true" :close-on-click-modal="false" width="70%">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>{{ title }}{{ $t('cronjob.cronTask') }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<el-date-picker
|
||||
@change="search()"
|
||||
v-model="timeRangeLoad"
|
||||
type="datetimerange"
|
||||
:range-separator="$t('commons.search.timeRange')"
|
||||
:start-placeholder="$t('commons.search.timeStart')"
|
||||
:end-placeholder="$t('commons.search.timeEnd')"
|
||||
:shortcuts="shortcuts"
|
||||
></el-date-picker>
|
||||
<el-checkbox style="margin-left: 20px" @change="search()" v-model="searchInfo.status">
|
||||
{{ $t('cronjob.failedFilter') }}
|
||||
</el-checkbox>
|
||||
<el-row :gutter="20" style="margin-top: 20px">
|
||||
<el-col :span="6">
|
||||
<el-card>
|
||||
<ul v-infinite-scroll="nextPage" class="infinite-list" style="overflow: auto">
|
||||
<li
|
||||
v-for="(item, index) in records"
|
||||
:key="index"
|
||||
@click="forDetail(item)"
|
||||
class="infinite-list-item"
|
||||
>
|
||||
<el-icon v-if="item.status === 'Success'"><Select /></el-icon>
|
||||
<el-icon v-if="item.status === 'Failed'"><CloseBold /></el-icon>
|
||||
{{ dateFromat(0, 0, item.startTime) }}
|
||||
</li>
|
||||
</ul>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="18">
|
||||
<el-card style="height: 340px">
|
||||
<el-form>
|
||||
<el-row>
|
||||
<el-col :span="8">
|
||||
<el-form-item :label="$t('cronjob.taskType')">
|
||||
{{ dialogData.rowData?.type }}
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item :label="$t('cronjob.taskName')">
|
||||
{{ dialogData.rowData?.name }}
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item :label="$t('cronjob.cronSpec')">
|
||||
<span
|
||||
v-if="
|
||||
dialogData.rowData?.specType.indexOf('N') === -1 ||
|
||||
dialogData.rowData?.specType === 'perWeek'
|
||||
"
|
||||
>
|
||||
{{ $t('cronjob.' + dialogData.rowData?.specType) }}
|
||||
</span>
|
||||
<span v-else>{{ $t('cronjob.per') }}</span>
|
||||
<span v-if="dialogData.rowData?.specType === 'perMonth'">
|
||||
{{ dialogData.rowData?.day }}{{ $t('cronjob.day') }}
|
||||
{{ loadZero(dialogData.rowData?.hour) }} :
|
||||
{{ loadZero(dialogData.rowData?.minute) }}
|
||||
</span>
|
||||
<span v-if="dialogData.rowData?.specType === 'perWeek'">
|
||||
{{ loadWeek(dialogData.rowData?.week) }}
|
||||
{{ loadZero(dialogData.rowData?.hour) }} :
|
||||
{{ loadZero(dialogData.rowData?.minute) }}
|
||||
</span>
|
||||
<span v-if="dialogData.rowData?.specType === 'perNDay'">
|
||||
{{ dialogData.rowData?.day }}{{ $t('cronjob.day1') }},
|
||||
{{ loadZero(dialogData.rowData?.hour) }} :
|
||||
{{ loadZero(dialogData.rowData?.minute) }}
|
||||
</span>
|
||||
<span v-if="dialogData.rowData?.specType === 'perNHour'">
|
||||
{{ dialogData.rowData?.hour }}{{ $t('cronjob.hour') }},
|
||||
{{ loadZero(dialogData.rowData?.minute) }}
|
||||
</span>
|
||||
<span v-if="dialogData.rowData?.specType === 'perHour'">
|
||||
{{ loadZero(dialogData.rowData?.minute) }}
|
||||
</span>
|
||||
<span v-if="dialogData.rowData?.specType === 'perNMinute'">
|
||||
{{ dialogData.rowData?.minute }}{{ $t('cronjob.minute') }}
|
||||
</span>
|
||||
{{ $t('cronjob.handle') }}
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8" v-if="hasScript()">
|
||||
<el-form-item :label="$t('cronjob.shellContent')">
|
||||
<el-popover
|
||||
placement="right"
|
||||
:width="600"
|
||||
trigger="click"
|
||||
style="white-space: pre-wrap"
|
||||
>
|
||||
<div style="margin-left: 20px">
|
||||
<span style="white-space: pre-wrap">{{ dialogData.rowData!.script }}</span>
|
||||
</div>
|
||||
<template #reference>
|
||||
<el-button link>{{ $t('commons.button.expand') }}</el-button>
|
||||
</template>
|
||||
</el-popover>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8" v-if="dialogData.rowData!.type === 'website'">
|
||||
<el-form-item :label="$t('cronjob.website')">
|
||||
{{ dialogData.rowData!.website }}
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8" v-if="dialogData.rowData!.type === 'database'">
|
||||
<el-form-item :label="$t('cronjob.database')">
|
||||
{{ dialogData.rowData!.database }}
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8" v-if="dialogData.rowData!.type === 'directory'">
|
||||
<el-form-item :label="$t('cronjob.directory')">
|
||||
{{ dialogData.rowData!.sourceDir }}
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8" v-if="isBackup()">
|
||||
<el-form-item :label="$t('cronjob.target')">
|
||||
{{ dialogData.rowData!.targetDirID }}
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8" v-if="isBackup()">
|
||||
<el-form-item :label="$t('cronjob.retainCopies')">
|
||||
{{ dialogData.rowData!.retainCopies }}
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8" v-if="dialogData.rowData!.type === 'curl'">
|
||||
<el-form-item :label="$t('cronjob.url') + 'URL'">
|
||||
{{ dialogData.rowData!.url }}
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col
|
||||
:span="8"
|
||||
v-if="dialogData.rowData!.type === 'website' || dialogData.rowData!.type === 'directory'"
|
||||
>
|
||||
<el-form-item :label="$t('cronjob.exclusionRules')">
|
||||
{{ dialogData.rowData!.exclusionRules }}
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="8">
|
||||
<el-form-item :label="$t('commons.search.timeStart')">
|
||||
{{ dateFromat(0, 0, currentRecord?.startTime) }}
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item :label="$t('commons.table.interval')">
|
||||
<span v-if="currentRecord?.interval! <= 1000">
|
||||
{{ currentRecord?.interval }} ms
|
||||
</span>
|
||||
<span v-if="currentRecord?.interval! > 1000">
|
||||
{{ currentRecord?.interval! / 1000 }} s
|
||||
</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item :label="$t('commons.table.status')">
|
||||
<el-tooltip
|
||||
v-if="currentRecord?.status === 'Failed'"
|
||||
class="box-item"
|
||||
:content="currentRecord?.message"
|
||||
placement="top"
|
||||
>
|
||||
{{ $t('commons.table.statusFailed') }}
|
||||
</el-tooltip>
|
||||
<span v-else>{{ $t('commons.table.statusSuccess') }}</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<el-form-item :label="$t('commons.table.records')">
|
||||
<el-popover
|
||||
placement="right"
|
||||
:width="600"
|
||||
trigger="click"
|
||||
style="white-space: pre-wrap"
|
||||
>
|
||||
<div style="margin-left: 20px">
|
||||
<span style="white-space: pre-wrap">
|
||||
{{ currentRecordDetail }}
|
||||
</span>
|
||||
</div>
|
||||
<template #reference>
|
||||
<el-button link @click="loadRecord(currentRecord?.records!)">
|
||||
{{ $t('commons.button.expand') }}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-popover>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="cronjobVisiable = false">{{ $t('commons.button.cancel') }}</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { reactive, ref } from 'vue';
|
||||
import { Cronjob } from '@/api/interface/cronjob';
|
||||
import { loadZero, loadWeek } from '@/views/cronjob/options';
|
||||
import { searchRecords, getRecordDetail } from '@/api/modules/cronjob';
|
||||
import { dateFromat } from '@/utils/util';
|
||||
import i18n from '@/lang';
|
||||
import { ElMessage } from 'element-plus';
|
||||
|
||||
interface DialogProps {
|
||||
rowData?: Cronjob.CronjobInfo;
|
||||
}
|
||||
const title = ref<string>('');
|
||||
const cronjobVisiable = ref(false);
|
||||
const dialogData = ref<DialogProps>({});
|
||||
const records = ref<Array<Cronjob.Record>>();
|
||||
const currentRecord = ref<Cronjob.Record>();
|
||||
const currentRecordDetail = ref<string>('');
|
||||
|
||||
const acceptParams = async (params: DialogProps): Promise<void> => {
|
||||
dialogData.value = params;
|
||||
let itemSearch = {
|
||||
page: searchInfo.page,
|
||||
pageSize: searchInfo.pageSize,
|
||||
cronjobID: dialogData.value.rowData!.id,
|
||||
startTime: searchInfo.startTime,
|
||||
endTime: searchInfo.endTime,
|
||||
status: searchInfo.status ? 'Stoped' : '',
|
||||
};
|
||||
const res = await searchRecords(itemSearch);
|
||||
records.value = res.data.items || [];
|
||||
if (records.value.length === 0) {
|
||||
ElMessage.info(i18n.global.t('commons.msg.notRecords'));
|
||||
return;
|
||||
}
|
||||
currentRecord.value = records.value[0];
|
||||
searchInfo.recordTotal = res.data.total;
|
||||
cronjobVisiable.value = true;
|
||||
};
|
||||
|
||||
const shortcuts = [
|
||||
{
|
||||
text: i18n.global.t('monitor.today'),
|
||||
value: () => {
|
||||
const end = new Date();
|
||||
const start = new Date(new Date().setHours(0, 0, 0, 0));
|
||||
return [start, end];
|
||||
},
|
||||
},
|
||||
{
|
||||
text: i18n.global.t('monitor.yestoday'),
|
||||
value: () => {
|
||||
const yestoday = new Date(new Date().getTime() - 3600 * 1000 * 24 * 1);
|
||||
const end = new Date(yestoday.setHours(23, 59, 59, 999));
|
||||
const start = new Date(yestoday.setHours(0, 0, 0, 0));
|
||||
return [start, end];
|
||||
},
|
||||
},
|
||||
{
|
||||
text: i18n.global.t('monitor.lastNDay', [3]),
|
||||
value: () => {
|
||||
const start = new Date(new Date().getTime() - 3600 * 1000 * 24 * 3);
|
||||
const end = new Date();
|
||||
return [start, end];
|
||||
},
|
||||
},
|
||||
{
|
||||
text: i18n.global.t('monitor.lastNDay', [7]),
|
||||
value: () => {
|
||||
const start = new Date(new Date().getTime() - 3600 * 1000 * 24 * 7);
|
||||
const end = new Date();
|
||||
return [start, end];
|
||||
},
|
||||
},
|
||||
{
|
||||
text: i18n.global.t('monitor.lastNDay', [30]),
|
||||
value: () => {
|
||||
const start = new Date(new Date().getTime() - 3600 * 1000 * 24 * 30);
|
||||
const end = new Date();
|
||||
return [start, end];
|
||||
},
|
||||
},
|
||||
];
|
||||
const timeRangeLoad = ref<Array<any>>([new Date(new Date().setHours(0, 0, 0, 0)), new Date()]);
|
||||
const searchInfo = reactive({
|
||||
page: 1,
|
||||
pageSize: 5,
|
||||
recordTotal: 0,
|
||||
cronjobID: 0,
|
||||
startTime: new Date(new Date().getTime() - 3600 * 1000 * 24 * 30),
|
||||
endTime: new Date(),
|
||||
status: false,
|
||||
});
|
||||
|
||||
const search = async () => {
|
||||
let params = {
|
||||
page: searchInfo.page,
|
||||
pageSize: searchInfo.pageSize,
|
||||
cronjobID: dialogData.value.rowData!.id,
|
||||
startTime: searchInfo.startTime,
|
||||
endTime: searchInfo.endTime,
|
||||
status: searchInfo.status ? 'Failed' : '',
|
||||
};
|
||||
const res = await searchRecords(params);
|
||||
records.value = res.data.items || [];
|
||||
searchInfo.recordTotal = res.data.total;
|
||||
};
|
||||
|
||||
const nextPage = async () => {
|
||||
if (searchInfo.pageSize >= searchInfo.recordTotal) {
|
||||
return;
|
||||
}
|
||||
searchInfo.pageSize = searchInfo.pageSize + 3;
|
||||
search();
|
||||
};
|
||||
const forDetail = async (row: Cronjob.Record) => {
|
||||
currentRecord.value = row;
|
||||
};
|
||||
const loadRecord = async (path: string) => {
|
||||
const res = await getRecordDetail(path);
|
||||
currentRecordDetail.value = res.data;
|
||||
};
|
||||
function isBackup() {
|
||||
return (
|
||||
dialogData.value.rowData!.type === 'website' ||
|
||||
dialogData.value.rowData!.type === 'database' ||
|
||||
dialogData.value.rowData!.type === 'directory'
|
||||
);
|
||||
}
|
||||
function hasScript() {
|
||||
return dialogData.value.rowData!.type === 'shell' || dialogData.value.rowData!.type === 'sync';
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
acceptParams,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.infinite-list {
|
||||
height: 300px;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
}
|
||||
.infinite-list .infinite-list-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 30px;
|
||||
background: var(--el-color-primary-light-9);
|
||||
margin: 10px;
|
||||
color: var(--el-color-primary);
|
||||
cursor: pointer;
|
||||
}
|
||||
.infinite-list .infinite-list-item:hover {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 30px;
|
||||
background: var(--el-color-primary-light-9);
|
||||
margin: 10px;
|
||||
font-weight: 500;
|
||||
color: red;
|
||||
}
|
||||
</style>
|
Loading…
Reference in New Issue
Block a user