1
0
mirror of https://github.com/1Panel-dev/1Panel.git synced 2025-01-19 00:09:16 +08:00

feat: 计划任务支持备份所有 (#526)

This commit is contained in:
ssongliu 2023-04-07 10:18:10 +08:00 committed by GitHub
parent a0e4c266a1
commit 0eb25d8413
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 203 additions and 65 deletions

View File

@ -9,6 +9,7 @@ import (
"time"
"github.com/1Panel-dev/1Panel/backend/app/model"
"github.com/1Panel-dev/1Panel/backend/app/repo"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/utils/cloud_storage"
@ -68,11 +69,6 @@ func (u *CronjobService) HandleJob(cronjob *model.Cronjob) {
}
func (u *CronjobService) HandleBackup(cronjob *model.Cronjob, startTime time.Time) (string, error) {
var (
backupDir string
fileName string
record model.BackupRecord
)
backup, err := backupRepo.Get(commonRepo.WithByID(uint(cronjob.TargetDirID)))
if err != nil {
return "", err
@ -89,77 +85,57 @@ func (u *CronjobService) HandleBackup(cronjob *model.Cronjob, startTime time.Tim
if err != nil {
return "", err
}
fileName = fmt.Sprintf("db_%s_%s.sql.gz", cronjob.DBName, startTime.Format("20060102150405"))
backupDir = fmt.Sprintf("%s/database/mysql/%s/%s", localDir, app.Name, cronjob.DBName)
if err = handleMysqlBackup(app, backupDir, cronjob.DBName, fileName); err != nil {
if err := u.handleDatabase(*cronjob, app, backup, startTime); err != nil {
return "", err
}
record.Type = "mysql"
record.Name = app.Name
record.DetailName = cronjob.DBName
if cronjob.DBName != "all" {
if !cronjob.KeepLocal && backup.Type != "LOCAL" {
return fmt.Sprintf("database/mysql/%s/%s/db_%s_%s.sql.gz", app.Name, cronjob.DBName, cronjob.DBName, startTime.Format("20060102150405")), nil
}
return fmt.Sprintf("%s/database/mysql/%s/%s/db_%s_%s.sql.gz", localDir, app.Name, cronjob.DBName, cronjob.DBName, startTime.Format("20060102150405")), nil
}
return "", nil
case "website":
fileName = fmt.Sprintf("website_%s_%s.tar.gz", cronjob.Website, startTime.Format("20060102150405"))
backupDir = fmt.Sprintf("%s/website/%s", localDir, cronjob.Website)
website, err := websiteRepo.GetFirst(websiteRepo.WithDomain(cronjob.Website))
if err != nil {
if err := u.handleWebsite(*cronjob, backup, startTime); err != nil {
return "", err
}
if err := handleWebsiteBackup(&website, backupDir, fileName); err != nil {
return "", err
if cronjob.DBName != "all" {
if !cronjob.KeepLocal && backup.Type != "LOCAL" {
return fmt.Sprintf("website/%s/website_%s_%s.tar.gz", cronjob.Website, cronjob.Website, startTime.Format("20060102150405")), nil
}
return fmt.Sprintf("%s/website/%s/website_%s_%s.tar.gz", localDir, cronjob.Website, cronjob.Website, startTime.Format("20060102150405")), nil
}
record.Type = "website"
record.Name = website.PrimaryDomain
return "", nil
default:
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)
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)
itemFileDir := fmt.Sprintf("%s/%s", cronjob.Type, cronjob.Name)
global.LOG.Infof("handle tar %s to %s", backupDir, fileName)
if err := handleTar(cronjob.SourceDir, backupDir, fileName, cronjob.ExclusionRules); err != nil {
return "", err
}
}
itemFileDir := strings.ReplaceAll(backupDir, localDir+"/", "")
if len(record.Name) != 0 {
record.FileName = fileName
record.FileDir = backupDir
record.Source = "LOCAL"
record.BackupType = backup.Type
if !cronjob.KeepLocal && backup.Type != "LOCAL" {
record.Source = backup.Type
record.FileDir = itemFileDir
if backup.Type == "LOCAL" {
u.HandleRmExpired(backup.Type, backupDir, cronjob, nil)
return fmt.Sprintf("%s/%s", backupDir, fileName), nil
}
if err := backupRepo.CreateRecord(&record); err != nil {
global.LOG.Errorf("save backup record failed, err: %v", err)
if !cronjob.KeepLocal {
defer func() {
_ = os.RemoveAll(fmt.Sprintf("%s/%s", backupDir, fileName))
}()
}
client, err := NewIBackupService().NewClient(&backup)
if err != nil {
return "", err
}
if _, err = client.Upload(backupDir+"/"+fileName, itemFileDir+"/"+fileName); err != nil {
return "", err
}
u.HandleRmExpired(backup.Type, itemFileDir, cronjob, client)
if cronjob.KeepLocal {
u.HandleRmExpired("LOCAL", backupDir, cronjob, client)
}
return fmt.Sprintf("%s/%s/%s", cronjob.Type, cronjob.Name, fileName), nil
}
fullPath := fmt.Sprintf("%s/%s", backupDir, fileName)
if backup.Type != "LOCAL" {
fullPath = fmt.Sprintf("%s/%s", itemFileDir, fileName)
}
if backup.Type == "LOCAL" {
u.HandleRmExpired(backup.Type, backupDir, cronjob, nil)
return fullPath, nil
}
if !cronjob.KeepLocal {
defer func() {
_ = os.RemoveAll(fmt.Sprintf("%s/%s", backupDir, fileName))
}()
}
client, err := NewIBackupService().NewClient(&backup)
if err != nil {
return fullPath, err
}
if _, err = client.Upload(backupDir+"/"+fileName, itemFileDir+"/"+fileName); err != nil {
return fullPath, err
}
u.HandleRmExpired(backup.Type, itemFileDir, cronjob, client)
if cronjob.KeepLocal {
u.HandleRmExpired("LOCAL", backupDir, cronjob, client)
}
return fullPath, nil
}
func (u *CronjobService) HandleDelete(id uint) error {
@ -281,3 +257,137 @@ func handleUnTar(sourceFile, targetDir string) error {
}
return nil
}
func (u *CronjobService) handleDatabase(cronjob model.Cronjob, app *repo.RootInfo, backup model.BackupAccount, startTime time.Time) error {
localDir, err := loadLocalDir()
if err != nil {
return err
}
var record model.BackupRecord
record.Type = "mysql"
record.Name = app.Name
record.Source = "LOCAL"
record.BackupType = backup.Type
var dblist []string
if cronjob.DBName == "all" {
mysqlService := NewIMysqlService()
dblist, err = mysqlService.ListDBName()
if err != nil {
return err
}
} else {
dblist = append(dblist, cronjob.DBName)
}
for _, dbName := range dblist {
backupDir := fmt.Sprintf("%s/database/mysql/%s/%s", localDir, app.Name, dbName)
record.FileName = fmt.Sprintf("db_%s_%s.sql.gz", dbName, startTime.Format("20060102150405"))
if err = handleMysqlBackup(app, backupDir, dbName, record.FileName); err != nil {
return err
}
record.DetailName = dbName
record.FileDir = backupDir
itemFileDir := strings.ReplaceAll(backupDir, localDir+"/", "")
if !cronjob.KeepLocal && backup.Type != "LOCAL" {
record.Source = backup.Type
record.FileDir = itemFileDir
}
if err := saveBackupRecord(record); err != nil {
return err
}
if backup.Type == "LOCAL" {
u.HandleRmExpired(backup.Type, backupDir, &cronjob, nil)
return nil
}
if !cronjob.KeepLocal {
defer func() {
_ = os.RemoveAll(fmt.Sprintf("%s/%s", backupDir, record.FileName))
}()
}
client, err := NewIBackupService().NewClient(&backup)
if err != nil {
return err
}
if _, err = client.Upload(backupDir+"/"+record.FileName, itemFileDir+"/"+record.FileName); err != nil {
return err
}
u.HandleRmExpired(backup.Type, itemFileDir, &cronjob, client)
if cronjob.KeepLocal {
u.HandleRmExpired("LOCAL", backupDir, &cronjob, client)
}
}
return nil
}
func (u *CronjobService) handleWebsite(cronjob model.Cronjob, backup model.BackupAccount, startTime time.Time) error {
localDir, err := loadLocalDir()
if err != nil {
return err
}
var record model.BackupRecord
record.Type = "website"
record.Name = cronjob.Website
record.Source = "LOCAL"
record.BackupType = backup.Type
var weblist []string
if cronjob.Website == "all" {
weblist, err = NewIWebsiteService().GetWebsiteOptions()
if err != nil {
return err
}
} else {
weblist = append(weblist, cronjob.Website)
}
for _, websiteItem := range weblist {
website, err := websiteRepo.GetFirst(websiteRepo.WithDomain(websiteItem))
if err != nil {
return err
}
backupDir := fmt.Sprintf("%s/website/%s", localDir, website.PrimaryDomain)
record.FileDir = backupDir
itemFileDir := strings.ReplaceAll(backupDir, localDir+"/", "")
if !cronjob.KeepLocal && backup.Type != "LOCAL" {
record.Source = backup.Type
record.FileDir = strings.ReplaceAll(backupDir, localDir+"/", "")
}
record.FileName = fmt.Sprintf("website_%s_%s.tar.gz", website.PrimaryDomain, startTime.Format("20060102150405"))
if err := handleWebsiteBackup(&website, backupDir, record.FileName); err != nil {
return err
}
record.Name = website.PrimaryDomain
if err := saveBackupRecord(record); err != nil {
return err
}
if backup.Type == "LOCAL" {
u.HandleRmExpired(backup.Type, backupDir, &cronjob, nil)
return nil
}
if !cronjob.KeepLocal {
defer func() {
_ = os.RemoveAll(fmt.Sprintf("%s/%s", backupDir, record.FileName))
}()
}
client, err := NewIBackupService().NewClient(&backup)
if err != nil {
return err
}
if _, err = client.Upload(backupDir+"/"+record.FileName, itemFileDir+"/"+record.FileName); err != nil {
return err
}
u.HandleRmExpired(backup.Type, itemFileDir, &cronjob, client)
if cronjob.KeepLocal {
u.HandleRmExpired("LOCAL", backupDir, &cronjob, client)
}
}
return nil
}
func saveBackupRecord(record model.BackupRecord) error {
if err := backupRepo.CreateRecord(&record); err != nil {
global.LOG.Errorf("save backup record failed, err: %v", err)
return err
}
return nil
}

View File

@ -586,6 +586,8 @@ const message = {
cronSpecHelper: 'Enter the correct execution period',
directory: 'Backup directory',
sourceDir: 'Backup directory',
allOptionHelper:
'The current task plan is to back up all {0}. Direct download is not supported at the moment. You can check the backup list of {0} menu.',
exclusionRules: 'Exclusive rule',
saveLocal: 'Retain local backups (the same as the number of cloud storage copies)',
url: 'URL Address',

View File

@ -590,6 +590,7 @@ const message = {
cronSpecHelper: '请输入正确的执行周期',
directory: '备份目录',
sourceDir: '备份目录',
allOptionHelper: '当前计划任务为备份所有 {0}暂不支持直接下载可在 {0} 备份列表中查看',
exclusionRules: '排除规则',
saveLocal: '同时保留本地备份和云存储保留份数一致',
url: 'URL 地址',

View File

@ -23,7 +23,12 @@
</el-form-item>
<el-form-item :label="$t('cronjob.taskName')" prop="name">
<el-input style="width: 100%" clearable v-model.trim="dialogData.rowData!.name" />
<el-input
:disabled="dialogData.title === 'edit'"
style="width: 100%"
clearable
v-model.trim="dialogData.rowData!.name"
/>
</el-form-item>
<el-form-item :label="$t('cronjob.cronSpec')" prop="spec">
@ -82,6 +87,7 @@
prop="website"
>
<el-select style="width: 100%" v-model="dialogData.rowData!.website">
<el-option :label="$t('commons.table.all')" value="all" />
<el-option v-for="item in websiteOptions" :key="item" :value="item" :label="item" />
</el-select>
</el-form-item>
@ -89,6 +95,7 @@
<div v-if="dialogData.rowData!.type === 'database'">
<el-form-item :label="$t('cronjob.database')" prop="dbName">
<el-select style="width: 100%" clearable v-model="dialogData.rowData!.dbName">
<el-option :label="$t('commons.table.all')" value="all" />
<el-option v-for="item in mysqlInfo.dbNames" :key="item" :label="item" :value="item" />
</el-select>
</el-form-item>

View File

@ -152,13 +152,23 @@
<template #label>
<span class="status-label">{{ $t('cronjob.website') }}</span>
</template>
<span class="status-count">{{ dialogData.rowData!.website }}</span>
<span v-if="dialogData.rowData!.website !== 'all'" class="status-count">
{{ dialogData.rowData!.website }}
</span>
<span v-else class="status-count">
{{ $t('commons.table.all') }}
</span>
</el-form-item>
<el-form-item class="description" v-if="dialogData.rowData!.type === 'database'">
<template #label>
<span class="status-label">{{ $t('cronjob.database') }}</span>
</template>
<span class="status-count">{{ dialogData.rowData!.dbName }}</span>
<span v-if="dialogData.rowData!.website !== 'all'" class="status-count">
{{ dialogData.rowData!.dbName }}
</span>
<span v-else class="status-count">
{{ $t('commons.table.all') }}
</span>
</el-form-item>
<el-form-item class="description" v-if="dialogData.rowData!.type === 'directory'">
<template #label>
@ -288,7 +298,7 @@ import LayoutContent from '@/layout/layout-content.vue';
import { Codemirror } from 'vue-codemirror';
import { javascript } from '@codemirror/lang-javascript';
import { oneDark } from '@codemirror/theme-one-dark';
import { MsgError, MsgSuccess } from '@/utils/message';
import { MsgError, MsgInfo, MsgSuccess } from '@/utils/message';
const loading = ref();
const hasRecords = ref();
@ -456,6 +466,14 @@ const onRefresh = () => {
};
const onDownload = async (record: any, backupID: number) => {
if (dialogData.value.rowData.dbName === 'all') {
MsgInfo(i18n.global.t('cronjob.allOptionHelper', [i18n.global.t('database.database')]));
return;
}
if (dialogData.value.rowData.website === 'all') {
MsgInfo(i18n.global.t('cronjob.allOptionHelper', [i18n.global.t('website.website')]));
return;
}
if (!record.file || record.file.indexOf('/') === -1) {
MsgError(i18n.global.t('cronjob.errPath', [record.file]));
return;