mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-10-18 19:36:21 +08:00
parent
d67dfe2dfd
commit
0934534ac5
16 changed files with 89 additions and 62 deletions
|
@ -303,6 +303,27 @@ func (b *BaseApi) DownloadRecord(c *gin.Context) {
|
|||
helper.SuccessWithData(c, filePath)
|
||||
}
|
||||
|
||||
// @Tags Backup Account
|
||||
// @Summary Update backup record description
|
||||
// @Accept json
|
||||
// @Param request body dto.UpdateDescription true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /backup/record/description/update [post]
|
||||
func (b *BaseApi) UpdateRecordDescription(c *gin.Context) {
|
||||
var req dto.UpdateDescription
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err := backupRecordService.UpdateDescription(req); err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithOutData(c)
|
||||
}
|
||||
|
||||
// @Tags Backup Account
|
||||
// @Summary Delete backup record
|
||||
// @Accept json
|
||||
|
|
|
@ -60,6 +60,8 @@ type CommonBackup struct {
|
|||
Secret string `json:"secret"`
|
||||
TaskID string `json:"taskID"`
|
||||
FileName string `json:"fileName"`
|
||||
|
||||
Description string `json:"description"`
|
||||
}
|
||||
type CommonRecover struct {
|
||||
DownloadAccountID uint `json:"downloadAccountID" validate:"required"`
|
||||
|
@ -92,6 +94,7 @@ type BackupRecords struct {
|
|||
DownloadAccountID uint `json:"downloadAccountID"`
|
||||
FileDir string `json:"fileDir"`
|
||||
FileName string `json:"fileName"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
type DownloadRecord struct {
|
||||
|
|
|
@ -26,4 +26,6 @@ type BackupRecord struct {
|
|||
DetailName string `json:"detailName"`
|
||||
FileDir string `json:"fileDir"`
|
||||
FileName string `json:"fileName"`
|
||||
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ type IBackupRepo interface {
|
|||
CreateRecord(record *model.BackupRecord) error
|
||||
DeleteRecord(ctx context.Context, opts ...DBOption) error
|
||||
UpdateRecord(record *model.BackupRecord) error
|
||||
UpdateRecordByMap(id uint, upMap map[string]interface{}) error
|
||||
WithByDetailName(detailName string) DBOption
|
||||
WithByFileName(fileName string) DBOption
|
||||
WithByCronID(cronjobID uint) DBOption
|
||||
|
@ -144,6 +145,10 @@ func (u *BackupRepo) UpdateRecord(record *model.BackupRecord) error {
|
|||
return global.DB.Save(record).Error
|
||||
}
|
||||
|
||||
func (u *BackupRepo) UpdateRecordByMap(id uint, upMap map[string]interface{}) error {
|
||||
return global.DB.Model(&model.BackupRecord{}).Where("id = ?", id).Updates(upMap).Error
|
||||
}
|
||||
|
||||
func (u *BackupRepo) DeleteRecord(ctx context.Context, opts ...DBOption) error {
|
||||
return getTx(ctx, opts...).Delete(&model.BackupRecord{}).Error
|
||||
}
|
||||
|
|
|
@ -58,6 +58,7 @@ func (u *BackupService) AppBackup(req dto.CommonBackup) (*model.BackupRecord, er
|
|||
DownloadAccountID: 1,
|
||||
FileDir: itemDir,
|
||||
FileName: fileName,
|
||||
Description: req.Description,
|
||||
}
|
||||
if err := backupRepo.CreateRecord(record); err != nil {
|
||||
global.LOG.Errorf("save backup record failed, err: %v", err)
|
||||
|
|
|
@ -43,6 +43,7 @@ func (u *BackupService) MysqlBackup(req dto.CommonBackup) error {
|
|||
DownloadAccountID: 1,
|
||||
FileDir: itemDir,
|
||||
FileName: fileName,
|
||||
Description: req.Description,
|
||||
}
|
||||
if err := backupRepo.CreateRecord(record); err != nil {
|
||||
global.LOG.Errorf("save backup record failed, err: %v", err)
|
||||
|
|
|
@ -44,6 +44,7 @@ func (u *BackupService) PostgresqlBackup(req dto.CommonBackup) error {
|
|||
DownloadAccountID: 1,
|
||||
FileDir: itemDir,
|
||||
FileName: fileName,
|
||||
Description: req.Description,
|
||||
}
|
||||
if err := backupRepo.CreateRecord(record); err != nil {
|
||||
global.LOG.Errorf("save backup record failed, err: %v", err)
|
||||
|
|
|
@ -26,6 +26,7 @@ type IBackupRecordService interface {
|
|||
|
||||
ListFiles(req dto.OperateByID) []string
|
||||
LoadRecordSize(req dto.SearchForSize) ([]dto.RecordFileSize, error)
|
||||
UpdateDescription(req dto.UpdateDescription) error
|
||||
}
|
||||
|
||||
func NewIBackupRecordService() IBackupRecordService {
|
||||
|
@ -261,3 +262,7 @@ func (u *BackupRecordService) LoadRecordSize(req dto.SearchForSize) ([]dto.Recor
|
|||
wg.Wait()
|
||||
return datas, nil
|
||||
}
|
||||
|
||||
func (u *BackupRecordService) UpdateDescription(req dto.UpdateDescription) error {
|
||||
return backupRepo.UpdateRecordByMap(req.ID, map[string]interface{}{"description": req.Description})
|
||||
}
|
||||
|
|
|
@ -54,6 +54,7 @@ func (u *BackupService) RedisBackup(req dto.CommonBackup) error {
|
|||
DownloadAccountID: 1,
|
||||
FileDir: itemDir,
|
||||
FileName: fileName,
|
||||
Description: req.Description,
|
||||
}
|
||||
if err := backupRepo.CreateRecord(record); err != nil {
|
||||
global.LOG.Errorf("save backup record failed, err: %v", err)
|
||||
|
|
|
@ -51,6 +51,7 @@ func (u *BackupService) WebsiteBackup(req dto.CommonBackup) error {
|
|||
DownloadAccountID: 1,
|
||||
FileDir: itemDir,
|
||||
FileName: fileName,
|
||||
Description: req.Description,
|
||||
}
|
||||
if err = backupRepo.CreateRecord(record); err != nil {
|
||||
global.LOG.Errorf("save backup record failed, err: %v", err)
|
||||
|
|
|
@ -741,31 +741,3 @@ func scanFile(pathItem string, size *int64, count *int) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func loadRestorePath(upgradeDir string) (string, error) {
|
||||
if _, err := os.Stat(upgradeDir); err != nil && os.IsNotExist(err) {
|
||||
return "no such file", nil
|
||||
}
|
||||
files, err := os.ReadDir(upgradeDir)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
type itemState struct {
|
||||
Name string
|
||||
CreateAt time.Time
|
||||
}
|
||||
var folders []itemState
|
||||
for _, file := range files {
|
||||
if file.IsDir() {
|
||||
info, _ := file.Info()
|
||||
folders = append(folders, itemState{Name: file.Name(), CreateAt: info.ModTime()})
|
||||
}
|
||||
}
|
||||
if len(folders) == 0 {
|
||||
return "no such file", nil
|
||||
}
|
||||
sort.Slice(folders, func(i, j int) bool {
|
||||
return folders[i].CreateAt.After(folders[j].CreateAt)
|
||||
})
|
||||
return folders[0].Name, nil
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ import (
|
|||
)
|
||||
|
||||
var AddTable = &gormigrate.Migration{
|
||||
ID: "20250108-add-table",
|
||||
ID: "20250408-add-table",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
return tx.AutoMigrate(
|
||||
&model.AppDetail{},
|
||||
|
|
|
@ -32,5 +32,6 @@ func (s *BackupRouter) InitRouter(Router *gin.RouterGroup) {
|
|||
backupRouter.POST("/record/search/bycronjob", baseApi.SearchBackupRecordsByCronjob)
|
||||
backupRouter.POST("/record/download", baseApi.DownloadRecord)
|
||||
backupRouter.POST("/record/del", baseApi.DeleteBackupRecord)
|
||||
backupRouter.POST("/record/description/update", baseApi.UpdateRecordDescription)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,6 +32,9 @@ export const downloadBackupRecord = (params: Backup.RecordDownload) => {
|
|||
export const deleteBackupRecord = (params: { ids: number[] }) => {
|
||||
return http.post(`/backups/record/del`, params);
|
||||
};
|
||||
export const updateRecordDescription = (id: Number, description: String) => {
|
||||
return http.post(`/backups/record/description/update`, { id: id, description: description });
|
||||
};
|
||||
export const searchBackupRecords = (params: Backup.SearchBackupRecord) => {
|
||||
return http.post<ResPage<Backup.RecordInfo>>(`/backups/record/search`, params, TimeoutEnum.T_5M);
|
||||
};
|
||||
|
|
|
@ -4,27 +4,27 @@
|
|||
:header="$t('commons.button.backup')"
|
||||
:resource="detailName ? name + ' [' + detailName + ']' : name"
|
||||
@close="handleClose"
|
||||
size="large"
|
||||
size="60%"
|
||||
>
|
||||
<template #content>
|
||||
<div class="mb-5" v-if="type === 'app'">
|
||||
<el-alert :closable="false" type="warning">
|
||||
<div class="mt-2 text-xs">
|
||||
<span>{{ $t('setting.backupJump') }}</span>
|
||||
<span class="jump" @click="goFile()">
|
||||
<el-icon class="ml-2"><Position /></el-icon>
|
||||
{{ $t('firewall.quickJump') }}
|
||||
</span>
|
||||
</div>
|
||||
</el-alert>
|
||||
</div>
|
||||
<el-alert v-if="type === 'app'" :closable="false" type="warning">
|
||||
<div class="mt-2 text-xs">
|
||||
<span>{{ $t('setting.backupJump') }}</span>
|
||||
<span class="jump" @click="goFile()">
|
||||
<el-icon class="ml-2"><Position /></el-icon>
|
||||
{{ $t('firewall.quickJump') }}
|
||||
</span>
|
||||
</div>
|
||||
</el-alert>
|
||||
|
||||
<ComplexTable
|
||||
class="mt-5"
|
||||
v-loading="loading"
|
||||
:pagination-config="paginationConfig"
|
||||
v-model:selects="selects"
|
||||
@search="search"
|
||||
:data="data"
|
||||
style="width: 100%"
|
||||
>
|
||||
<template #toolbar>
|
||||
<el-button type="primary" :disabled="status && status != 'Running'" @click="onBackup()">
|
||||
|
@ -49,7 +49,7 @@
|
|||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('app.source')" prop="backupType">
|
||||
<el-table-column min-width="100px" :label="$t('app.source')" prop="backupType">
|
||||
<template #default="{ row }">
|
||||
<span v-if="row.accountType === 'LOCAL'">
|
||||
{{ $t('setting.LOCAL') }}
|
||||
|
@ -61,13 +61,24 @@
|
|||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
min-width="120px"
|
||||
:label="$t('commons.table.description')"
|
||||
prop="description"
|
||||
show-overflow-tooltip
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<fu-input-rw-switch v-model="row.description" @blur="onChange(row)" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
min-width="80px"
|
||||
prop="createdAt"
|
||||
:label="$t('commons.table.date')"
|
||||
:formatter="dateFormat"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
|
||||
<fu-table-operations width="230px" :buttons="buttons" :label="$t('commons.table.operate')" fix />
|
||||
<fu-table-operations width="200px" :buttons="buttons" :label="$t('commons.table.operate')" fix />
|
||||
</ComplexTable>
|
||||
</template>
|
||||
</DrawerPro>
|
||||
|
@ -78,14 +89,16 @@
|
|||
size="small"
|
||||
@close="handleBackupClose"
|
||||
>
|
||||
<el-form ref="backupForm" @submit.prevent label-position="left" v-loading="loading">
|
||||
<el-form-item
|
||||
:label="$t('setting.compressPassword')"
|
||||
class="mt-10"
|
||||
v-if="type === 'app' || type === 'website'"
|
||||
>
|
||||
<el-alert :closable="false">
|
||||
{{ $t('commons.msg.' + (isBackup ? 'backupHelper' : 'recoverHelper'), [name + '( ' + detailName + ' )']) }}
|
||||
</el-alert>
|
||||
<el-form class="mt-5" ref="backupForm" @submit.prevent label-position="top" v-loading="loading">
|
||||
<el-form-item :label="$t('setting.compressPassword')" v-if="type === 'app' || type === 'website'">
|
||||
<el-input v-model="secret" :placeholder="$t('setting.backupRecoverMessage')" />
|
||||
</el-form-item>
|
||||
<el-form-item v-if="isBackup" :label="$t('commons.table.description')">
|
||||
<el-input type="textarea" :autosize="{ minRows: 2, maxRows: 5 }" v-model="description" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
|
@ -114,6 +127,7 @@ import {
|
|||
downloadBackupRecord,
|
||||
searchBackupRecords,
|
||||
loadRecordSize,
|
||||
updateRecordDescription,
|
||||
} from '@/api/modules/backup';
|
||||
import i18n from '@/lang';
|
||||
import { Backup } from '@/api/interface/backup';
|
||||
|
@ -142,6 +156,7 @@ const detailName = ref();
|
|||
const backupPath = ref();
|
||||
const status = ref();
|
||||
const secret = ref();
|
||||
const description = ref();
|
||||
|
||||
const open = ref();
|
||||
const isBackup = ref();
|
||||
|
@ -181,6 +196,11 @@ const goFile = async () => {
|
|||
router.push({ name: 'File', query: { path: `${backupPath.value}/app/${name.value}/${detailName.value}` } });
|
||||
};
|
||||
|
||||
const onChange = async (info: any) => {
|
||||
await updateRecordDescription(info.id, info.description);
|
||||
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
|
||||
};
|
||||
|
||||
const search = async () => {
|
||||
let params = {
|
||||
page: paginationConfig.currentPage,
|
||||
|
@ -236,6 +256,7 @@ const backup = async (close: boolean) => {
|
|||
detailName: detailName.value,
|
||||
secret: secret.value,
|
||||
taskID: taskID,
|
||||
description: description.value,
|
||||
};
|
||||
loading.value = true;
|
||||
try {
|
||||
|
@ -285,20 +306,8 @@ const recover = async (close: boolean, row?: any) => {
|
|||
};
|
||||
|
||||
const onBackup = async () => {
|
||||
description.value = '';
|
||||
isBackup.value = true;
|
||||
if (type.value !== 'app' && type.value !== 'website') {
|
||||
ElMessageBox.confirm(
|
||||
i18n.global.t('commons.msg.backupHelper', [name.value + '( ' + detailName.value + ' )']),
|
||||
i18n.global.t('commons.button.backup'),
|
||||
{
|
||||
confirmButtonText: i18n.global.t('commons.button.confirm'),
|
||||
cancelButtonText: i18n.global.t('commons.button.cancel'),
|
||||
},
|
||||
).then(async () => {
|
||||
backup(true);
|
||||
});
|
||||
return;
|
||||
}
|
||||
open.value = true;
|
||||
};
|
||||
|
||||
|
|
|
@ -103,6 +103,7 @@ onMounted(() => {
|
|||
let itemSize = Number(localStorage.getItem(props.paginationConfig.cacheSizeKey));
|
||||
if (itemSize) {
|
||||
props.paginationConfig.pageSize = itemSize;
|
||||
sizeChange();
|
||||
}
|
||||
}
|
||||
let heightDiff = 320;
|
||||
|
|
Loading…
Add table
Reference in a new issue