diff --git a/.gitignore b/.gitignore index 72ed4c849..85715c0bd 100644 --- a/.gitignore +++ b/.gitignore @@ -16,7 +16,6 @@ build/1panel # Dependency directories (remove the comment below to include it) # vendor/ -/build /pkg/ backend/__debug_bin cmd/server/__debug_bin diff --git a/backend/app/repo/cronjob.go b/backend/app/repo/cronjob.go index a668fefe7..daa53f167 100644 --- a/backend/app/repo/cronjob.go +++ b/backend/app/repo/cronjob.go @@ -86,7 +86,7 @@ func (u *CronjobRepo) Page(page, size int, opts ...DBOption) (int64, []model.Cro func (u *CronjobRepo) RecordFirst(id uint) (model.JobRecords, error) { var record model.JobRecords - err := global.DB.Order("created_at desc").First(&record).Error + err := global.DB.Where("cronjob_id = ?", id).Order("created_at desc").First(&record).Error return record, err } diff --git a/backend/app/service/backup.go b/backend/app/service/backup.go index 68e145207..2127c2d00 100644 --- a/backend/app/service/backup.go +++ b/backend/app/service/backup.go @@ -5,7 +5,6 @@ import ( "encoding/json" "fmt" "os" - "os/exec" "strings" "github.com/1Panel-dev/1Panel/backend/app/dto" @@ -13,6 +12,7 @@ import ( "github.com/1Panel-dev/1Panel/backend/constant" "github.com/1Panel-dev/1Panel/backend/global" "github.com/1Panel-dev/1Panel/backend/utils/cloud_storage" + "github.com/1Panel-dev/1Panel/backend/utils/cmd" "github.com/jinzhu/copier" "github.com/pkg/errors" ) @@ -232,6 +232,9 @@ func (u *BackupService) Update(req dto.BackupOperate) error { if backup.Type == "LOCAL" { if dir, ok := varMap["dir"]; ok { if dirStr, isStr := dir.(string); isStr { + if strings.HasSuffix(dirStr, "/") { + dirStr = dirStr[:strings.LastIndex(dirStr, "/")] + } if err := updateBackupDir(dirStr); err != nil { upMap["vars"] = backup.Vars _ = backupRepo.Update(req.ID, upMap) @@ -316,8 +319,7 @@ func updateBackupDir(dir string) error { if strings.HasSuffix(oldDir, "/") { oldDir = oldDir[:strings.LastIndex(oldDir, "/")] } - cmd := exec.Command("cp", "-r", oldDir+"/*", dir) - stdout, err := cmd.CombinedOutput() + stdout, err := cmd.Execf("cp -r %s/* %s", oldDir, dir) if err != nil { return errors.New(string(stdout)) } diff --git a/backend/app/service/cornjob.go b/backend/app/service/cornjob.go index 578f6cf97..63156a600 100644 --- a/backend/app/service/cornjob.go +++ b/backend/app/service/cornjob.go @@ -151,7 +151,7 @@ func (u *CronjobService) Download(down dto.CronjobDownload) (string, error) { } return fmt.Sprintf("%v/database/mysql/%s/%s/db_%s_%s.sql.gz", varMap["dir"], mysqlInfo.Name, cronjob.DBName, cronjob.DBName, record.StartTime.Format("20060102150405")), nil case "directory": - return fmt.Sprintf("%v/%s/%s/%s.tar.gz", varMap["dir"], cronjob.Type, cronjob.Name, record.StartTime.Format("20060102150405")), nil + return fmt.Sprintf("%v/%s/%s/directory%s_%s.tar.gz", varMap["dir"], cronjob.Type, cronjob.Name, strings.ReplaceAll(cronjob.SourceDir, "/", "_"), record.StartTime.Format("20060102150405")), nil default: return "", fmt.Errorf("not support type %s", cronjob.Type) } @@ -220,6 +220,11 @@ func (u *CronjobService) Update(id uint, req dto.CronjobUpdate) error { if err := copier.Copy(&cronjob, &req); err != nil { return errors.WithMessage(constant.ErrStructTransform, err.Error()) } + cronModel, err := cronjobRepo.Get(commonRepo.WithByID(id)) + if err != nil { + return constant.ErrRecordNotFound + } + cronjob.EntryID = cronModel.EntryID cronjob.Spec = loadSpec(cronjob) if err := u.StartJob(&cronjob); err != nil { return err diff --git a/backend/app/service/cronjob_helper.go b/backend/app/service/cronjob_helper.go index 40b0f8e70..d7cdf46c8 100644 --- a/backend/app/service/cronjob_helper.go +++ b/backend/app/service/cronjob_helper.go @@ -114,7 +114,7 @@ func (u *CronjobService) HandleBackup(cronjob *model.Cronjob, startTime time.Tim fileName = fmt.Sprintf("directory%s_%s.tar.gz", strings.ReplaceAll(cronjob.SourceDir, "/", "_"), startTime.Format("20060102150405")) backupDir = fmt.Sprintf("%s/%s/%s", localDir, cronjob.Type, cronjob.Name) global.LOG.Infof("handle tar %s to %s", backupDir, fileName) - if err := handleTar(cronjob.SourceDir, localDir+"/"+backupDir, fileName, cronjob.ExclusionRules); err != nil { + if err := handleTar(cronjob.SourceDir, backupDir, fileName, cronjob.ExclusionRules); err != nil { return "", err } } @@ -197,21 +197,26 @@ func (u *CronjobService) HandleRmExpired(backType, backupDir string, cronjob *mo if len(files) == 0 { return } - if cronjob.Type == "database" { - dbCopies := uint64(0) - for i := len(files) - 1; i >= 0; i-- { - if strings.HasPrefix(files[i].Name(), "db_") { - dbCopies++ - if dbCopies > cronjob.RetainCopies { - _ = os.Remove(backupDir + "/" + files[i].Name()) - _ = backupRepo.DeleteRecord(context.Background(), backupRepo.WithByFileName(files[i].Name())) - } + + prefix := "" + switch cronjob.Type { + case "database": + prefix = "db_" + case "website": + prefix = "website_" + case "directory": + prefix = "directory_" + } + + dbCopies := uint64(0) + for i := len(files) - 1; i >= 0; i-- { + if strings.HasPrefix(files[i].Name(), prefix) { + dbCopies++ + if dbCopies > cronjob.RetainCopies { + _ = os.Remove(backupDir + "/" + files[i].Name()) + _ = backupRepo.DeleteRecord(context.Background(), backupRepo.WithByFileName(files[i].Name())) } } - } else { - for i := 0; i < len(files)-int(cronjob.RetainCopies); i++ { - _ = os.Remove(backupDir + "/" + files[i].Name()) - } } records, _ := cronjobRepo.ListRecord(cronjobRepo.WithByJobID(int(cronjob.ID))) if len(records) > int(cronjob.RetainCopies) { diff --git a/frontend/src/global/form-rules.ts b/frontend/src/global/form-rules.ts index 8aed48753..c8be16ae9 100644 --- a/frontend/src/global/form-rules.ts +++ b/frontend/src/global/form-rules.ts @@ -84,7 +84,7 @@ const checkImageName = (rule: any, value: any, callback: any) => { if (value === '' || typeof value === 'undefined' || value == null) { callback(new Error(i18n.global.t('commons.rule.imageName'))); } else { - const reg = /^[a-zA-Z0-9\u4e00-\u9fa5]{1}[a-z:A-Z0-9_.\u4e00-\u9fa5-]{0,30}$/; + const reg = /^[a-zA-Z0-9]{1}[a-z:A-Z0-9_/.-]{0,150}$/; if (!reg.test(value) && value !== '') { callback(new Error(i18n.global.t('commons.rule.imageName'))); } else { diff --git a/frontend/src/lang/modules/en.ts b/frontend/src/lang/modules/en.ts index 07b907a4c..262ebe515 100644 --- a/frontend/src/lang/modules/en.ts +++ b/frontend/src/lang/modules/en.ts @@ -129,7 +129,7 @@ export default { userName: 'Support English, Chinese, numbers and _ length 3-30', simpleName: 'Support English, numbers and _ length 1-30', dbName: 'Support English, Chinese, numbers, .-, and _ length 1-16', - imageName: 'Support English, Chinese, numbers, :.-_, length 1-30', + imageName: 'Support English, numbers, :/.-_, length 1-150', volumeName: 'Support English, numbers, .-_, length 1-30', complexityPassword: 'Enter a password that is longer than eight characters and contains at least two letters, digits, and special characters', diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts index f0d50d4f0..70177ee23 100644 --- a/frontend/src/lang/modules/zh.ts +++ b/frontend/src/lang/modules/zh.ts @@ -133,7 +133,7 @@ export default { userName: '支持英文、中文、数字和_,长度3-30', simpleName: '支持英文、数字、_,长度1-30', dbName: '支持英文、中文、数字、.-_,长度1-16', - imageName: '支持英文、中文、数字、:.-_,长度1-30', + imageName: '支持英文、数字、:/.-_,长度1-150', volumeName: '支持英文、数字、.-和_,长度1-30', complexityPassword: '请输入长度大于 8 位且包含字母、数字、特殊字符至少两项的密码组合', commonPassword: '请输入 6 位以上长度密码', diff --git a/frontend/src/styles/common.scss b/frontend/src/styles/common.scss index 4a03fd75d..2c5960566 100644 --- a/frontend/src/styles/common.scss +++ b/frontend/src/styles/common.scss @@ -279,3 +279,10 @@ .middle-center { vertical-align: middle; } +.status-count { + font-size: 24px; +} +.status-label { + font-size: 14px; + color: #646a73; +} diff --git a/frontend/src/views/container/image/pull/index.vue b/frontend/src/views/container/image/pull/index.vue index fa3db503c..533a8b47a 100644 --- a/frontend/src/views/container/image/pull/index.vue +++ b/frontend/src/views/container/image/pull/index.vue @@ -25,7 +25,7 @@ - + diff --git a/frontend/src/views/container/image/push/index.vue b/frontend/src/views/container/image/push/index.vue index 552ec2ade..365695154 100644 --- a/frontend/src/views/container/image/push/index.vue +++ b/frontend/src/views/container/image/push/index.vue @@ -27,7 +27,7 @@ /> - + diff --git a/frontend/src/views/cronjob/index.vue b/frontend/src/views/cronjob/index.vue index 0b94bc46c..8a59ba9fe 100644 --- a/frontend/src/views/cronjob/index.vue +++ b/frontend/src/views/cronjob/index.vue @@ -45,7 +45,14 @@ diff --git a/frontend/src/views/cronjob/operate/index.vue b/frontend/src/views/cronjob/operate/index.vue index f020a85ff..a5a063841 100644 --- a/frontend/src/views/cronjob/operate/index.vue +++ b/frontend/src/views/cronjob/operate/index.vue @@ -7,13 +7,19 @@ - + + {{ dialogData.rowData!.type }} @@ -120,6 +126,8 @@ @@ -184,7 +192,9 @@ const dialogData = ref({ }); const acceptParams = (params: DialogProps): void => { dialogData.value = params; - changeType(); + if (dialogData.value.title === 'create') { + changeType(); + } title.value = i18n.global.t('commons.button.' + dialogData.value.title); drawerVisiable.value = true; checkMysqlInstalled(); diff --git a/frontend/src/views/cronjob/record/index.vue b/frontend/src/views/cronjob/record/index.vue index 5c182c360..d77fe111e 100644 --- a/frontend/src/views/cronjob/record/index.vue +++ b/frontend/src/views/cronjob/record/index.vue @@ -126,153 +126,136 @@ - - - - {{ $t('cronjob.shellContent') }} - - - - - - {{ dialogData.rowData!.website }} - - - - - {{ dialogData.rowData!.dbName }} - - - - - - {{ dialogData.rowData!.sourceDir }} - -
- - - -
-
-
- - - {{ dialogData.rowData!.targetDir }} - - {{ $t('file.download') }} - - - - - - {{ dialogData.rowData!.retainCopies }} - - - - - {{ dialogData.rowData!.url }} - - - + + + + {{ dialogData.rowData!.targetDir }} + - -
-
- {{ item }} -
-
- - -
-
-
- - - - {{ dateFormat(0, 0, currentRecord?.startTime) }} - - - - - - {{ currentRecord?.interval }} ms - - - {{ currentRecord?.interval! / 1000 }} s - - - - - - - {{ $t('commons.table.statusFailed') }} - - - {{ $t('commons.table.statusSuccess') }} - - - {{ $t('commons.table.statusWaiting') }} - - - - - - {{ $t('commons.table.records') }} - - -
-
+ {{ $t('file.download') }} + +
+ + + {{ dialogData.rowData!.website }} + + + + {{ dialogData.rowData!.dbName }} + + + + + {{ dialogData.rowData!.sourceDir }} + +
+ + + +
+
+ + + {{ dialogData.rowData!.retainCopies }} + + + + +
+
+ {{ item }} +
+
+ - +
+ + + + {{ dateFormat(0, 0, currentRecord?.startTime) }} + + + + + {{ currentRecord?.interval }} ms + + + {{ currentRecord?.interval! / 1000 }} s + + + + + + {{ $t('commons.table.statusFailed') }} + + + {{ $t('commons.table.statusSuccess') }} + + + {{ $t('commons.table.statusWaiting') }} + + + + + {{ $t('commons.table.records') }} + + +
@@ -416,6 +399,8 @@ const onHandle = async (row: Cronjob.CronjobInfo) => { .then(() => { loading.value = false; MsgSuccess(i18n.global.t('commons.msg.operationSuccess')); + searchInfo.page = 1; + records.value = []; search(); }) .catch(() => { @@ -451,13 +436,14 @@ const search = async () => { endTime: searchInfo.endTime, status: searchInfo.status, }; - records.value = []; const res = await searchRecords(params); - if (!res.data.items) { + if (searchInfo.page === 1 && !res.data.items) { hasRecords.value = false; return; } - records.value = res.data.items; + for (const item of res.data.items) { + records.value.push(item); + } hasRecords.value = true; currentRecord.value = records.value[0]; currentRecordIndex.value = 0; @@ -490,7 +476,7 @@ const nextPage = async () => { if (searchInfo.pageSize >= searchInfo.recordTotal) { return; } - searchInfo.pageSize = searchInfo.pageSize + 5; + searchInfo.page = searchInfo.page + 1; search(); }; const forDetail = async (row: Cronjob.Record, index: number) => { @@ -515,9 +501,6 @@ function isBackup() { dialogData.value.rowData!.type === 'directory' ); } -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) { @@ -534,7 +517,7 @@ defineExpose({ diff --git a/frontend/src/views/database/mysql/setting/status/index.vue b/frontend/src/views/database/mysql/setting/status/index.vue index 867f83a5a..28e7d36c6 100644 --- a/frontend/src/views/database/mysql/setting/status/index.vue +++ b/frontend/src/views/database/mysql/setting/status/index.vue @@ -213,13 +213,6 @@ defineExpose({