1
0
mirror of https://github.com/1Panel-dev/1Panel.git synced 2025-03-01 03:24:14 +08:00

feat: 增加系统临时文件清理功能 ()

This commit is contained in:
ssongliu 2023-09-28 15:40:17 +08:00 committed by GitHub
parent 2624238354
commit e8564f38ab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 1427 additions and 10 deletions
backend
app
init/migration/migrations
router
cmd/server/docs
frontend/src
api
interface
modules
lang/modules
views/setting/panel

@ -277,6 +277,37 @@ func (b *BaseApi) CleanMonitor(c *gin.Context) {
helper.SuccessWithData(c, nil) helper.SuccessWithData(c, nil)
} }
// @Tags System Setting
// @Summary Scan system
// @Description 扫描系统垃圾文件
// @Success 200
// @Security ApiKeyAuth
// @Router /settings/scan [post]
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFuntions":[],"formatZH":"扫描系统垃圾文件","formatEN":"scan System Junk Files"}
func (b *BaseApi) ScanSystem(c *gin.Context) {
helper.SuccessWithData(c, settingService.SystemScan())
}
// @Tags System Setting
// @Summary System clean
// @Description 清理系统垃圾文件
// @Accept json
// @Param request body []dto.Clean true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /settings/clean [post]
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFuntions":[],"formatZH":"清理系统垃圾文件","formatEN":"Clean system junk files"}
func (b *BaseApi) SystemClean(c *gin.Context) {
var req []dto.Clean
if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
settingService.SystemClean(req)
helper.SuccessWithData(c, nil)
}
// @Tags System Setting // @Tags System Setting
// @Summary Load mfa info // @Summary Load mfa info
// @Description 获取 mfa 信息 // @Description 获取 mfa 信息

@ -18,6 +18,9 @@ type SettingInfo struct {
Theme string `json:"theme"` Theme string `json:"theme"`
Language string `json:"language"` Language string `json:"language"`
DefaultNetwork string `json:"defaultNetwork"` DefaultNetwork string `json:"defaultNetwork"`
LastCleanTime string `json:"lastCleanTime"`
LastCleanSize string `json:"lastCleanSize"`
LastCleanData string `json:"lastCleanData"`
ServerPort string `json:"serverPort"` ServerPort string `json:"serverPort"`
SSL string `json:"ssl"` SSL string `json:"ssl"`
@ -136,3 +139,29 @@ type SyncTime struct {
type Upgrade struct { type Upgrade struct {
Version string `json:"version"` Version string `json:"version"`
} }
type CleanData struct {
SystemClean []CleanTree `json:"systemClean"`
UploadClean []CleanTree `json:"uploadClean"`
DownloadClean []CleanTree `json:"downloadClean"`
SystemLogClean []CleanTree `json:"systemLogClean"`
}
type CleanTree struct {
ID string `json:"id"`
Label string `json:"label"`
Children []CleanTree `json:"children"`
Type string `json:"type"`
Name string `json:"name"`
Size uint64 `json:"size"`
IsCheck bool `json:"isCheck"`
IsRecommend bool `json:"isRecommend"`
}
type Clean struct {
TreeType string `json:"treeType"`
Name string `json:"name"`
Size uint64 `json:"size"`
}

@ -22,6 +22,7 @@ type ICronjobRepo interface {
WithByJobID(id int) DBOption WithByJobID(id int) DBOption
WithByBackupID(id uint) DBOption WithByBackupID(id uint) DBOption
WithByRecordDropID(id int) DBOption WithByRecordDropID(id int) DBOption
WithByRecordFile(file string) DBOption
Save(id uint, cronjob model.Cronjob) error Save(id uint, cronjob model.Cronjob) error
Update(id uint, vars map[string]interface{}) error Update(id uint, vars map[string]interface{}) error
Delete(opts ...DBOption) error Delete(opts ...DBOption) error
@ -121,6 +122,12 @@ func (c *CronjobRepo) WithByBackupID(id uint) DBOption {
} }
} }
func (c *CronjobRepo) WithByRecordFile(file string) DBOption {
return func(g *gorm.DB) *gorm.DB {
return g.Where("records = ?", file)
}
}
func (c *CronjobRepo) WithByRecordDropID(id int) DBOption { func (c *CronjobRepo) WithByRecordDropID(id int) DBOption {
return func(g *gorm.DB) *gorm.DB { return func(g *gorm.DB) *gorm.DB {
return g.Where("id < ?", id) return g.Where("id < ?", id)

@ -38,6 +38,9 @@ type ISettingService interface {
LoadFromCert() (*dto.SSLInfo, error) LoadFromCert() (*dto.SSLInfo, error)
HandlePasswordExpired(c *gin.Context, old, new string) error HandlePasswordExpired(c *gin.Context, old, new string) error
SyncTime(req dto.SyncTime) error SyncTime(req dto.SyncTime) error
SystemScan() dto.CleanData
SystemClean(req []dto.Clean)
} }
func NewISettingService() ISettingService { func NewISettingService() ISettingService {

@ -0,0 +1,358 @@
package service
import (
"fmt"
"os"
"path"
"sort"
"strings"
"time"
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
fileUtils "github.com/1Panel-dev/1Panel/backend/utils/files"
"github.com/google/uuid"
)
func (u *SettingService) SystemScan() dto.CleanData {
var (
SystemClean dto.CleanData
treeData []dto.CleanTree
)
fileOp := fileUtils.NewFileOp()
originalPath := path.Join(global.CONF.System.BaseDir, "1panel_original")
originalSize, _ := fileOp.GetDirSize(originalPath)
treeData = append(treeData, dto.CleanTree{
ID: uuid.NewString(),
Label: "1panel_original",
Size: uint64(originalSize),
IsCheck: true,
IsRecommend: true,
Type: "1panel_original",
Children: loadTreeWithDir(true, "1panel_original", originalPath, fileOp),
})
upgradePath := path.Join(global.CONF.System.BaseDir, "1panel/tmp/upgrade")
upgradeSize, _ := fileOp.GetDirSize(upgradePath)
treeData = append(treeData, dto.CleanTree{
ID: uuid.NewString(),
Label: "upgrade",
Size: uint64(upgradeSize),
IsCheck: false,
IsRecommend: true,
Type: "upgrade",
Children: loadTreeWithDir(true, "upgrade", upgradePath, fileOp),
})
snapTree := loadSnapshotTree(fileOp)
snapSize := uint64(0)
for _, snap := range snapTree {
snapSize += uint64(snap.Size)
}
treeData = append(treeData, dto.CleanTree{
ID: uuid.NewString(),
Label: "snapshot",
Size: snapSize,
IsCheck: true,
IsRecommend: true,
Type: "snapshot",
Children: snapTree,
})
cachePath := path.Join(global.CONF.System.BaseDir, "1panel/cache")
cacheSize, _ := fileOp.GetDirSize(cachePath)
treeData = append(treeData, dto.CleanTree{
ID: uuid.NewString(),
Label: "cache",
Size: uint64(cacheSize),
IsCheck: false,
IsRecommend: false,
Type: "cache",
})
unusedTree := loadUnusedFile(fileOp)
unusedSize := uint64(0)
for _, unused := range unusedTree {
unusedSize += uint64(unused.Size)
}
treeData = append(treeData, dto.CleanTree{
ID: uuid.NewString(),
Label: "unused",
Size: unusedSize,
IsCheck: true,
IsRecommend: true,
Type: "unused",
Children: unusedTree,
})
SystemClean.SystemClean = treeData
uploadPath := path.Join(global.CONF.System.BaseDir, "1panel/uploads")
uploadTreeData := loadTreeWithAllFile(true, uploadPath, "upload", uploadPath, fileOp)
SystemClean.UploadClean = append(SystemClean.UploadClean, uploadTreeData...)
downloadPath := path.Join(global.CONF.System.BaseDir, "1panel/download")
downloadTreeData := loadTreeWithAllFile(true, downloadPath, "download", downloadPath, fileOp)
SystemClean.DownloadClean = append(SystemClean.DownloadClean, downloadTreeData...)
logTree := loadLogTree(fileOp)
SystemClean.SystemLogClean = append(SystemClean.SystemLogClean, logTree...)
return SystemClean
}
func (u *SettingService) SystemClean(req []dto.Clean) {
size := uint64(0)
for _, item := range req {
size += item.Size
switch item.TreeType {
case "1panel_original":
dropFileOrDir(path.Join(global.CONF.System.BaseDir, "1panel_original", item.Name))
case "upgrade":
dropFileOrDir(path.Join(global.CONF.System.BaseDir, "1panel/tmp/upgrade", item.Name))
case "snapshot":
dropFileOrDir(path.Join(global.CONF.System.BaseDir, "1panel/tmp/system", item.Name))
dropFileOrDir(path.Join(global.CONF.System.Backup, "system", item.Name))
case "snapshot_tmp":
dropFileOrDir(path.Join(global.CONF.System.BaseDir, "1panel/tmp/system", item.Name))
case "snapshot_local":
dropFileOrDir(path.Join(global.CONF.System.Backup, "system", item.Name))
case "cache":
dropFileOrDir(path.Join(global.CONF.System.BaseDir, "1panel/cache", item.Name))
defer func() {
_, _ = cmd.Exec("systemctl restart 1panel.service")
}()
case "unused":
dropFileOrDir(path.Join(global.CONF.System.BaseDir, "original", item.Name))
dropFileOrDir(path.Join(global.CONF.System.BaseDir, "1panel/resource/apps_bak", item.Name))
dropFileOrDir(path.Join(global.CONF.System.BaseDir, "1panel/tmp/download", item.Name))
dropFileOrDir(path.Join(global.CONF.System.BaseDir, "1panel/tmp", item.Name))
case "old_original":
dropFileOrDir(path.Join(global.CONF.System.BaseDir, "original", item.Name))
case "old_apps_bak":
dropFileOrDir(path.Join(global.CONF.System.BaseDir, "1panel/resource/apps_bak", item.Name))
case "old_download":
dropFileOrDir(path.Join(global.CONF.System.BaseDir, "1panel/tmp/download", item.Name))
case "old_upgrade":
if len(item.Name) == 0 {
files, _ := os.ReadDir(path.Join(global.CONF.System.BaseDir, "1panel/tmp"))
if len(files) == 0 {
continue
}
for _, file := range files {
dropFileOrDir(path.Join(global.CONF.System.BaseDir, "1panel/tmp", file.Name()))
}
} else {
dropFileOrDir(path.Join(global.CONF.System.BaseDir, "1panel/tmp", item.Name))
}
case "upload":
dropFileOrDir(path.Join(global.CONF.System.BaseDir, "1panel/uploads", item.Name))
case "download":
dropFileOrDir(path.Join(global.CONF.System.BaseDir, "1panel/download", item.Name))
case "system_log":
if len(item.Name) == 0 {
files, _ := os.ReadDir(path.Join(global.CONF.System.BaseDir, "1panel/log"))
if len(files) == 0 {
continue
}
for _, file := range files {
if file.Name() == "1Panel.log" {
continue
}
dropFileOrDir(path.Join(global.CONF.System.BaseDir, "1panel/log", file.Name()))
}
} else {
dropFileOrDir(path.Join(global.CONF.System.BaseDir, "1panel/log", item.Name))
}
case "docker_log":
dropFileOrDir(path.Join(global.CONF.System.BaseDir, "1panel/tmp/docker_logs", item.Name))
case "task_log":
pathItem := path.Join(global.CONF.System.BaseDir, "1panel/task", item.Name)
dropFileOrDir(path.Join(global.CONF.System.BaseDir, "1panel/task", item.Name))
if len(item.Name) == 0 {
files, _ := os.ReadDir(pathItem)
if len(files) == 0 {
continue
}
for _, file := range files {
_ = cronjobRepo.DeleteRecord(cronjobRepo.WithByRecordFile(path.Join(pathItem, file.Name())))
}
} else {
_ = cronjobRepo.DeleteRecord(cronjobRepo.WithByRecordFile(pathItem))
}
}
}
_ = settingRepo.Update("LastCleanTime", time.Now().Format("2006-01-02 15:04:05"))
_ = settingRepo.Update("LastCleanSize", fmt.Sprintf("%v", size))
_ = settingRepo.Update("LastCleanData", fmt.Sprintf("%v", len(req)))
}
func loadSnapshotTree(fileOp fileUtils.FileOp) []dto.CleanTree {
var treeData []dto.CleanTree
path1 := path.Join(global.CONF.System.BaseDir, "1panel/tmp/system")
list1 := loadTreeWithAllFile(true, path1, "snapshot_tmp", path1, fileOp)
if len(list1) != 0 {
size, _ := fileOp.GetDirSize(path1)
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "snapshot_tmp", Size: uint64(size), Children: list1, Type: "snapshot_tmp", IsRecommend: true})
}
path2 := path.Join(global.CONF.System.Backup, "system")
list2 := loadTreeWithAllFile(true, path2, "snapshot_local", path2, fileOp)
if len(list2) != 0 {
size, _ := fileOp.GetDirSize(path2)
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "snapshot_local", Size: uint64(size), Children: list2, Type: "snapshot_local", IsRecommend: true})
}
return treeData
}
func loadUnusedFile(fileOp fileUtils.FileOp) []dto.CleanTree {
var treeData []dto.CleanTree
path1 := path.Join(global.CONF.System.BaseDir, "original")
list1 := loadTreeWithAllFile(true, path1, "old_original", path1, fileOp)
if len(list1) != 0 {
size, _ := fileOp.GetDirSize(path1)
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "old_original", Size: uint64(size), Children: list1, Type: "old_original"})
}
path2 := path.Join(global.CONF.System.BaseDir, "1panel/resource/apps_bak")
list2 := loadTreeWithAllFile(true, path2, "old_apps_bak", path2, fileOp)
if len(list2) != 0 {
size, _ := fileOp.GetDirSize(path2)
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "old_apps_bak", Size: uint64(size), Children: list2, Type: "old_apps_bak"})
}
path3 := path.Join(global.CONF.System.BaseDir, "1panel/tmp/download")
list3 := loadTreeWithAllFile(true, path3, "old_download", path3, fileOp)
if len(list3) != 0 {
size, _ := fileOp.GetDirSize(path3)
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "old_download", Size: uint64(size), Children: list3, Type: "old_download"})
}
path4 := path.Join(global.CONF.System.BaseDir, "1panel/tmp")
list4 := loadTreeWithDir(true, "old_upgrade", path4, fileOp)
itemSize := uint64(0)
for _, item := range list4 {
itemSize += item.Size
}
if len(list4) != 0 {
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "old_upgrade", Size: itemSize, Children: list4, Type: "old_upgrade"})
}
return treeData
}
func loadLogTree(fileOp fileUtils.FileOp) []dto.CleanTree {
var treeData []dto.CleanTree
path1 := path.Join(global.CONF.System.BaseDir, "1panel/log")
list1 := loadTreeWithAllFile(true, path1, "system_log", path1, fileOp)
size := uint64(0)
for _, file := range list1 {
size += file.Size
}
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "system_log", Size: uint64(size), Children: list1, Type: "system_log", IsRecommend: true})
path2 := path.Join(global.CONF.System.BaseDir, "1panel/tmp/docker_logs")
list2 := loadTreeWithAllFile(true, path2, "docker_log", path2, fileOp)
size2, _ := fileOp.GetDirSize(path2)
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "docker_log", Size: uint64(size2), Children: list2, Type: "docker_log", IsRecommend: true})
path3 := path.Join(global.CONF.System.BaseDir, "1panel/task")
list3 := loadTreeWithAllFile(false, path3, "task_log", path3, fileOp)
size3, _ := fileOp.GetDirSize(path3)
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "task_log", Size: uint64(size3), Children: list3, Type: "task_log"})
return treeData
}
func loadTreeWithDir(isCheck bool, treeType, pathItem string, fileOp fileUtils.FileOp) []dto.CleanTree {
var lists []dto.CleanTree
files, err := os.ReadDir(pathItem)
if err != nil {
return lists
}
sort.Slice(files, func(i, j int) bool {
return files[i].Name() > files[j].Name()
})
for _, file := range files {
if (treeType == "old_upgrade" || treeType == "upgrade") && !strings.HasPrefix(file.Name(), "upgrade_2023") {
continue
}
if file.IsDir() {
size, err := fileOp.GetDirSize(path.Join(pathItem, file.Name()))
if err != nil {
continue
}
item := dto.CleanTree{
ID: uuid.NewString(),
Label: file.Name(),
Type: treeType,
Size: uint64(size),
Name: strings.TrimPrefix(file.Name(), "/"),
IsCheck: isCheck,
IsRecommend: isCheck,
}
if treeType == "upgrade" && len(lists) == 0 {
item.IsCheck = false
item.IsRecommend = false
}
lists = append(lists, item)
}
}
return lists
}
func loadTreeWithAllFile(isCheck bool, originalPath, treeType, pathItem string, fileOp fileUtils.FileOp) []dto.CleanTree {
var lists []dto.CleanTree
files, err := os.ReadDir(pathItem)
if err != nil {
return lists
}
for _, file := range files {
if treeType == "system_log" && file.Name() == "1Panel.log" {
continue
}
size := uint64(0)
name := strings.TrimPrefix(path.Join(pathItem, file.Name()), originalPath+"/")
if file.IsDir() {
sizeItem, err := fileOp.GetDirSize(path.Join(pathItem, file.Name()))
if err != nil {
continue
}
size = uint64(sizeItem)
} else {
fileInfo, err := file.Info()
if err != nil {
continue
}
size = uint64(fileInfo.Size())
}
item := dto.CleanTree{
ID: uuid.NewString(),
Label: file.Name(),
Type: treeType,
Size: uint64(size),
Name: name,
IsCheck: isCheck,
IsRecommend: isCheck,
}
if file.IsDir() {
item.Children = loadTreeWithAllFile(isCheck, originalPath, treeType, path.Join(pathItem, file.Name()), fileOp)
}
lists = append(lists, item)
}
return lists
}
func dropFileOrDir(itemPath string) {
global.LOG.Debugf("drop file %s", itemPath)
if err := os.RemoveAll(itemPath); err != nil {
global.LOG.Errorf("drop file %s failed, err %v", itemPath, err)
}
}

@ -7,11 +7,20 @@ import (
) )
var AddDefaultNetwork = &gormigrate.Migration{ var AddDefaultNetwork = &gormigrate.Migration{
ID: "20230918-add-default-network", ID: "20230928-add-default-network",
Migrate: func(tx *gorm.DB) error { Migrate: func(tx *gorm.DB) error {
if err := tx.Create(&model.Setting{Key: "DefaultNetwork", Value: ""}).Error; err != nil { if err := tx.Create(&model.Setting{Key: "DefaultNetwork", Value: ""}).Error; err != nil {
return err return err
} }
if err := tx.Create(&model.Setting{Key: "LastCleanTime", Value: ""}).Error; err != nil {
return err
}
if err := tx.Create(&model.Setting{Key: "LastCleanSize", Value: ""}).Error; err != nil {
return err
}
if err := tx.Create(&model.Setting{Key: "LastCleanData", Value: ""}).Error; err != nil {
return err
}
return nil return nil
}, },
} }

@ -32,6 +32,8 @@ func (s *SettingRouter) InitSettingRouter(Router *gin.RouterGroup) {
settingRouter.POST("/monitor/clean", baseApi.CleanMonitor) settingRouter.POST("/monitor/clean", baseApi.CleanMonitor)
settingRouter.GET("/mfa/:interval", baseApi.GetMFA) settingRouter.GET("/mfa/:interval", baseApi.GetMFA)
settingRouter.POST("/mfa/bind", baseApi.MFABind) settingRouter.POST("/mfa/bind", baseApi.MFABind)
settingRouter.POST("/scan", baseApi.ScanSystem)
settingRouter.POST("/clean", baseApi.SystemClean)
settingRouter.POST("/snapshot", baseApi.CreateSnapshot) settingRouter.POST("/snapshot", baseApi.CreateSnapshot)
settingRouter.POST("/snapshot/status", baseApi.LoadSnapShotStatus) settingRouter.POST("/snapshot/status", baseApi.LoadSnapShotStatus)

@ -1,5 +1,5 @@
// Package docs GENERATED BY SWAG; DO NOT EDIT // Code generated by swaggo/swag. DO NOT EDIT.
// This file was generated by swaggo/swag
package docs package docs
import "github.com/swaggo/swag" import "github.com/swaggo/swag"
@ -8565,6 +8565,49 @@ const docTemplate = `{
} }
} }
}, },
"/settings/clean": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "清理系统垃圾文件",
"consumes": [
"application/json"
],
"tags": [
"System Setting"
],
"summary": "System clean",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/dto.Clean"
}
}
}
],
"responses": {
"200": {
"description": "OK"
}
},
"x-panel-log": {
"BeforeFuntions": [],
"bodyKeys": [],
"formatEN": "Clean system junk files",
"formatZH": "清理系统垃圾文件",
"paramKeys": []
}
}
},
"/settings/expired/handle": { "/settings/expired/handle": {
"post": { "post": {
"security": [ "security": [
@ -8784,6 +8827,32 @@ const docTemplate = `{
} }
} }
}, },
"/settings/scan": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "扫描系统垃圾文件",
"tags": [
"System Setting"
],
"summary": "Scan system",
"responses": {
"200": {
"description": "OK"
}
},
"x-panel-log": {
"BeforeFuntions": [],
"bodyKeys": [],
"formatEN": "scan System Junk Files",
"formatZH": "扫描系统垃圾文件",
"paramKeys": []
}
}
},
"/settings/search": { "/settings/search": {
"post": { "post": {
"security": [ "security": [
@ -12132,6 +12201,20 @@ const docTemplate = `{
} }
} }
}, },
"dto.Clean": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"size": {
"type": "integer"
},
"treeType": {
"type": "string"
}
}
},
"dto.CleanLog": { "dto.CleanLog": {
"type": "object", "type": "object",
"required": [ "required": [
@ -14904,6 +14987,15 @@ const docTemplate = `{
"language": { "language": {
"type": "string" "type": "string"
}, },
"lastCleanData": {
"type": "string"
},
"lastCleanSize": {
"type": "string"
},
"lastCleanTime": {
"type": "string"
},
"localTime": { "localTime": {
"type": "string" "type": "string"
}, },

@ -8558,6 +8558,49 @@
} }
} }
}, },
"/settings/clean": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "清理系统垃圾文件",
"consumes": [
"application/json"
],
"tags": [
"System Setting"
],
"summary": "System clean",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/dto.Clean"
}
}
}
],
"responses": {
"200": {
"description": "OK"
}
},
"x-panel-log": {
"BeforeFuntions": [],
"bodyKeys": [],
"formatEN": "Clean system junk files",
"formatZH": "清理系统垃圾文件",
"paramKeys": []
}
}
},
"/settings/expired/handle": { "/settings/expired/handle": {
"post": { "post": {
"security": [ "security": [
@ -8777,6 +8820,32 @@
} }
} }
}, },
"/settings/scan": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "扫描系统垃圾文件",
"tags": [
"System Setting"
],
"summary": "Scan system",
"responses": {
"200": {
"description": "OK"
}
},
"x-panel-log": {
"BeforeFuntions": [],
"bodyKeys": [],
"formatEN": "scan System Junk Files",
"formatZH": "扫描系统垃圾文件",
"paramKeys": []
}
}
},
"/settings/search": { "/settings/search": {
"post": { "post": {
"security": [ "security": [
@ -12125,6 +12194,20 @@
} }
} }
}, },
"dto.Clean": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"size": {
"type": "integer"
},
"treeType": {
"type": "string"
}
}
},
"dto.CleanLog": { "dto.CleanLog": {
"type": "object", "type": "object",
"required": [ "required": [
@ -14897,6 +14980,15 @@
"language": { "language": {
"type": "string" "type": "string"
}, },
"lastCleanData": {
"type": "string"
},
"lastCleanSize": {
"type": "string"
},
"lastCleanTime": {
"type": "string"
},
"localTime": { "localTime": {
"type": "string" "type": "string"
}, },

@ -170,6 +170,15 @@ definitions:
required: required:
- value - value
type: object type: object
dto.Clean:
properties:
name:
type: string
size:
type: integer
treeType:
type: string
type: object
dto.CleanLog: dto.CleanLog:
properties: properties:
logType: logType:
@ -2038,6 +2047,12 @@ definitions:
type: string type: string
language: language:
type: string type: string
lastCleanData:
type: string
lastCleanSize:
type: string
lastCleanTime:
type: string
localTime: localTime:
type: string type: string
messageType: messageType:
@ -9542,6 +9557,34 @@ paths:
summary: Load local backup dir summary: Load local backup dir
tags: tags:
- System Setting - System Setting
/settings/clean:
post:
consumes:
- application/json
description: 清理系统垃圾文件
parameters:
- description: request
in: body
name: request
required: true
schema:
items:
$ref: '#/definitions/dto.Clean'
type: array
responses:
"200":
description: OK
security:
- ApiKeyAuth: []
summary: System clean
tags:
- System Setting
x-panel-log:
BeforeFuntions: []
bodyKeys: []
formatEN: Clean system junk files
formatZH: 清理系统垃圾文件
paramKeys: []
/settings/expired/handle: /settings/expired/handle:
post: post:
consumes: consumes:
@ -9683,6 +9726,23 @@ paths:
formatEN: update system port => [serverPort] formatEN: update system port => [serverPort]
formatZH: 修改系统端口 => [serverPort] formatZH: 修改系统端口 => [serverPort]
paramKeys: [] paramKeys: []
/settings/scan:
post:
description: 扫描系统垃圾文件
responses:
"200":
description: OK
security:
- ApiKeyAuth: []
summary: Scan system
tags:
- System Setting
x-panel-log:
BeforeFuntions: []
bodyKeys: []
formatEN: scan System Junk Files
formatZH: 扫描系统垃圾文件
paramKeys: []
/settings/search: /settings/search:
post: post:
description: 加载系统配置信息 description: 加载系统配置信息

@ -17,6 +17,9 @@ export namespace Setting {
theme: string; theme: string;
language: string; language: string;
defaultNetwork: string; defaultNetwork: string;
lastCleanTime: string;
lastCleanSize: string;
lastCleanData: string;
serverPort: number; serverPort: number;
ssl: string; ssl: string;
@ -76,6 +79,24 @@ export namespace Setting {
code: string; code: string;
interval: string; interval: string;
} }
export interface CleanData {
systemClean: Array<CleanTree>;
uploadClean: Array<CleanTree>;
downloadClean: Array<CleanTree>;
systemLogClean: Array<CleanTree>;
}
export interface CleanTree {
id: string;
label: string;
children: Array<CleanTree>;
type: string;
name: string;
size: number;
isCheck: boolean;
isRecommend: boolean;
}
export interface SnapshotCreate { export interface SnapshotCreate {
id: number; id: number;
from: string; from: string;

@ -39,6 +39,13 @@ export const handleExpired = (param: Setting.PasswordUpdate) => {
return http.post(`/settings/expired/handle`, param); return http.post(`/settings/expired/handle`, param);
}; };
export const scanSystem = () => {
return http.post<Setting.CleanData>(`/settings/scan`, {});
};
export const cleanSystem = (param: any) => {
return http.post(`/settings/clean`, param);
};
export const loadTimeZone = () => { export const loadTimeZone = () => {
return http.get<Array<string>>(`/settings/time/option`); return http.get<Array<string>>(`/settings/time/option`);
}; };

@ -89,7 +89,6 @@ const message = {
loadingText: { loadingText: {
Upgrading: 'System upgrade, please wait...', Upgrading: 'System upgrade, please wait...',
Restarting: 'System restart, please wait...', Restarting: 'System restart, please wait...',
Snapshoting: 'Making snapshots, please wait...',
Recovering: 'Recovering from snapshot, please wait...', Recovering: 'Recovering from snapshot, please wait...',
Rollbacking: 'Rollbacking from snapshot, please wait...', Rollbacking: 'Rollbacking from snapshot, please wait...',
}, },
@ -1203,6 +1202,58 @@ const message = {
doc2: 'Document', doc2: 'Document',
currentVersion: 'Version', currentVersion: 'Version',
}, },
clean: {
scan: 'Start Scanning',
clean: 'Clean Now',
reScan: 'Rescan',
cleanHelper:
'After checking the files and directories for cleaning, it cannot be undone (system cache cleaning requires a service restart). Do you want to continue?',
statusSuggest: '( Recommended Cleaning )',
statusClean: '( Very Clean )',
statusEmpty: 'Very clean, no cleaning needed!',
statusWarning: '( Proceed with Caution )',
lastCleanTime: 'Last Cleaned: {0}',
lastCleanHelper: 'Files and directories cleaned: {0}, Total cleaned: {1}',
cleanSuccessful: 'Cleaning Successful!',
currentCleanHelper: 'Files and directories cleaned in this session: {0}, Total cleaned: {1}',
suggest: '(Recommended)',
totalScan: 'Total junk files to be cleaned: ',
selectScan: 'Total selected junk files: ',
system: 'System Junk',
systemHelper:
'Scan system junk (specifically includes temporary files generated during snapshots, upgrades, and obsolete file contents during version iterations)',
panelOriginal: 'System snapshot recovery backup files',
upgrade: 'System upgrade backup files',
cache: 'System cache files',
snapshot: 'System snapshot temporary files',
snapshotTmp: 'System snapshot upload temporary files',
snapshotLocal: 'System snapshot creation temporary files',
unused: 'Unused system directories',
oldUpgrade: 'Unused pre-upgrade backup directories',
oldOriginal: 'Unused pre-snapshot recovery backup directories',
oldAppsBak: 'Unused application backup directories',
upload: 'Temporary upload files',
uploadHelper:
'Scan temporary upload directory (specifically includes temporary files uploaded from the system backup list)',
download: 'Temporary Download Files',
downloadHelper:
'Scan temporary download directory (specifically includes temporary files downloaded from third-party backup accounts by the system)',
website: 'Website',
app: 'Application',
database: 'Database',
directory: 'Directory',
systemLog: 'System Log Files',
systemLogHelper:
'Scan system junk (specifically includes system log information, container build or image pull log information, and log files generated in scheduled tasks)',
dockerLog: 'Container operation log files',
taskLog: 'Scheduled task execution log files',
shell: 'Shell script scheduled tasks',
containerShell: 'Container internal Shell script scheduled tasks',
curl: 'CURL scheduled tasks',
},
app: { app: {
app: 'Application', app: 'Application',
installName: 'Name', installName: 'Name',

@ -89,7 +89,6 @@ const message = {
loadingText: { loadingText: {
Upgrading: '系統升級中請稍候...', Upgrading: '系統升級中請稍候...',
Restarting: '系統重啟中請稍候...', Restarting: '系統重啟中請稍候...',
Snapshoting: '製作快照中請稍候...',
Recovering: '從快照恢復中請稍候...', Recovering: '從快照恢復中請稍候...',
Rollbacking: '快照回滾中請稍候...', Rollbacking: '快照回滾中請稍候...',
}, },
@ -1144,6 +1143,54 @@ const message = {
doc2: '文檔', doc2: '文檔',
currentVersion: '當前運行版本', currentVersion: '當前運行版本',
}, },
clean: {
scan: '開始掃描',
clean: '立即清理',
reScan: '重新掃描',
cleanHelper: '已勾選文件及目錄清理後無法回滾系統緩存文件清理需要重啟服務是否繼續',
statusSuggest: '( 建議清理 )',
statusClean: '( 很幹凈 )',
statusEmpty: '非常幹凈無需清理',
statusWarning: '( 謹慎操作 )',
lastCleanTime: '上次清理時間: {0}',
lastCleanHelper: '清理文件及目錄{0} 總計清理{1}',
cleanSuccessful: '清理成功',
currentCleanHelper: '本次清理文件及目錄{0} 總計清理{1}',
suggest: '( 建議清理 )',
totalScan: '待清理垃圾文件共計 ',
selectScan: '已選中垃圾文件共計 ',
system: '系統垃圾',
systemHelper: '掃描系統垃圾具體包括快照升級等過程中產生的臨時文件以及版本叠代過程中廢棄的文件內容',
panelOriginal: '系統快照恢復前備份文件',
upgrade: '系統升級備份文件',
cache: '系統緩存文件',
snapshot: '系統快照臨時文件',
snapshotTmp: '系統快照上傳臨時文件',
snapshotLocal: '系統快照製作臨時文件',
unused: '系統廢棄目錄',
oldUpgrade: '廢棄升級前備份目錄',
oldOriginal: '廢棄快照恢復前備份目錄',
oldAppsBak: '廢棄應用備份目錄',
upload: '臨時上傳文件',
uploadHelper: '掃描臨時上傳目錄具體包括系統上傳備份列表中上傳的臨時文件',
download: '臨時下載文件',
downloadHelper: '掃描臨時下載目錄具體包括系統從第三方備份賬號下載的臨時文件',
website: '網站',
app: '應用',
database: '數據庫',
directory: '應用',
systemLog: '系統日誌文件',
systemLogHelper:
'掃描系統垃圾具體包括系統日誌信息容器構建或鏡像拉取等日誌信息以及計劃任務中產生的日誌文件',
dockerLog: '容器操作日誌文件',
taskLog: '計劃任務執行日誌文件',
shell: 'Shell 腳本計劃任務',
containerShell: '容器內執行 Shell 腳本計劃任務',
curl: 'CURL 計劃任務',
},
app: { app: {
app: '應用', app: '應用',
installName: '安裝名稱', installName: '安裝名稱',

@ -89,7 +89,6 @@ const message = {
loadingText: { loadingText: {
Upgrading: '系统升级中请稍候...', Upgrading: '系统升级中请稍候...',
Restarting: '系统重启中请稍候...', Restarting: '系统重启中请稍候...',
Snapshoting: '制作快照中请稍候...',
Recovering: '从快照恢复中请稍候...', Recovering: '从快照恢复中请稍候...',
Rollbacking: '快照回滚中请稍候...', Rollbacking: '快照回滚中请稍候...',
}, },
@ -996,6 +995,7 @@ const message = {
newPassword: '新密码', newPassword: '新密码',
retryPassword: '确认密码', retryPassword: '确认密码',
duplicatePassword: '新密码不能与原始密码一致请重新输入', duplicatePassword: '新密码不能与原始密码一致请重新输入',
diskClean: '磁盘清理',
thirdParty: '第三方账号', thirdParty: '第三方账号',
createBackupAccount: '添加 {0}', createBackupAccount: '添加 {0}',
@ -1144,6 +1144,53 @@ const message = {
doc2: '文档', doc2: '文档',
currentVersion: '当前运行版本', currentVersion: '当前运行版本',
}, },
clean: {
scan: '开始扫描',
clean: '立即清理',
reScan: '重新扫描',
cleanHelper: '已勾选文件及目录清理后无法回滚系统缓存文件清理需要重启服务是否继续',
statusSuggest: '( 建议清理 )',
statusClean: '( 很干净 )',
statusEmpty: '非常干净无需清理',
statusWarning: '( 谨慎操作 )',
lastCleanTime: '上次清理时间: {0}',
lastCleanHelper: '清理文件及目录{0} 总计清理{1}',
cleanSuccessful: '清理成功',
currentCleanHelper: '本次清理文件及目录{0} 总计清理{1}',
totalScan: '待清理垃圾文件共计 ',
selectScan: '已选中垃圾文件共计 ',
system: '系统垃圾',
systemHelper: '扫描系统垃圾具体包括快照升级等过程中产生的临时文件以及版本迭代过程中废弃的文件内容',
panelOriginal: '系统快照恢复前备份文件',
upgrade: '系统升级备份文件',
cache: '系统缓存文件',
snapshot: '系统快照临时文件',
snapshotTmp: '系统快照上传临时文件',
snapshotLocal: '系统快照制作临时文件',
unused: '系统废弃目录',
oldUpgrade: '废弃升级前备份目录',
oldOriginal: '废弃快照恢复前备份目录',
oldAppsBak: '废弃应用备份目录',
upload: '临时上传文件',
uploadHelper: '扫描临时上传目录具体包括系统上传备份列表中上传的临时文件',
download: '临时下载文件',
downloadHelper: '扫描临时下载目录具体包括系统从第三方备份账号下载的临时文件',
website: '网站',
app: '应用',
database: '数据库',
directory: '应用',
systemLog: '系统日志文件',
systemLogHelper:
'扫描系统垃圾具体包括系统日志信息容器构建或镜像拉取等日志信息以及计划任务中产生的日志文件',
dockerLog: '容器操作日志文件',
taskLog: '计划任务执行日志文件',
shell: 'Shell 脚本计划任务',
containerShell: '容器内执行 Shell 脚本计划任务',
curl: 'CURL 计划任务',
},
app: { app: {
app: '应用', app: '应用',
installName: '安装名称', installName: '安装名称',

@ -0,0 +1,534 @@
<template>
<div>
<el-drawer v-model="drawerVisible" :destroy-on-close="true" :close-on-click-modal="false" size="50%">
<template #header>
<DrawerHeader :header="$t('setting.diskClean')" :back="handleClose" />
</template>
<div v-loading="loading">
<div v-if="scanStatus !== 'scanned'">
<div v-if="scanStatus === 'beforeScan'">
<el-text class="clean_title" type="success">
{{ $t('clean.lastCleanTime', [form.lastCleanTime || '-']) }}
</el-text>
<div class="mt-4">
<el-text type="success">
{{
$t('clean.lastCleanHelper', [
form.lastCleanData || '-',
form.lastCleanSize ? computeSize(Number(form.lastCleanSize)) : '-',
])
}}
</el-text>
</div>
<div class="large_button">
<el-button type="primary" size="large" @click="scanData">{{ $t('clean.scan') }}</el-button>
</div>
</div>
<div v-if="scanStatus === 'afterScan'">
<el-text class="clean_title" type="success">{{ $t('clean.cleanSuccessful') }}</el-text>
<div class="mt-4">
<el-text type="success">
{{
$t('clean.currentCleanHelper', [
form.lastCleanData,
computeSize(Number(form.lastCleanSize)),
])
}}
</el-text>
</div>
<div class="large_button">
<el-button type="primary" size="large" @click="scanData">
{{ $t('clean.reScan') }}
</el-button>
</div>
</div>
<div class="app-card">
<el-card class="e-card">
<el-row>
<el-col :span="3">
<el-button icon="Setting" link class="card_icon" />
</el-col>
<el-col :span="21">
<div>
<el-text class="mx-1 card_title" type="primary">
{{ $t('clean.system') }}
</el-text>
</div>
<span>{{ $t('clean.systemHelper') }}</span>
</el-col>
</el-row>
</el-card>
<el-card class="e-card">
<el-row>
<el-col :span="3">
<el-button icon="Upload" link class="card_icon" />
</el-col>
<el-col :span="21">
<div>
<el-text class="mx-1 card_title" type="primary">
{{ $t('clean.upload') }}
</el-text>
</div>
<span>{{ $t('clean.uploadHelper') }}</span>
</el-col>
</el-row>
</el-card>
<el-card class="e-card">
<el-row>
<el-col :span="3">
<el-button icon="Download" link class="card_icon" />
</el-col>
<el-col :span="21">
<div>
<el-text class="mx-1 card_title" type="primary">
{{ $t('clean.download') }}
</el-text>
</div>
<span>{{ $t('clean.downloadHelper') }}</span>
</el-col>
</el-row>
</el-card>
<el-card class="e-card">
<el-row>
<el-col :span="3">
<el-button icon="Document" link class="card_icon" />
</el-col>
<el-col :span="21">
<div>
<el-text class="mx-1 card_title" type="primary">
{{ $t('clean.systemLog') }}
</el-text>
</div>
<span>
{{ $t('clean.systemLogHelper') }}
</span>
</el-col>
</el-row>
</el-card>
</div>
</div>
<div v-if="scanStatus === 'scanned'">
<div>
<el-text class="clean_title" type="primary">
{{ $t('clean.totalScan') }} {{ computeSize(totalSize) }}
</el-text>
<div class="mt-4">
<el-text type="info">{{ $t('clean.selectScan') }} {{ computeSize(selectSize) }}</el-text>
</div>
<div class="large_button">
<el-button type="primary" size="large" @click="onSubmitClean">
{{ $t('clean.clean') }}
</el-button>
</div>
</div>
<el-collapse class="mt-4" v-model="activeNames">
<el-collapse-item :title="$t('clean.system')" name="system">
<el-tree
ref="systemRef"
:data="cleanData.systemClean"
node-key="id"
:default-checked-keys="systemDefaultCheck"
show-checkbox
:props="defaultProps"
@check-change="onChange"
>
<template #default="{ node, data }">
<div class="float-left">
<span>{{ load18n(data.label) }}</span>
</div>
<div class="ml-4 float-left">
<span v-if="data.size">{{ computeSize(data.size) }}</span>
</div>
<div class="ml-4 float-left">
<span>{{ loadTag(node, data) }}</span>
</div>
</template>
</el-tree>
</el-collapse-item>
<el-collapse-item :title="$t('clean.upload')" name="upload">
<el-tree
ref="uploadRef"
:data="cleanData.uploadClean"
node-key="id"
:default-checked-keys="uploadDefaultCheck"
show-checkbox
:props="defaultProps"
@check-change="onChange"
>
<template #empty>
<el-empty :image-size="60" :description="$t('clean.statusEmpty')" />
</template>
<template #default="{ node, data }">
<div class="float-left">
<span>{{ load18n(data.label) }}</span>
</div>
<div class="ml-4 float-left">
<span v-if="data.size">{{ computeSize(data.size) }}</span>
</div>
<div class="ml-4 float-left">
<span>{{ loadTag(node, data) }}</span>
</div>
</template>
</el-tree>
</el-collapse-item>
<el-collapse-item :title="$t('clean.download')" name="download">
<el-tree
ref="downloadRef"
:data="cleanData.downloadClean"
node-key="id"
:default-checked-keys="downloadDefaultCheck"
show-checkbox
:props="defaultProps"
@check-change="onChange"
>
<template #empty>
<el-empty :image-size="60" :description="$t('clean.statusEmpty')" />
</template>
<template #default="{ node, data }">
<div class="float-left">
<span>{{ load18n(data.label) }}</span>
</div>
<div class="ml-4 float-left">
<span v-if="data.size">{{ computeSize(data.size) }}</span>
</div>
<div class="ml-4 float-left">
<span>{{ loadTag(node, data) }}</span>
</div>
</template>
</el-tree>
</el-collapse-item>
<el-collapse-item :title="$t('clean.systemLog')" name="system_log">
<el-tree
ref="systemLogRef"
:data="cleanData.systemLogClean"
node-key="id"
:default-checked-keys="systemLogDefaultCheck"
show-checkbox
:props="defaultProps"
@check-change="onChange"
>
<template #default="{ node, data }">
<div class="float-left">
<span>{{ load18n(data.label) }}</span>
</div>
<div class="ml-4 float-left">
<span v-if="data.size">{{ computeSize(data.size) }}</span>
</div>
<div class="ml-4 float-left">
<span>{{ loadTag(node, data) }}</span>
</div>
</template>
</el-tree>
</el-collapse-item>
</el-collapse>
</div>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="onCancel">{{ $t('commons.button.cancel') }}</el-button>
</span>
</template>
</el-drawer>
</div>
</template>
<script lang="ts" setup>
import { reactive, ref } from 'vue';
import { computeSize } from '@/utils/util';
import DrawerHeader from '@/components/drawer-header/index.vue';
import { cleanSystem, scanSystem } from '@/api/modules/setting';
import i18n from '@/lang';
import { MsgSuccess } from '@/utils/message';
const emit = defineEmits<{ (e: 'search'): void }>();
const drawerVisible = ref();
const loading = ref();
const totalSize = ref<number>(0);
const selectSize = ref<number>(0);
const scanStatus = ref<string>('beforeScan');
const defaultProps = {
children: 'children',
label: 'label',
};
const cleanData = reactive({
systemClean: [],
uploadClean: [],
downloadClean: [],
systemLogClean: [],
});
const systemRef = ref();
const systemDefaultCheck = ref([]);
const uploadRef = ref();
const uploadDefaultCheck = ref([]);
const downloadRef = ref();
const downloadDefaultCheck = ref([]);
const systemLogRef = ref();
const systemLogDefaultCheck = ref([]);
const activeNames = ref(['system', 'upload', 'download', 'system_log']);
const submitCleans = ref();
const form = reactive({
lastCleanTime: '',
lastCleanSize: '',
lastCleanData: '',
});
interface DialogProps {
lastCleanTime: string;
lastCleanSize: string;
lastCleanData: string;
}
const acceptParams = (params: DialogProps): void => {
scanStatus.value = 'beforeScan';
form.lastCleanTime = params.lastCleanTime;
console.log(form.lastCleanTime);
form.lastCleanSize = params.lastCleanSize;
form.lastCleanData = params.lastCleanData;
drawerVisible.value = true;
};
const scanData = async () => {
loading.value = true;
await scanSystem()
.then((res) => {
loading.value = false;
selectSize.value = 0;
totalSize.value = 0;
cleanData.systemClean = res.data.systemClean || [];
for (const item of cleanData.systemClean) {
totalSize.value += item.size;
}
cleanData.uploadClean = res.data.uploadClean || [];
for (const item of cleanData.uploadClean) {
totalSize.value += item.size;
}
cleanData.downloadClean = res.data.downloadClean || [];
for (const item of cleanData.downloadClean) {
totalSize.value += item.size;
}
cleanData.systemLogClean = res.data.systemLogClean || [];
for (const item of cleanData.systemLogClean) {
totalSize.value += item.size;
}
loadCheck(cleanData.systemClean, systemDefaultCheck.value);
loadCheck(cleanData.uploadClean, uploadDefaultCheck.value);
loadCheck(cleanData.downloadClean, downloadDefaultCheck.value);
loadCheck(cleanData.systemLogClean, systemLogDefaultCheck.value);
scanStatus.value = 'scanned';
})
.catch(() => {
loading.value = false;
});
};
const onSubmitClean = async () => {
ElMessageBox.confirm(i18n.global.t('clean.cleanHelper'), i18n.global.t('clean.clean'), {
confirmButtonText: i18n.global.t('commons.button.confirm'),
cancelButtonText: i18n.global.t('commons.button.cancel'),
type: 'info',
}).then(async () => {
loading.value = true;
submitCleans.value = [];
loadSubmitCheck(cleanData.systemClean);
loadSubmitCheck(cleanData.uploadClean);
loadSubmitCheck(cleanData.downloadClean);
loadSubmitCheck(cleanData.systemLogClean);
await cleanSystem(submitCleans.value)
.then(() => {
form.lastCleanSize = selectSize.value + '';
form.lastCleanData = submitCleans.value.length + '';
scanStatus.value = 'afterScan';
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
loading.value = false;
})
.catch(() => {
loading.value = false;
});
});
};
const onCancel = async () => {
drawerVisible.value = false;
emit('search');
};
const loadSubmitCheck = (data: any) => {
if (data.children === null) {
if (data.isCheck) {
submitCleans.value.push({ treeType: data.type, name: data.name, size: data.size });
}
return;
}
for (const item of data) {
if (item.isCheck) {
submitCleans.value.push({ treeType: item.type, name: item.name, size: item.size });
continue;
}
if (item.children) {
loadSubmitCheck(item.children);
}
}
};
const changeCheckStatus = (data: any, isCheck: boolean) => {
data.isCheck = isCheck;
if (data.children) {
for (const item of data.children) {
changeCheckStatus(item, isCheck);
}
}
};
function onChange(data: any, isCheck: boolean) {
changeCheckStatus(data, isCheck);
selectSize.value = 0;
let systemSelects = systemRef.value.getCheckedNodes(false, true);
for (const item of systemSelects) {
if (item.children === null) {
selectSize.value = selectSize.value + Number(item.size);
}
}
let uploadSelects = uploadRef.value.getCheckedNodes(false, true);
for (const item of uploadSelects) {
if (item.children === null) {
selectSize.value = selectSize.value + Number(item.size);
}
}
let downloadSelects = downloadRef.value.getCheckedNodes(false, true);
for (const item of downloadSelects) {
if (item.children === null) {
selectSize.value = selectSize.value + Number(item.size);
}
}
let systemLogSelects = systemLogRef.value.getCheckedNodes(false, true);
for (const item of systemLogSelects) {
if (item.children === null) {
selectSize.value = selectSize.value + Number(item.size);
}
}
}
function loadCheck(data: any, checkList: any) {
if (data.children === null) {
if (data.isCheck) {
checkList.push(data.id);
}
return;
}
for (const item of data) {
if (item.isCheck) {
selectSize.value = selectSize.value + Number(item.size);
checkList.push(item.id);
continue;
}
if (item.children) {
loadCheck(item.children, checkList);
}
}
}
function loadTag(node: any, data: any) {
if (node.level !== 1) {
return '';
}
if (data.size === 0) {
return i18n.global.t('clean.statusClean');
}
return data.isRecommend ? i18n.global.t('clean.statusSuggest') : i18n.global.t('clean.statusWarning');
}
function load18n(label: string) {
switch (label) {
case '1panel_original':
return i18n.global.t('clean.panelOriginal');
case 'upgrade':
return i18n.global.t('clean.upgrade');
case 'cache':
return i18n.global.t('clean.cache');
case 'snapshot':
return i18n.global.t('clean.snapshot');
case 'snapshot_tmp':
return i18n.global.t('clean.snapshotTmp');
case 'snapshot_local':
return i18n.global.t('clean.snapshotLocal');
case 'unused':
return i18n.global.t('clean.unused');
case 'old_original':
return i18n.global.t('clean.oldOriginal');
case 'old_apps_bak':
return i18n.global.t('clean.oldAppsBak');
case 'old_upgrade':
return i18n.global.t('clean.oldUpgrade');
case 'upload':
return i18n.global.t('clean.upload');
case 'download':
return i18n.global.t('clean.download');
case 'website':
return i18n.global.t('clean.website');
case 'app':
return i18n.global.t('clean.app');
case 'database':
return i18n.global.t('clean.database');
case 'directory':
return i18n.global.t('clean.directory');
case 'system_log':
return i18n.global.t('clean.systemLog');
case 'docker_log':
return i18n.global.t('clean.dockerLog');
case 'task_log':
return i18n.global.t('clean.taskLog');
case 'shell':
return i18n.global.t('clean.shell');
case 'containerShell':
return i18n.global.t('clean.containerShell');
case 'curl':
return i18n.global.t('clean.curl');
default:
return label;
}
}
const handleClose = () => {
emit('search');
drawerVisible.value = false;
};
defineExpose({
acceptParams,
});
</script>
<style lang="scss" scoped>
.app-card {
cursor: pointer;
width: 100%;
&:hover .app-icon {
transform: scale(1.2);
}
.e-card {
margin-top: 30px;
cursor: pointer;
border: var(--panel-border) !important;
&:hover {
cursor: pointer;
border: 1px solid var(--el-color-primary) !important;
}
}
}
.card_icon {
font-size: 36px;
float: right;
margin-right: 15px;
}
.card_title {
font-size: 18px;
}
.clean_title {
font-size: 22px;
}
.large_button {
float: right;
margin-top: -40px;
}
</style>

@ -117,6 +117,15 @@
</template> </template>
</el-input> </el-input>
</el-form-item> </el-form-item>
<el-form-item :label="$t('setting.diskClean')">
<el-input disabled v-model="form.lastCleanTime">
<template #append>
<el-button v-show="!show" @click="onClean" icon="Setting">
{{ $t('commons.button.set') }}
</el-button>
</template>
</el-input>
</el-form-item>
</el-col> </el-col>
</el-row> </el-row>
</el-form> </el-form>
@ -130,7 +139,8 @@
<Timeout ref="timeoutRef" @search="search()" /> <Timeout ref="timeoutRef" @search="search()" />
<TimeZone ref="timezoneRef" @search="search()" /> <TimeZone ref="timezoneRef" @search="search()" />
<Ntp ref="ntpRef" @search="search()" /> <Ntp ref="ntpRef" @search="search()" />
<Netwrok ref="netwrokRef" @search="search()" /> <Network ref="networkRef" @search="search()" />
<Clean ref="cleanRef" @search="search()" />
</div> </div>
</template> </template>
@ -148,7 +158,8 @@ import Timeout from '@/views/setting/panel/timeout/index.vue';
import PanelName from '@/views/setting/panel/name/index.vue'; import PanelName from '@/views/setting/panel/name/index.vue';
import SystemIP from '@/views/setting/panel/systemip/index.vue'; import SystemIP from '@/views/setting/panel/systemip/index.vue';
import TimeZone from '@/views/setting/panel/timezone/index.vue'; import TimeZone from '@/views/setting/panel/timezone/index.vue';
import Netwrok from '@/views/setting/panel/default-network/index.vue'; import Network from '@/views/setting/panel/default-network/index.vue';
import Clean from '@/views/setting/panel/clean/index.vue';
import Ntp from '@/views/setting/panel/ntp/index.vue'; import Ntp from '@/views/setting/panel/ntp/index.vue';
const loading = ref(false); const loading = ref(false);
@ -172,6 +183,10 @@ const form = reactive({
complexityVerification: '', complexityVerification: '',
defaultNetwork: '', defaultNetwork: '',
defaultNetworkVal: '', defaultNetworkVal: '',
lastCleanTime: '',
lastCleanSize: '',
lastCleanData: '',
}); });
const show = ref(); const show = ref();
@ -183,7 +198,8 @@ const systemIPRef = ref();
const timeoutRef = ref(); const timeoutRef = ref();
const ntpRef = ref(); const ntpRef = ref();
const timezoneRef = ref(); const timezoneRef = ref();
const netwrokRef = ref(); const networkRef = ref();
const cleanRef = ref();
const unset = ref(i18n.t('setting.unSetting')); const unset = ref(i18n.t('setting.unSetting'));
const search = async () => { const search = async () => {
@ -201,6 +217,10 @@ const search = async () => {
form.complexityVerification = res.data.complexityVerification; form.complexityVerification = res.data.complexityVerification;
form.defaultNetwork = res.data.defaultNetwork; form.defaultNetwork = res.data.defaultNetwork;
form.defaultNetworkVal = res.data.defaultNetwork === 'all' ? i18n.t('commons.table.all') : res.data.defaultNetwork; form.defaultNetworkVal = res.data.defaultNetwork === 'all' ? i18n.t('commons.table.all') : res.data.defaultNetwork;
form.lastCleanTime = res.data.lastCleanTime;
form.lastCleanSize = res.data.lastCleanSize;
form.lastCleanData = res.data.lastCleanData;
}; };
const onChangePassword = () => { const onChangePassword = () => {
@ -225,7 +245,14 @@ const onChangeNtp = () => {
ntpRef.value.acceptParams({ localTime: form.localTime, ntpSite: form.ntpSite }); ntpRef.value.acceptParams({ localTime: form.localTime, ntpSite: form.ntpSite });
}; };
const onChangeNetwork = () => { const onChangeNetwork = () => {
netwrokRef.value.acceptParams({ defaultNetwork: form.defaultNetwork }); networkRef.value.acceptParams({ defaultNetwork: form.defaultNetwork });
};
const onClean = () => {
cleanRef.value.acceptParams({
lastCleanTime: form.lastCleanTime,
lastCleanSize: form.lastCleanSize,
lastCleanData: form.lastCleanData,
});
}; };
const onSave = async (key: string, val: any) => { const onSave = async (key: string, val: any) => {