mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-12-17 12:58:51 +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)
|
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
|
// @Tags Backup Account
|
||||||
// @Summary Delete backup record
|
// @Summary Delete backup record
|
||||||
// @Accept json
|
// @Accept json
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,8 @@ type CommonBackup struct {
|
||||||
Secret string `json:"secret"`
|
Secret string `json:"secret"`
|
||||||
TaskID string `json:"taskID"`
|
TaskID string `json:"taskID"`
|
||||||
FileName string `json:"fileName"`
|
FileName string `json:"fileName"`
|
||||||
|
|
||||||
|
Description string `json:"description"`
|
||||||
}
|
}
|
||||||
type CommonRecover struct {
|
type CommonRecover struct {
|
||||||
DownloadAccountID uint `json:"downloadAccountID" validate:"required"`
|
DownloadAccountID uint `json:"downloadAccountID" validate:"required"`
|
||||||
|
|
@ -92,6 +94,7 @@ type BackupRecords struct {
|
||||||
DownloadAccountID uint `json:"downloadAccountID"`
|
DownloadAccountID uint `json:"downloadAccountID"`
|
||||||
FileDir string `json:"fileDir"`
|
FileDir string `json:"fileDir"`
|
||||||
FileName string `json:"fileName"`
|
FileName string `json:"fileName"`
|
||||||
|
Description string `json:"description"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type DownloadRecord struct {
|
type DownloadRecord struct {
|
||||||
|
|
|
||||||
|
|
@ -26,4 +26,6 @@ type BackupRecord struct {
|
||||||
DetailName string `json:"detailName"`
|
DetailName string `json:"detailName"`
|
||||||
FileDir string `json:"fileDir"`
|
FileDir string `json:"fileDir"`
|
||||||
FileName string `json:"fileName"`
|
FileName string `json:"fileName"`
|
||||||
|
|
||||||
|
Description string `json:"description"`
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ type IBackupRepo interface {
|
||||||
CreateRecord(record *model.BackupRecord) error
|
CreateRecord(record *model.BackupRecord) error
|
||||||
DeleteRecord(ctx context.Context, opts ...DBOption) error
|
DeleteRecord(ctx context.Context, opts ...DBOption) error
|
||||||
UpdateRecord(record *model.BackupRecord) error
|
UpdateRecord(record *model.BackupRecord) error
|
||||||
|
UpdateRecordByMap(id uint, upMap map[string]interface{}) error
|
||||||
WithByDetailName(detailName string) DBOption
|
WithByDetailName(detailName string) DBOption
|
||||||
WithByFileName(fileName string) DBOption
|
WithByFileName(fileName string) DBOption
|
||||||
WithByCronID(cronjobID uint) DBOption
|
WithByCronID(cronjobID uint) DBOption
|
||||||
|
|
@ -144,6 +145,10 @@ func (u *BackupRepo) UpdateRecord(record *model.BackupRecord) error {
|
||||||
return global.DB.Save(record).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 {
|
func (u *BackupRepo) DeleteRecord(ctx context.Context, opts ...DBOption) error {
|
||||||
return getTx(ctx, opts...).Delete(&model.BackupRecord{}).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,
|
DownloadAccountID: 1,
|
||||||
FileDir: itemDir,
|
FileDir: itemDir,
|
||||||
FileName: fileName,
|
FileName: fileName,
|
||||||
|
Description: req.Description,
|
||||||
}
|
}
|
||||||
if err := backupRepo.CreateRecord(record); err != nil {
|
if err := backupRepo.CreateRecord(record); err != nil {
|
||||||
global.LOG.Errorf("save backup record failed, err: %v", err)
|
global.LOG.Errorf("save backup record failed, err: %v", err)
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,7 @@ func (u *BackupService) MysqlBackup(req dto.CommonBackup) error {
|
||||||
DownloadAccountID: 1,
|
DownloadAccountID: 1,
|
||||||
FileDir: itemDir,
|
FileDir: itemDir,
|
||||||
FileName: fileName,
|
FileName: fileName,
|
||||||
|
Description: req.Description,
|
||||||
}
|
}
|
||||||
if err := backupRepo.CreateRecord(record); err != nil {
|
if err := backupRepo.CreateRecord(record); err != nil {
|
||||||
global.LOG.Errorf("save backup record failed, err: %v", err)
|
global.LOG.Errorf("save backup record failed, err: %v", err)
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,7 @@ func (u *BackupService) PostgresqlBackup(req dto.CommonBackup) error {
|
||||||
DownloadAccountID: 1,
|
DownloadAccountID: 1,
|
||||||
FileDir: itemDir,
|
FileDir: itemDir,
|
||||||
FileName: fileName,
|
FileName: fileName,
|
||||||
|
Description: req.Description,
|
||||||
}
|
}
|
||||||
if err := backupRepo.CreateRecord(record); err != nil {
|
if err := backupRepo.CreateRecord(record); err != nil {
|
||||||
global.LOG.Errorf("save backup record failed, err: %v", err)
|
global.LOG.Errorf("save backup record failed, err: %v", err)
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ type IBackupRecordService interface {
|
||||||
|
|
||||||
ListFiles(req dto.OperateByID) []string
|
ListFiles(req dto.OperateByID) []string
|
||||||
LoadRecordSize(req dto.SearchForSize) ([]dto.RecordFileSize, error)
|
LoadRecordSize(req dto.SearchForSize) ([]dto.RecordFileSize, error)
|
||||||
|
UpdateDescription(req dto.UpdateDescription) error
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewIBackupRecordService() IBackupRecordService {
|
func NewIBackupRecordService() IBackupRecordService {
|
||||||
|
|
@ -261,3 +262,7 @@ func (u *BackupRecordService) LoadRecordSize(req dto.SearchForSize) ([]dto.Recor
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
return datas, nil
|
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,
|
DownloadAccountID: 1,
|
||||||
FileDir: itemDir,
|
FileDir: itemDir,
|
||||||
FileName: fileName,
|
FileName: fileName,
|
||||||
|
Description: req.Description,
|
||||||
}
|
}
|
||||||
if err := backupRepo.CreateRecord(record); err != nil {
|
if err := backupRepo.CreateRecord(record); err != nil {
|
||||||
global.LOG.Errorf("save backup record failed, err: %v", err)
|
global.LOG.Errorf("save backup record failed, err: %v", err)
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,7 @@ func (u *BackupService) WebsiteBackup(req dto.CommonBackup) error {
|
||||||
DownloadAccountID: 1,
|
DownloadAccountID: 1,
|
||||||
FileDir: itemDir,
|
FileDir: itemDir,
|
||||||
FileName: fileName,
|
FileName: fileName,
|
||||||
|
Description: req.Description,
|
||||||
}
|
}
|
||||||
if err = backupRepo.CreateRecord(record); err != nil {
|
if err = backupRepo.CreateRecord(record); err != nil {
|
||||||
global.LOG.Errorf("save backup record failed, err: %v", err)
|
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{
|
var AddTable = &gormigrate.Migration{
|
||||||
ID: "20250108-add-table",
|
ID: "20250408-add-table",
|
||||||
Migrate: func(tx *gorm.DB) error {
|
Migrate: func(tx *gorm.DB) error {
|
||||||
return tx.AutoMigrate(
|
return tx.AutoMigrate(
|
||||||
&model.AppDetail{},
|
&model.AppDetail{},
|
||||||
|
|
|
||||||
|
|
@ -32,5 +32,6 @@ func (s *BackupRouter) InitRouter(Router *gin.RouterGroup) {
|
||||||
backupRouter.POST("/record/search/bycronjob", baseApi.SearchBackupRecordsByCronjob)
|
backupRouter.POST("/record/search/bycronjob", baseApi.SearchBackupRecordsByCronjob)
|
||||||
backupRouter.POST("/record/download", baseApi.DownloadRecord)
|
backupRouter.POST("/record/download", baseApi.DownloadRecord)
|
||||||
backupRouter.POST("/record/del", baseApi.DeleteBackupRecord)
|
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[] }) => {
|
export const deleteBackupRecord = (params: { ids: number[] }) => {
|
||||||
return http.post(`/backups/record/del`, params);
|
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) => {
|
export const searchBackupRecords = (params: Backup.SearchBackupRecord) => {
|
||||||
return http.post<ResPage<Backup.RecordInfo>>(`/backups/record/search`, params, TimeoutEnum.T_5M);
|
return http.post<ResPage<Backup.RecordInfo>>(`/backups/record/search`, params, TimeoutEnum.T_5M);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,10 @@
|
||||||
:header="$t('commons.button.backup')"
|
:header="$t('commons.button.backup')"
|
||||||
:resource="detailName ? name + ' [' + detailName + ']' : name"
|
:resource="detailName ? name + ' [' + detailName + ']' : name"
|
||||||
@close="handleClose"
|
@close="handleClose"
|
||||||
size="large"
|
size="60%"
|
||||||
>
|
>
|
||||||
<template #content>
|
<template #content>
|
||||||
<div class="mb-5" v-if="type === 'app'">
|
<el-alert v-if="type === 'app'" :closable="false" type="warning">
|
||||||
<el-alert :closable="false" type="warning">
|
|
||||||
<div class="mt-2 text-xs">
|
<div class="mt-2 text-xs">
|
||||||
<span>{{ $t('setting.backupJump') }}</span>
|
<span>{{ $t('setting.backupJump') }}</span>
|
||||||
<span class="jump" @click="goFile()">
|
<span class="jump" @click="goFile()">
|
||||||
|
|
@ -17,14 +16,15 @@
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</el-alert>
|
</el-alert>
|
||||||
</div>
|
|
||||||
|
|
||||||
<ComplexTable
|
<ComplexTable
|
||||||
|
class="mt-5"
|
||||||
v-loading="loading"
|
v-loading="loading"
|
||||||
:pagination-config="paginationConfig"
|
:pagination-config="paginationConfig"
|
||||||
v-model:selects="selects"
|
v-model:selects="selects"
|
||||||
@search="search"
|
@search="search"
|
||||||
:data="data"
|
:data="data"
|
||||||
|
style="width: 100%"
|
||||||
>
|
>
|
||||||
<template #toolbar>
|
<template #toolbar>
|
||||||
<el-button type="primary" :disabled="status && status != 'Running'" @click="onBackup()">
|
<el-button type="primary" :disabled="status && status != 'Running'" @click="onBackup()">
|
||||||
|
|
@ -49,7 +49,7 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</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 }">
|
<template #default="{ row }">
|
||||||
<span v-if="row.accountType === 'LOCAL'">
|
<span v-if="row.accountType === 'LOCAL'">
|
||||||
{{ $t('setting.LOCAL') }}
|
{{ $t('setting.LOCAL') }}
|
||||||
|
|
@ -61,13 +61,24 @@
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<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"
|
prop="createdAt"
|
||||||
:label="$t('commons.table.date')"
|
:label="$t('commons.table.date')"
|
||||||
:formatter="dateFormat"
|
:formatter="dateFormat"
|
||||||
show-overflow-tooltip
|
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>
|
</ComplexTable>
|
||||||
</template>
|
</template>
|
||||||
</DrawerPro>
|
</DrawerPro>
|
||||||
|
|
@ -78,14 +89,16 @@
|
||||||
size="small"
|
size="small"
|
||||||
@close="handleBackupClose"
|
@close="handleBackupClose"
|
||||||
>
|
>
|
||||||
<el-form ref="backupForm" @submit.prevent label-position="left" v-loading="loading">
|
<el-alert :closable="false">
|
||||||
<el-form-item
|
{{ $t('commons.msg.' + (isBackup ? 'backupHelper' : 'recoverHelper'), [name + '( ' + detailName + ' )']) }}
|
||||||
:label="$t('setting.compressPassword')"
|
</el-alert>
|
||||||
class="mt-10"
|
<el-form class="mt-5" ref="backupForm" @submit.prevent label-position="top" v-loading="loading">
|
||||||
v-if="type === 'app' || type === 'website'"
|
<el-form-item :label="$t('setting.compressPassword')" v-if="type === 'app' || type === 'website'">
|
||||||
>
|
|
||||||
<el-input v-model="secret" :placeholder="$t('setting.backupRecoverMessage')" />
|
<el-input v-model="secret" :placeholder="$t('setting.backupRecoverMessage')" />
|
||||||
</el-form-item>
|
</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>
|
</el-form>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<span class="dialog-footer">
|
<span class="dialog-footer">
|
||||||
|
|
@ -114,6 +127,7 @@ import {
|
||||||
downloadBackupRecord,
|
downloadBackupRecord,
|
||||||
searchBackupRecords,
|
searchBackupRecords,
|
||||||
loadRecordSize,
|
loadRecordSize,
|
||||||
|
updateRecordDescription,
|
||||||
} from '@/api/modules/backup';
|
} from '@/api/modules/backup';
|
||||||
import i18n from '@/lang';
|
import i18n from '@/lang';
|
||||||
import { Backup } from '@/api/interface/backup';
|
import { Backup } from '@/api/interface/backup';
|
||||||
|
|
@ -142,6 +156,7 @@ const detailName = ref();
|
||||||
const backupPath = ref();
|
const backupPath = ref();
|
||||||
const status = ref();
|
const status = ref();
|
||||||
const secret = ref();
|
const secret = ref();
|
||||||
|
const description = ref();
|
||||||
|
|
||||||
const open = ref();
|
const open = ref();
|
||||||
const isBackup = ref();
|
const isBackup = ref();
|
||||||
|
|
@ -181,6 +196,11 @@ const goFile = async () => {
|
||||||
router.push({ name: 'File', query: { path: `${backupPath.value}/app/${name.value}/${detailName.value}` } });
|
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 () => {
|
const search = async () => {
|
||||||
let params = {
|
let params = {
|
||||||
page: paginationConfig.currentPage,
|
page: paginationConfig.currentPage,
|
||||||
|
|
@ -236,6 +256,7 @@ const backup = async (close: boolean) => {
|
||||||
detailName: detailName.value,
|
detailName: detailName.value,
|
||||||
secret: secret.value,
|
secret: secret.value,
|
||||||
taskID: taskID,
|
taskID: taskID,
|
||||||
|
description: description.value,
|
||||||
};
|
};
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
try {
|
try {
|
||||||
|
|
@ -285,20 +306,8 @@ const recover = async (close: boolean, row?: any) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const onBackup = async () => {
|
const onBackup = async () => {
|
||||||
|
description.value = '';
|
||||||
isBackup.value = true;
|
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;
|
open.value = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -103,6 +103,7 @@ onMounted(() => {
|
||||||
let itemSize = Number(localStorage.getItem(props.paginationConfig.cacheSizeKey));
|
let itemSize = Number(localStorage.getItem(props.paginationConfig.cacheSizeKey));
|
||||||
if (itemSize) {
|
if (itemSize) {
|
||||||
props.paginationConfig.pageSize = itemSize;
|
props.paginationConfig.pageSize = itemSize;
|
||||||
|
sizeChange();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let heightDiff = 320;
|
let heightDiff = 320;
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue