mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2024-11-24 02:59:16 +08:00
parent
e3329597bc
commit
e359e1c544
@ -135,6 +135,28 @@ func (b *BaseApi) SearchSnapshot(c *gin.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
// @Tags System Setting
|
||||
// @Summary Load system snapshot size
|
||||
// @Description 获取系统快照文件大小
|
||||
// @Accept json
|
||||
// @Param request body dto.SearchWithPage true "request"
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /settings/snapshot/size [post]
|
||||
func (b *BaseApi) LoadSnapshotSize(c *gin.Context) {
|
||||
var req dto.SearchWithPage
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
accounts, err := snapshotService.LoadSize(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, accounts)
|
||||
}
|
||||
|
||||
// @Tags System Setting
|
||||
// @Summary Recover system backup
|
||||
// @Description 从系统快照恢复
|
||||
|
@ -101,3 +101,11 @@ type SnapshotInfo struct {
|
||||
RollbackMessage string `json:"rollbackMessage"`
|
||||
LastRollbackedAt string `json:"lastRollbackedAt"`
|
||||
}
|
||||
|
||||
type SnapshotFile struct {
|
||||
ID uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
From string `json:"from"`
|
||||
DefaultDownload string `json:"defaultDownload"`
|
||||
Size int64 `json:"size"`
|
||||
}
|
||||
|
@ -120,6 +120,7 @@ func (u *BackupService) CheckUsed(id uint) error {
|
||||
|
||||
type loadSizeHelper struct {
|
||||
isOk bool
|
||||
backupName string
|
||||
backupPath string
|
||||
client cloud_storage.CloudStorageClient
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
@ -18,7 +19,6 @@ import (
|
||||
"github.com/docker/docker/api/types/image"
|
||||
"github.com/google/uuid"
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/shirou/gopsutil/v3/host"
|
||||
)
|
||||
|
||||
@ -28,6 +28,7 @@ type SnapshotService struct {
|
||||
|
||||
type ISnapshotService interface {
|
||||
SearchWithPage(req dto.SearchWithPage) (int64, interface{}, error)
|
||||
LoadSize(req dto.SearchWithPage) ([]dto.SnapshotFile, error)
|
||||
LoadSnapshotData() (dto.SnapshotData, error)
|
||||
SnapshotCreate(req dto.SnapshotCreate) error
|
||||
SnapshotReCreate(id uint) error
|
||||
@ -46,15 +47,64 @@ func NewISnapshotService() ISnapshotService {
|
||||
}
|
||||
|
||||
func (u *SnapshotService) SearchWithPage(req dto.SearchWithPage) (int64, interface{}, error) {
|
||||
total, systemBackups, err := snapshotRepo.Page(req.Page, req.PageSize, commonRepo.WithByLikeName(req.Info))
|
||||
total, records, err := snapshotRepo.Page(req.Page, req.PageSize, commonRepo.WithByLikeName(req.Info))
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
dtoSnap, err := loadSnapSize(systemBackups)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
var datas []dto.SnapshotInfo
|
||||
for i := 0; i < len(records); i++ {
|
||||
var item dto.SnapshotInfo
|
||||
if err := copier.Copy(&item, &records[i]); err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
datas = append(datas, item)
|
||||
}
|
||||
return total, dtoSnap, err
|
||||
return total, datas, err
|
||||
}
|
||||
|
||||
func (u *SnapshotService) LoadSize(req dto.SearchWithPage) ([]dto.SnapshotFile, error) {
|
||||
_, records, err := snapshotRepo.Page(req.Page, req.PageSize, commonRepo.WithByLikeName(req.Info))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var datas []dto.SnapshotFile
|
||||
var wg sync.WaitGroup
|
||||
clientMap := make(map[uint]loadSizeHelper)
|
||||
for i := 0; i < len(records); i++ {
|
||||
itemPath := fmt.Sprintf("system_snapshot/%s.tar.gz", records[i].Name)
|
||||
data := dto.SnapshotFile{ID: records[i].ID, Name: records[i].Name}
|
||||
accounts := strings.Split(records[i].SourceAccountIDs, ",")
|
||||
var accountNames []string
|
||||
for _, account := range accounts {
|
||||
itemVal, _ := strconv.Atoi(account)
|
||||
if _, ok := clientMap[uint(itemVal)]; !ok {
|
||||
backup, client, err := NewBackupClientWithID(uint(itemVal))
|
||||
if err != nil {
|
||||
global.LOG.Errorf("load backup client from db failed, err: %v", err)
|
||||
clientMap[records[i].DownloadAccountID] = loadSizeHelper{}
|
||||
continue
|
||||
}
|
||||
backupName := fmt.Sprintf("%s - %s", backup.Type, backup.Name)
|
||||
clientMap[uint(itemVal)] = loadSizeHelper{backupPath: strings.TrimLeft(backup.BackupPath, "/"), client: client, isOk: true, backupName: backupName}
|
||||
accountNames = append(accountNames, backupName)
|
||||
}
|
||||
}
|
||||
data.DefaultDownload = clientMap[records[i].DownloadAccountID].backupName
|
||||
data.From = strings.Join(accountNames, ",")
|
||||
if clientMap[records[i].DownloadAccountID].isOk {
|
||||
wg.Add(1)
|
||||
go func(index int) {
|
||||
data.Size, _ = clientMap[records[index].DownloadAccountID].client.Size(path.Join(clientMap[records[index].DownloadAccountID].backupPath, itemPath))
|
||||
datas = append(datas, data)
|
||||
wg.Done()
|
||||
}(i)
|
||||
} else {
|
||||
datas = append(datas, data)
|
||||
}
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
return datas, nil
|
||||
}
|
||||
|
||||
func (u *SnapshotService) SnapshotImport(req dto.SnapshotImport) error {
|
||||
@ -175,44 +225,6 @@ func loadOs() string {
|
||||
}
|
||||
}
|
||||
|
||||
func loadSnapSize(records []model.Snapshot) ([]dto.SnapshotInfo, error) {
|
||||
var datas []dto.SnapshotInfo
|
||||
clientMap := make(map[uint]loadSizeHelper)
|
||||
var wg sync.WaitGroup
|
||||
for i := 0; i < len(records); i++ {
|
||||
var item dto.SnapshotInfo
|
||||
if err := copier.Copy(&item, &records[i]); err != nil {
|
||||
return nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
|
||||
}
|
||||
itemPath := fmt.Sprintf("system_snapshot/%s.tar.gz", item.Name)
|
||||
if _, ok := clientMap[records[i].DownloadAccountID]; !ok {
|
||||
backup, client, err := NewBackupClientWithID(records[i].DownloadAccountID)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("load backup client from db failed, err: %v", err)
|
||||
clientMap[records[i].DownloadAccountID] = loadSizeHelper{}
|
||||
datas = append(datas, item)
|
||||
continue
|
||||
}
|
||||
item.Size, _ = client.Size(path.Join(strings.TrimLeft(backup.BackupPath, "/"), itemPath))
|
||||
datas = append(datas, item)
|
||||
clientMap[records[i].DownloadAccountID] = loadSizeHelper{backupPath: strings.TrimLeft(backup.BackupPath, "/"), client: client, isOk: true}
|
||||
continue
|
||||
}
|
||||
if clientMap[records[i].DownloadAccountID].isOk {
|
||||
wg.Add(1)
|
||||
go func(index int) {
|
||||
item.Size, _ = clientMap[records[index].DownloadAccountID].client.Size(path.Join(clientMap[records[index].DownloadAccountID].backupPath, itemPath))
|
||||
datas = append(datas, item)
|
||||
wg.Done()
|
||||
}(i)
|
||||
} else {
|
||||
datas = append(datas, item)
|
||||
}
|
||||
}
|
||||
wg.Wait()
|
||||
return datas, nil
|
||||
}
|
||||
|
||||
func loadApps(fileOp fileUtils.FileOp) ([]dto.DataTree, error) {
|
||||
var data []dto.DataTree
|
||||
apps, err := appInstallRepo.ListBy()
|
||||
|
@ -19,6 +19,7 @@ func (s *SettingRouter) InitRouter(Router *gin.RouterGroup) {
|
||||
settingRouter.POST("/snapshot", baseApi.CreateSnapshot)
|
||||
settingRouter.POST("/snapshot/recreate", baseApi.RecreateSnapshot)
|
||||
settingRouter.POST("/snapshot/search", baseApi.SearchSnapshot)
|
||||
settingRouter.POST("/snapshot/size", baseApi.LoadSnapshotSize)
|
||||
settingRouter.POST("/snapshot/import", baseApi.ImportSnapshot)
|
||||
settingRouter.POST("/snapshot/del", baseApi.DeleteSnapshot)
|
||||
settingRouter.POST("/snapshot/recover", baseApi.RecoverSnapshot)
|
||||
|
@ -166,6 +166,13 @@ export namespace Setting {
|
||||
rollbackStatus: string;
|
||||
rollbackMessage: string;
|
||||
}
|
||||
export interface SnapshotFile {
|
||||
id: number;
|
||||
name: string;
|
||||
from: string;
|
||||
defaultDownload: string;
|
||||
size: number;
|
||||
}
|
||||
export interface SnapshotData {
|
||||
appData: Array<DataTree>;
|
||||
panelData: Array<DataTree>;
|
||||
|
@ -124,6 +124,9 @@ export const snapshotRollback = (param: Setting.SnapshotRecover) => {
|
||||
export const searchSnapshotPage = (param: SearchWithPage) => {
|
||||
return http.post<ResPage<Setting.SnapshotInfo>>(`/settings/snapshot/search`, param);
|
||||
};
|
||||
export const loadSnapshotSize = (param: SearchWithPage) => {
|
||||
return http.post<Array<Setting.SnapshotFile>>(`/settings/snapshot/size`, param);
|
||||
};
|
||||
|
||||
// upgrade
|
||||
export const loadUpgradeInfo = () => {
|
||||
|
@ -38,41 +38,51 @@
|
||||
<el-table-column prop="version" :label="$t('app.version')" />
|
||||
<el-table-column :label="$t('setting.backupAccount')" min-width="80" prop="from">
|
||||
<template #default="{ row }">
|
||||
<div v-for="(item, index) of row.from.split(',')" :key="index" class="mt-1">
|
||||
<div v-if="row.expand || (!row.expand && index < 3)">
|
||||
<span v-if="row.from" type="info">
|
||||
<span>
|
||||
{{ $t('setting.' + item) }}
|
||||
<div v-if="row.hasLoad">
|
||||
<div v-for="(item, index) of row.from.split(',')" :key="index" class="mt-1">
|
||||
<div v-if="row.expand || (!row.expand && index < 3)">
|
||||
<span v-if="row.from" type="info">
|
||||
<span>
|
||||
{{ loadName(item) }}
|
||||
</span>
|
||||
<el-icon
|
||||
v-if="item === row.defaultDownload"
|
||||
size="12"
|
||||
class="relative top-px left-1"
|
||||
>
|
||||
<Star />
|
||||
</el-icon>
|
||||
</span>
|
||||
<el-icon
|
||||
v-if="item === row.defaultDownload"
|
||||
size="12"
|
||||
class="relative top-px left-1"
|
||||
>
|
||||
<Star />
|
||||
</el-icon>
|
||||
</span>
|
||||
<span v-else>-</span>
|
||||
<span v-else>-</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="!row.expand && row.from.split(',').length > 3">
|
||||
<el-button type="primary" link @click="row.expand = true">
|
||||
{{ $t('commons.button.expand') }}...
|
||||
</el-button>
|
||||
</div>
|
||||
<div v-if="row.expand && row.from.split(',').length > 3">
|
||||
<el-button type="primary" link @click="row.expand = false">
|
||||
{{ $t('commons.button.collapse') }}
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="!row.expand && row.from.split(',').length > 3">
|
||||
<el-button type="primary" link @click="row.expand = true">
|
||||
{{ $t('commons.button.expand') }}...
|
||||
</el-button>
|
||||
</div>
|
||||
<div v-if="row.expand && row.from.split(',').length > 3">
|
||||
<el-button type="primary" link @click="row.expand = false">
|
||||
{{ $t('commons.button.collapse') }}
|
||||
</el-button>
|
||||
<div v-if="!row.hasLoad">
|
||||
<el-button link loading></el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('file.size')" prop="size" min-width="60" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<span v-if="row.size">
|
||||
{{ computeSize(row.size) }}
|
||||
</span>
|
||||
<span v-else>-</span>
|
||||
<div v-if="row.hasLoad">
|
||||
<span v-if="row.size">
|
||||
{{ computeSize(row.size) }}
|
||||
</span>
|
||||
<span v-else>-</span>
|
||||
</div>
|
||||
<div v-if="!row.hasLoad">
|
||||
<el-button link loading></el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('commons.table.status')" min-width="80" prop="status">
|
||||
@ -168,6 +178,7 @@
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
searchSnapshotPage,
|
||||
loadSnapshotSize,
|
||||
snapshotDelete,
|
||||
snapshotRecreate,
|
||||
snapshotRollback,
|
||||
@ -371,6 +382,7 @@ const search = async () => {
|
||||
await searchSnapshotPage(params)
|
||||
.then((res) => {
|
||||
loading.value = false;
|
||||
loadSize();
|
||||
cleanData.value = false;
|
||||
data.value = res.data.items || [];
|
||||
paginationConfig.total = res.data.total;
|
||||
@ -380,6 +392,40 @@ const search = async () => {
|
||||
});
|
||||
};
|
||||
|
||||
const loadSize = async () => {
|
||||
let params = {
|
||||
info: searchName.value,
|
||||
page: paginationConfig.currentPage,
|
||||
pageSize: paginationConfig.pageSize,
|
||||
};
|
||||
await loadSnapshotSize(params)
|
||||
.then((res) => {
|
||||
let stats = res.data || [];
|
||||
if (stats.length === 0) {
|
||||
return;
|
||||
}
|
||||
for (const snap of data.value) {
|
||||
for (const item of stats) {
|
||||
if (snap.id === item.id) {
|
||||
snap.hasLoad = true;
|
||||
snap.from = item.from;
|
||||
snap.defaultDownload = item.defaultDownload;
|
||||
snap.size = item.size;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
};
|
||||
|
||||
const loadName = (from: any) => {
|
||||
let items = from.split(' - ');
|
||||
return i18n.global.t('setting.' + items[0]) + ' ' + items[1];
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
search();
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user