1
0
mirror of https://github.com/1Panel-dev/1Panel.git synced 2025-01-31 14:08:06 +08:00

feat: s3 定时备份适配

This commit is contained in:
ssongliu 2022-09-29 11:13:05 +08:00 committed by ssongliu
parent c5e9a2e085
commit 49f0fb10a8
24 changed files with 277 additions and 231 deletions

View File

@ -171,3 +171,16 @@ func (b *BaseApi) TargetDownload(c *gin.Context) {
}
c.File(filePath)
}
func (b *BaseApi) HandleOnce(c *gin.Context) {
id, err := helper.GetParamID(c)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
if err := cronjobService.HandleOnce(id); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}

View File

@ -5,7 +5,7 @@ import "time"
type Cronjob struct {
BaseModel
Name string `gorm:"type:varchar(64);not null" json:"name"`
Name string `gorm:"type:varchar(64);not null;unique" json:"name"`
Type string `gorm:"type:varchar(64);not null" json:"type"`
SpecType string `gorm:"type:varchar(64);not null" json:"specType"`
Spec string `gorm:"type:varchar(64);not null" json:"spec"`

View File

@ -25,7 +25,6 @@ import (
const (
errRecord = "errRecord"
errHandle = "errHandle"
noRecord = "noRecord"
)
type CronjobService struct{}
@ -34,6 +33,7 @@ type ICronjobService interface {
SearchWithPage(search dto.SearchWithPage) (int64, interface{}, error)
SearchRecords(search dto.SearchRecord) (int64, interface{}, error)
Create(cronjobDto dto.CronjobCreate) error
HandleOnce(id uint) error
Save(id uint, req dto.CronjobUpdate) error
UpdateStatus(id uint, status string) error
Delete(ids []uint) error
@ -143,6 +143,15 @@ func (u *CronjobService) Download(down dto.CronjobDownload) (string, error) {
return name, nil
}
func (u *CronjobService) HandleOnce(id uint) error {
cronjob, _ := cronjobRepo.Get(commonRepo.WithByID(id))
if cronjob.ID == 0 {
return constant.ErrRecordNotFound
}
u.HandleJob(&cronjob)
return nil
}
func (u *CronjobService) Create(cronjobDto dto.CronjobCreate) error {
cronjob, _ := cronjobRepo.Get(commonRepo.WithByName(cronjobDto.Name))
if cronjob.ID != 0 {
@ -165,24 +174,7 @@ func (u *CronjobService) Create(cronjobDto dto.CronjobCreate) error {
func (u *CronjobService) StartJob(cronjob *model.Cronjob) error {
global.Cron.Remove(cron.EntryID(cronjob.EntryID))
var (
entryID int
err error
)
switch cronjob.Type {
case "shell":
entryID, err = u.AddShellJob(cronjob)
case "curl":
entryID, err = u.AddCurlJob(cronjob)
case "directory":
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)
}
entryID, err := u.AddCronJob(cronjob)
if err != nil {
return err
}
@ -244,23 +236,9 @@ func (u *CronjobService) UpdateStatus(id uint, status string) error {
return cronjobRepo.Update(cronjob.ID, map[string]interface{}{"status": status})
}
func (u *CronjobService) AddShellJob(cronjob *model.Cronjob) (int, error) {
func (u *CronjobService) AddCronJob(cronjob *model.Cronjob) (int, error) {
addFunc := func() {
record := cronjobRepo.StartRecords(cronjob.ID, "")
cmd := exec.Command(cronjob.Script)
stdout, err := cmd.CombinedOutput()
if err != nil {
record.Records = errHandle
cronjobRepo.EndRecords(record, constant.StatusFailed, err.Error(), errHandle)
return
}
record.Records, err = mkdirAndWriteFile(cronjob, record.StartTime, stdout)
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)
u.HandleJob(cronjob)
}
global.LOG.Infof("add %s job %s successful", cronjob.Type, cronjob.Name)
entryID, err := global.Cron.AddFunc(cronjob.Spec, addFunc)
@ -270,9 +248,26 @@ func (u *CronjobService) AddShellJob(cronjob *model.Cronjob) (int, error) {
return int(entryID), nil
}
func (u *CronjobService) AddCurlJob(cronjob *model.Cronjob) (int, error) {
addFunc := func() {
func (u *CronjobService) HandleJob(cronjob *model.Cronjob) {
var (
message []byte
err error
)
record := cronjobRepo.StartRecords(cronjob.ID, "")
switch cronjob.Type {
case "shell":
cmd := exec.Command(cronjob.Script)
message, err = cmd.CombinedOutput()
case "website":
message, err = tarWithExclude(cronjob, record.StartTime)
case "database":
message, err = tarWithExclude(cronjob, record.StartTime)
case "directory":
if len(cronjob.SourceDir) == 0 {
return
}
message, err = tarWithExclude(cronjob, record.StartTime)
case "curl":
if len(cronjob.URL) == 0 {
return
}
@ -287,30 +282,8 @@ func (u *CronjobService) AddCurlJob(cronjob *model.Cronjob) (int, error) {
cronjobRepo.EndRecords(record, constant.StatusFailed, err.Error(), errHandle)
}
defer response.Body.Close()
stdout, _ := ioutil.ReadAll(response.Body)
record.Records, err = mkdirAndWriteFile(cronjob, record.StartTime, stdout)
if err != nil {
record.Records = errRecord
global.LOG.Errorf("save file %s failed, err: %v", record.Records, err)
message, _ = ioutil.ReadAll(response.Body)
}
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) AddDirectoryJob(cronjob *model.Cronjob) (int, error) {
addFunc := func() {
record := cronjobRepo.StartRecords(cronjob.ID, "")
if len(cronjob.SourceDir) == 0 {
return
}
message, err := tarWithExclude(cronjob, record.StartTime)
if err != nil {
record.Records = errHandle
cronjobRepo.EndRecords(record, constant.StatusFailed, err.Error(), errHandle)
@ -323,72 +296,6 @@ func (u *CronjobService) AddDirectoryJob(cronjob *model.Cronjob) (int, error) {
}
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) AddWebSiteJob(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
}
if len(message) == 0 {
record.Records = noRecord
cronjobRepo.EndRecords(record, constant.StatusSuccess, "", record.Records)
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)
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)
if err != nil {
return 0, err
}
return int(entryID), nil
}
func mkdirAndWriteFile(cronjob *model.Cronjob, startTime time.Time, msg []byte) (string, error) {
dir := fmt.Sprintf("%s%s/%s-%v", constant.TaskDir, cronjob.Type, cronjob.Name, cronjob.ID)
@ -451,7 +358,7 @@ func tarWithExclude(cronjob *model.Cronjob, startTime time.Time) ([]byte, error)
}
}
if backType, ok := varMaps["type"].(string); ok {
rmOverdueCloud(backType, targetdir, cronjob, backClient)
rmExpiredRecords(backType, targetdir, cronjob, backClient)
}
return stdout, nil
}
@ -493,7 +400,7 @@ func loadTargetInfo(cronjob *model.Cronjob) (map[string]interface{}, string, err
return varMap, dir, nil
}
func rmOverdueCloud(backType, path string, cronjob *model.Cronjob, backClient cloud_storage.CloudStorageClient) {
func rmExpiredRecords(backType, path string, cronjob *model.Cronjob, backClient cloud_storage.CloudStorageClient) {
timeNow := time.Now()
timeZero := time.Date(timeNow.Year(), timeNow.Month(), timeNow.Day(), 0, 0, 0, 0, timeNow.Location())
timeStart := timeZero.AddDate(0, 0, -int(cronjob.RetainDays)+1)
@ -544,7 +451,7 @@ func rmOverdueCloud(backType, path string, cronjob *model.Cronjob, backClient cl
_ = os.Remove(path + "/" + file.Name())
}
}
_ = cronjobRepo.DeleteRecord(cronjobRepo.WithByStartDate(timeStart))
_ = cronjobRepo.DeleteRecord(cronjobRepo.WithByJobID(int(cronjob.ID)), cronjobRepo.WithByStartDate(timeStart))
}
func loadSpec(cronjob model.Cronjob) string {

View File

@ -18,7 +18,8 @@ func (s *CronjobRouter) InitCronjobRouter(Router *gin.RouterGroup) {
withRecordRouter.POST("/del", baseApi.DeleteCronjob)
withRecordRouter.PUT(":id", baseApi.UpdateCronjob)
withRecordRouter.POST("/status", baseApi.UpdateCronjobStatus)
withRecordRouter.POST("/download", baseApi.TargetDownload)
cmdRouter.POST("/handle/:id", baseApi.HandleOnce)
cmdRouter.POST("/download", baseApi.TargetDownload)
cmdRouter.POST("/search", baseApi.SearchCronjob)
cmdRouter.POST("/search/records", baseApi.SearchJobRecords)
cmdRouter.POST("/search/detail", baseApi.LoadRecordDetail)

View File

@ -0,0 +1,40 @@
package client
import (
"encoding/json"
"fmt"
"testing"
"github.com/1Panel-dev/1Panel/app/model"
"github.com/1Panel-dev/1Panel/constant"
"github.com/1Panel-dev/1Panel/global"
"github.com/1Panel-dev/1Panel/init/db"
"github.com/1Panel-dev/1Panel/init/log"
"github.com/1Panel-dev/1Panel/init/viper"
)
func TestMinio(t *testing.T) {
viper.Init()
log.Init()
db.Init()
var backup model.BackupAccount
if err := global.DB.Where("id = ?", 3).First(&backup).Error; err != nil {
fmt.Println(err)
}
varMap := make(map[string]interface{})
if err := json.Unmarshal([]byte(backup.Vars), &varMap); err != nil {
fmt.Println(err)
}
varMap["type"] = backup.Type
varMap["bucket"] = backup.Bucket
switch backup.Type {
case constant.Sftp:
varMap["password"] = backup.Credential
case constant.OSS, constant.S3, constant.MinIo:
varMap["secretKey"] = backup.Credential
}
client, _ := NewMinIoClient(varMap)
_, _ = client.ListObjects("directory/directory-test-minio/")
}

View File

@ -187,7 +187,7 @@ func (s3C *s3Client) ListObjects(prefix string) ([]interface{}, error) {
Prefix: &prefix,
}, func(p *s3.ListObjectsOutput, last bool) (shouldContinue bool) {
for _, obj := range p.Contents {
result = append(result, obj)
result = append(result, *obj.Key)
}
return true
}); err != nil {

View File

@ -0,0 +1,44 @@
package client
import (
"encoding/json"
"fmt"
"testing"
"github.com/1Panel-dev/1Panel/app/model"
"github.com/1Panel-dev/1Panel/constant"
"github.com/1Panel-dev/1Panel/global"
"github.com/1Panel-dev/1Panel/init/db"
"github.com/1Panel-dev/1Panel/init/log"
"github.com/1Panel-dev/1Panel/init/viper"
)
func TestCronS(t *testing.T) {
viper.Init()
log.Init()
db.Init()
var backup model.BackupAccount
if err := global.DB.Where("id = ?", 5).First(&backup).Error; err != nil {
fmt.Println(err)
}
varMap := make(map[string]interface{})
if err := json.Unmarshal([]byte(backup.Vars), &varMap); err != nil {
fmt.Println(err)
}
varMap["type"] = backup.Type
varMap["bucket"] = backup.Bucket
switch backup.Type {
case constant.Sftp:
varMap["password"] = backup.Credential
case constant.OSS, constant.S3, constant.MinIo:
varMap["secretKey"] = backup.Credential
}
client, err := NewS3Client(varMap)
if err != nil {
fmt.Println(err)
}
_, _ = client.ListObjects("directory/directory-test-s3")
}

View File

@ -33,3 +33,7 @@ export const updateStatus = (params: Cronjob.UpdateStatus) => {
export const download = (params: Cronjob.Download) => {
return http.download<BlobPart>(`cronjobs/download`, params, { responseType: 'blob' });
};
export const handleOnce = (params: number) => {
return http.post(`cronjobs/handle/${params}`);
};

View File

@ -1,9 +1,9 @@
@font-face {
font-family: "panel"; /* Project id 3575356 */
src: url('iconfont.woff2?t=1663584463212') format('woff2'),
url('iconfont.woff?t=1663584463212') format('woff'),
url('iconfont.ttf?t=1663584463212') format('truetype'),
url('iconfont.svg?t=1663584463212#panel') format('svg');
src: url('iconfont.woff2?t=1664421291278') format('woff2'),
url('iconfont.woff?t=1664421291278') format('woff'),
url('iconfont.ttf?t=1664421291278') format('truetype'),
url('iconfont.svg?t=1664421291278#panel') format('svg');
}
.panel {
@ -14,12 +14,16 @@
-moz-osx-font-smoothing: grayscale;
}
.p-taolun:before {
content: "\e602";
.p-star:before {
content: "\e60f";
}
.p-StarStar:before {
content: "\e635";
.p-aws:before {
content: "\e600";
}
.p-taolun:before {
content: "\e602";
}
.p-bug:before {
@ -38,10 +42,6 @@
content: "\e607";
}
.p-s3:before {
content: "\e8e4";
}
.p-minio:before {
content: "\e63c";
}

File diff suppressed because one or more lines are too long

View File

@ -5,6 +5,20 @@
"css_prefix_text": "p-",
"description": "",
"glyphs": [
{
"icon_id": "974125",
"name": "star",
"font_class": "star",
"unicode": "e60f",
"unicode_decimal": 58895
},
{
"icon_id": "32101973",
"name": "Amazon_Web_Services_Logo",
"font_class": "aws",
"unicode": "e600",
"unicode_decimal": 58880
},
{
"icon_id": "1760690",
"name": "讨论",
@ -12,13 +26,6 @@
"unicode": "e602",
"unicode_decimal": 58882
},
{
"icon_id": "5192988",
"name": "Star Star",
"font_class": "StarStar",
"unicode": "e635",
"unicode_decimal": 58933
},
{
"icon_id": "6642940",
"name": "bug",
@ -47,13 +54,6 @@
"unicode": "e607",
"unicode_decimal": 58887
},
{
"icon_id": "17895439",
"name": "Amazon S3上传",
"font_class": "s3",
"unicode": "e8e4",
"unicode_decimal": 59620
},
{
"icon_id": "20290513",
"name": "minio",

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 48 KiB

View File

@ -18,6 +18,7 @@ export default {
login: 'Login',
close: 'Close',
view: 'View',
handle: 'Handle',
expand: 'Expand',
log: 'Log',
saveAndEnable: 'Save and enable',
@ -244,6 +245,8 @@ export default {
commands: 'Command',
backups: 'Backup Account',
settings: 'Panel Setting',
cronjobs: 'Cronjob',
status: ' Update status',
auth: 'User',
login: ' login',
logout: ' logout',

View File

@ -18,6 +18,7 @@ export default {
login: '登录',
close: '关闭',
view: '详情',
handle: '执行',
expand: '展开',
log: '日志',
saveAndEnable: '保存并启用',
@ -241,6 +242,8 @@ export default {
commands: '快捷命令',
backups: '备份账号',
settings: '面板设置',
cronjobs: '计划任务',
status: '状态修改',
auth: '用户',
post: '创建',
put: '更新',

View File

@ -89,6 +89,10 @@ export function getRandomStr(e: number): string {
return n;
}
export function loadZero(i: number) {
return i < 10 ? '0' + i : '' + i;
}
export function computeSize(size: number): string {
const num = 1024.0;

View File

@ -73,11 +73,10 @@
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 { loadZero } from '@/utils/util';
import { onMounted, reactive, ref } from 'vue';
import { deleteCronjob, getCronjobPage, updateStatus } from '@/api/modules/cronjob';
import { deleteCronjob, getCronjobPage, handleOnce, updateStatus } from '@/api/modules/cronjob';
import { loadBackupName } from '@/views/setting/helper';
import { loadWeek } from './options';
import i18n from '@/lang';
import { Cronjob } from '@/api/interface/cronjob';
import { useDeleteData } from '@/hooks/use-delete-data';
@ -97,6 +96,15 @@ const logSearch = reactive({
page: 1,
pageSize: 5,
});
const weekOptions = [
{ label: i18n.global.t('cronjob.monday'), value: 1 },
{ label: i18n.global.t('cronjob.tuesday'), value: 2 },
{ label: i18n.global.t('cronjob.wednesday'), value: 3 },
{ label: i18n.global.t('cronjob.thursday'), value: 4 },
{ label: i18n.global.t('cronjob.friday'), value: 5 },
{ label: i18n.global.t('cronjob.saturday'), value: 6 },
{ label: i18n.global.t('cronjob.sunday'), value: 7 },
];
const search = async () => {
logSearch.page = paginationConfig.currentPage;
@ -158,8 +166,20 @@ const onChangeStatus = async (row: Cronjob.CronjobInfo) => {
search();
}
};
const onHandle = async (row: Cronjob.CronjobInfo) => {
await handleOnce(row.id);
ElMessage.success(i18n.global.t('commons.msg.operationSuccess'));
search();
};
const buttons = [
{
label: i18n.global.t('commons.button.handle'),
icon: 'Pointer',
click: (row: Cronjob.CronjobInfo) => {
onHandle(row);
},
},
{
label: i18n.global.t('commons.button.edit'),
icon: 'Edit',
@ -189,6 +209,14 @@ const onOpenRecordDialog = async (rowData: Partial<Cronjob.CronjobInfo> = {}) =>
dialogRecordRef.value!.acceptParams(params);
};
function loadWeek(i: number) {
for (const week of weekOptions) {
if (week.value === i) {
return week.label;
}
}
return '';
}
onMounted(() => {
search();
});

View File

@ -145,7 +145,6 @@
import { onMounted, reactive, ref } from 'vue';
import { Rules } from '@/global/form-rules';
import { loadBackupName } from '@/views/setting/helper';
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';
@ -222,6 +221,31 @@ const varifySpec = (rule: any, value: any, callback: any) => {
}
callback();
};
const typeOptions = [
{ label: i18n.global.t('cronjob.shell'), value: 'shell' },
{ label: i18n.global.t('cronjob.website'), value: 'website' },
{ label: i18n.global.t('cronjob.database'), value: 'database' },
{ label: i18n.global.t('cronjob.directory'), value: 'directory' },
{ label: i18n.global.t('cronjob.curl') + ' URL', value: 'curl' },
];
const specOptions = [
{ label: i18n.global.t('cronjob.perMonth'), value: 'perMonth' },
{ label: i18n.global.t('cronjob.perWeek'), value: 'perWeek' },
{ label: i18n.global.t('cronjob.perNDay'), value: 'perNDay' },
{ label: i18n.global.t('cronjob.perNHour'), value: 'perNHour' },
{ label: i18n.global.t('cronjob.perHour'), value: 'perHour' },
{ label: i18n.global.t('cronjob.perNMinute'), value: 'perNMinute' },
];
const weekOptions = [
{ label: i18n.global.t('cronjob.monday'), value: 1 },
{ label: i18n.global.t('cronjob.tuesday'), value: 2 },
{ label: i18n.global.t('cronjob.wednesday'), value: 3 },
{ label: i18n.global.t('cronjob.thursday'), value: 4 },
{ label: i18n.global.t('cronjob.friday'), value: 5 },
{ label: i18n.global.t('cronjob.saturday'), value: 6 },
{ label: i18n.global.t('cronjob.sunday'), value: 7 },
];
const rules = reactive({
name: [Rules.requiredInput, Rules.name],
type: [Rules.requiredSelect],
@ -265,8 +289,9 @@ function isBackup() {
dialogData.value.rowData!.type === 'directory'
);
}
function hasScript() {
return dialogData.value.rowData!.type === 'shell' || dialogData.value.rowData!.type === 'sync';
return dialogData.value.rowData!.type === 'shell';
}
function changeName(isChangeType: boolean, type: string) {
if (isChangeType) {

View File

@ -1,41 +0,0 @@
import i18n from '@/lang';
export const typeOptions = [
{ label: i18n.global.t('cronjob.shell'), value: 'shell' },
{ label: i18n.global.t('cronjob.website'), value: 'website' },
{ label: i18n.global.t('cronjob.database'), value: 'database' },
{ label: i18n.global.t('cronjob.directory'), value: 'directory' },
{ label: i18n.global.t('cronjob.syncDate'), value: 'sync' },
// { label: i18n.global.t('cronjob.releaseMemory'), value: 'release' },
{ label: i18n.global.t('cronjob.curl') + ' URL', value: 'curl' },
];
export const specOptions = [
{ label: i18n.global.t('cronjob.perMonth'), value: 'perMonth' },
{ label: i18n.global.t('cronjob.perWeek'), value: 'perWeek' },
{ label: i18n.global.t('cronjob.perNDay'), value: 'perNDay' },
{ label: i18n.global.t('cronjob.perNHour'), value: 'perNHour' },
{ label: i18n.global.t('cronjob.perHour'), value: 'perHour' },
{ label: i18n.global.t('cronjob.perNMinute'), value: 'perNMinute' },
];
export const weekOptions = [
{ label: i18n.global.t('cronjob.monday'), value: 1 },
{ label: i18n.global.t('cronjob.tuesday'), value: 2 },
{ label: i18n.global.t('cronjob.wednesday'), value: 3 },
{ label: i18n.global.t('cronjob.thursday'), value: 4 },
{ label: i18n.global.t('cronjob.friday'), value: 5 },
{ label: i18n.global.t('cronjob.saturday'), value: 6 },
{ label: i18n.global.t('cronjob.sunday'), value: 7 },
];
export const loadWeek = (i: number) => {
for (const week of weekOptions) {
if (week.value === i) {
return week.label;
}
}
return '';
};
export const loadZero = (i: number) => {
return i < 10 ? '0' + i : '' + i;
};

View File

@ -59,35 +59,35 @@
dialogData.rowData?.specType === 'perWeek'
"
>
{{ $t('cronjob.' + dialogData.rowData?.specType) }}
{{ $t('cronjob.' + dialogData.rowData?.specType) }}&nbsp;
</span>
<span v-else>{{ $t('cronjob.per') }}</span>
<span v-if="dialogData.rowData?.specType === 'perMonth'">
{{ dialogData.rowData?.day }}{{ $t('cronjob.day') }}
{{ dialogData.rowData?.day }}{{ $t('cronjob.day') }}&nbsp;
{{ loadZero(dialogData.rowData?.hour) }} :
{{ loadZero(dialogData.rowData?.minute) }}
</span>
<span v-if="dialogData.rowData?.specType === 'perWeek'">
{{ loadWeek(dialogData.rowData?.week) }}
{{ loadWeek(dialogData.rowData?.week) }}&nbsp;
{{ loadZero(dialogData.rowData?.hour) }} :
{{ loadZero(dialogData.rowData?.minute) }}
</span>
<span v-if="dialogData.rowData?.specType === 'perNDay'">
{{ dialogData.rowData?.day }}{{ $t('cronjob.day1') }},
{{ dialogData.rowData?.day }}{{ $t('cronjob.day1') }},&nbsp;
{{ loadZero(dialogData.rowData?.hour) }} :
{{ loadZero(dialogData.rowData?.minute) }}
</span>
<span v-if="dialogData.rowData?.specType === 'perNHour'">
{{ dialogData.rowData?.hour }}{{ $t('cronjob.hour') }},
{{ dialogData.rowData?.hour }}{{ $t('cronjob.hour') }},&nbsp;
{{ loadZero(dialogData.rowData?.minute) }}
</span>
<span v-if="dialogData.rowData?.specType === 'perHour'">
{{ loadZero(dialogData.rowData?.minute) }}
&nbsp;{{ loadZero(dialogData.rowData?.minute) }}
</span>
<span v-if="dialogData.rowData?.specType === 'perNMinute'">
{{ dialogData.rowData?.minute }}{{ $t('cronjob.minute') }}
&nbsp;{{ dialogData.rowData?.minute }}{{ $t('cronjob.minute') }}
</span>
{{ $t('cronjob.handle') }}
&nbsp;{{ $t('cronjob.handle') }}
</el-form-item>
</el-col>
<el-col :span="8" v-if="hasScript()">
@ -233,7 +233,7 @@
<script lang="ts" setup>
import { reactive, ref } from 'vue';
import { Cronjob } from '@/api/interface/cronjob';
import { loadZero, loadWeek } from '@/views/cronjob/options';
import { loadZero } from '@/utils/util';
import { loadBackupName } from '@/views/setting/helper';
import { searchRecords, getRecordDetail, download } from '@/api/modules/cronjob';
import { dateFromat, dateFromatForName } from '@/utils/util';
@ -314,6 +314,15 @@ const shortcuts = [
},
},
];
const weekOptions = [
{ label: i18n.global.t('cronjob.monday'), value: 1 },
{ label: i18n.global.t('cronjob.tuesday'), value: 2 },
{ label: i18n.global.t('cronjob.wednesday'), value: 3 },
{ label: i18n.global.t('cronjob.thursday'), value: 4 },
{ label: i18n.global.t('cronjob.friday'), value: 5 },
{ label: i18n.global.t('cronjob.saturday'), value: 6 },
{ label: i18n.global.t('cronjob.sunday'), value: 7 },
];
const timeRangeLoad = ref<Array<any>>([new Date(new Date().setHours(0, 0, 0, 0)), new Date()]);
const searchInfo = reactive({
page: 1,
@ -388,6 +397,14 @@ function isBackup() {
function hasScript() {
return dialogData.value.rowData!.type === 'shell' || dialogData.value.rowData!.type === 'sync';
}
function loadWeek(i: number) {
for (const week of weekOptions) {
if (week.value === i) {
return week.label;
}
}
return '';
}
defineExpose({
acceptParams,

View File

@ -25,7 +25,7 @@
<el-link @click="toTalk">
<span>{{ $t('setting.chat') }}</span>
</el-link>
<svg-icon style="font-size: 9px; margin-left: 15px" iconName="p-StarStar"></svg-icon>
<svg-icon style="font-size: 9px; margin-left: 15px" iconName="p-star"></svg-icon>
<el-link @click="toGithubStar">
<span>{{ $t('setting.star') }}</span>
</el-link>

View File

@ -14,9 +14,7 @@
<template #header>
<div class="card-header">
<svg-icon style="font-size: 7px" :iconName="loadIconName(item.type)"></svg-icon>
<span style="font-size: 16px; font-weight: 500">
{{ loadBackupName(item.type) }}
</span>
<span style="font-size: 16px; font-weight: 500">&nbsp;{{ loadBackupName(item.type) }}</span>
<div style="float: right">
<el-button @click="onEdit(item)">{{ $t('commons.button.edit') }}</el-button>
<el-button @click="onBatchDelete(item)">
@ -299,7 +297,7 @@ const loadIconName = (type: string) => {
return 'p-oss';
break;
case 'S3':
return 'p-s3';
return 'p-aws';
break;
case 'SFTP':
return 'p-SFTP';