From e66ce1a9f271bc87ef3d75c13aeb4f712f6759df Mon Sep 17 00:00:00 2001 From: ssongliu Date: Thu, 1 Dec 2022 10:36:49 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E7=BB=9F=E4=B8=80=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E4=B8=8A=E4=BC=A0=E3=80=81=E5=88=A0=E9=99=A4=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/app/api/v1/database_mysql.go | 46 ---- backend/app/api/v1/database_redis.go | 18 -- backend/app/api/v1/file.go | 30 +++ backend/app/dto/database.go | 4 - backend/app/dto/file.go | 5 + backend/app/service/database_mysql.go | 72 ------- backend/app/service/file.go | 32 ++- backend/router/ro_database.go | 3 - backend/router/ro_file.go | 1 + frontend/src/api/interface/database.ts | 4 - frontend/src/api/interface/file.ts | 5 + frontend/src/api/modules/database.ts | 10 - frontend/src/api/modules/files.ts | 4 + .../src/views/database/mysql/upload/index.vue | 52 ++--- .../redis/setting/persistence/index.vue | 13 +- frontend/src/views/website/website/index.vue | 2 +- .../views/website/website/upload/index.vue | 198 ++++++++++++++++++ 17 files changed, 304 insertions(+), 195 deletions(-) create mode 100644 frontend/src/views/website/website/upload/index.vue diff --git a/backend/app/api/v1/database_mysql.go b/backend/app/api/v1/database_mysql.go index 96af36ecf..475849c45 100644 --- a/backend/app/api/v1/database_mysql.go +++ b/backend/app/api/v1/database_mysql.go @@ -1,8 +1,6 @@ package v1 import ( - "errors" - "github.com/1Panel-dev/1Panel/backend/app/api/v1/helper" "github.com/1Panel-dev/1Panel/backend/app/dto" "github.com/1Panel-dev/1Panel/backend/constant" @@ -44,50 +42,6 @@ func (b *BaseApi) UpdateMysql(c *gin.Context) { helper.SuccessWithData(c, nil) } -func (b *BaseApi) UploadMysqlFiles(c *gin.Context) { - form, err := c.MultipartForm() - if err != nil { - helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) - return - } - files := form.File["file"] - - mysqlName, ok := c.Params.Get("mysqlName") - if !ok { - helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, errors.New("error mysqlName in path")) - return - } - if err := mysqlService.UpFile(mysqlName, files); err != nil { - helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) - return - } - - helper.SuccessWithData(c, nil) -} - -func (b *BaseApi) MysqlUpList(c *gin.Context) { - var req dto.SearchDBWithPage - if err := c.ShouldBindJSON(&req); err != nil { - helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) - return - } - if err := global.VALID.Struct(req); err != nil { - helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) - return - } - - total, list, err := mysqlService.SearchUpListWithPage(req) - if err != nil { - helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) - return - } - - helper.SuccessWithData(c, dto.PageResult{ - Items: list, - Total: total, - }) -} - func (b *BaseApi) UpdateMysqlVariables(c *gin.Context) { var req []dto.MysqlVariablesUpdate if err := c.ShouldBindJSON(&req); err != nil { diff --git a/backend/app/api/v1/database_redis.go b/backend/app/api/v1/database_redis.go index bf0abf5e4..d43e10806 100644 --- a/backend/app/api/v1/database_redis.go +++ b/backend/app/api/v1/database_redis.go @@ -128,24 +128,6 @@ func (b *BaseApi) RedisBackupList(c *gin.Context) { }) } -func (b *BaseApi) RedisBackupDelete(c *gin.Context) { - var req dto.RedisBackupDelete - if err := c.ShouldBindJSON(&req); err != nil { - helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) - return - } - - for _, name := range req.Names { - fullPath := fmt.Sprintf("%s/%s", req.FileDir, name) - if err := os.Remove(fullPath); err != nil { - helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) - return - } - } - - helper.SuccessWithData(c, nil) -} - func (b *BaseApi) UpdateRedisConfByFile(c *gin.Context) { var req dto.RedisConfUpdateByFile if err := c.ShouldBindJSON(&req); err != nil { diff --git a/backend/app/api/v1/file.go b/backend/app/api/v1/file.go index da728b6fa..ee0769772 100644 --- a/backend/app/api/v1/file.go +++ b/backend/app/api/v1/file.go @@ -1,10 +1,13 @@ package v1 import ( + "errors" "fmt" "io/ioutil" "net/http" + "os" "path" + "strings" "github.com/1Panel-dev/1Panel/backend/app/api/v1/helper" "github.com/1Panel-dev/1Panel/backend/app/dto" @@ -71,6 +74,20 @@ func (b *BaseApi) DeleteFile(c *gin.Context) { helper.SuccessWithData(c, nil) } +func (b *BaseApi) BatchDeleteFile(c *gin.Context) { + var req dto.FileBatchDelete + if err := c.ShouldBindJSON(&req); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) + return + } + err := fileService.BatchDelete(req) + if err != nil { + helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) + return + } + helper.SuccessWithData(c, nil) +} + func (b *BaseApi) ChangeFileMode(c *gin.Context) { var req dto.FileCreate if err := c.ShouldBindJSON(&req); err != nil { @@ -148,6 +165,19 @@ func (b *BaseApi) UploadFiles(c *gin.Context) { } files := form.File["file"] paths := form.Value["path"] + if len(paths) == 0 || !strings.Contains(paths[0], "/") { + helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, errors.New("error paths in request")) + return + } + dir := paths[0][:strings.LastIndex(paths[0], "/")] + if _, err := os.Stat(dir); err != nil && os.IsNotExist(err) { + if err = os.MkdirAll(dir, os.ModePerm); err != nil { + if err != nil { + helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, fmt.Errorf("mkdir %s failed, err: %v", dir, err)) + return + } + } + } success := 0 for _, file := range files { err := c.SaveUploadedFile(file, path.Join(paths[0], file.Filename)) diff --git a/backend/app/dto/database.go b/backend/app/dto/database.go index c315e7f81..e24ec3ad3 100644 --- a/backend/app/dto/database.go +++ b/backend/app/dto/database.go @@ -190,7 +190,3 @@ type RedisBackupRecover struct { FileName string `json:"fileName"` FileDir string `json:"fileDir"` } -type RedisBackupDelete struct { - FileDir string `json:"fileDir"` - Names []string `json:"names"` -} diff --git a/backend/app/dto/file.go b/backend/app/dto/file.go index 24efcdc9a..baad3adc0 100644 --- a/backend/app/dto/file.go +++ b/backend/app/dto/file.go @@ -34,6 +34,11 @@ type FileDelete struct { IsDir bool } +type FileBatchDelete struct { + IsDir bool + Paths []string +} + type FileCompress struct { Files []string Dst string diff --git a/backend/app/service/database_mysql.go b/backend/app/service/database_mysql.go index 8abfc9a61..59ed6f0c2 100644 --- a/backend/app/service/database_mysql.go +++ b/backend/app/service/database_mysql.go @@ -6,9 +6,7 @@ import ( "context" "encoding/json" "fmt" - "io" "io/ioutil" - "mime/multipart" "os" "os/exec" "path/filepath" @@ -38,9 +36,7 @@ type IMysqlService interface { UpdateVariables(updatas []dto.MysqlVariablesUpdate) error UpdateConfByFile(info dto.MysqlConfUpdateByFile) error - UpFile(mysqlName string, files []*multipart.FileHeader) error RecoverByUpload(req dto.UploadRecover) error - SearchUpListWithPage(req dto.SearchDBWithPage) (int64, interface{}, error) Backup(db dto.BackupDB) error Recover(db dto.RecoverDB) error @@ -67,74 +63,6 @@ func (u *MysqlService) SearchWithPage(search dto.PageInfo) (int64, interface{}, return total, dtoMysqls, err } -func (u *MysqlService) SearchUpListWithPage(req dto.SearchDBWithPage) (int64, interface{}, error) { - var ( - list []dto.DatabaseFileRecords - backDatas []dto.DatabaseFileRecords - ) - localDir, err := loadLocalDir() - if err != nil { - return 0, list, nil - } - uploadDir := fmt.Sprintf("%s/database/mysql/%s/upload", localDir, req.MysqlName) - if _, err := os.Stat(uploadDir); err != nil { - return 0, list, nil - } - _ = filepath.Walk(uploadDir, func(path string, info os.FileInfo, err error) error { - if err != nil { - return nil - } - if !info.IsDir() { - list = append(list, dto.DatabaseFileRecords{ - CreatedAt: info.ModTime().Format("2006-01-02 15:04:05"), - Size: int(info.Size()), - FileDir: uploadDir, - FileName: info.Name(), - }) - } - return nil - }) - total, start, end := len(list), (req.Page-1)*req.PageSize, req.Page*req.PageSize - if start > total { - backDatas = make([]dto.DatabaseFileRecords, 0) - } else { - if end >= total { - end = total - } - backDatas = list[start:end] - } - return int64(total), backDatas, nil -} - -func (u *MysqlService) UpFile(mysqlName string, files []*multipart.FileHeader) error { - localDir, err := loadLocalDir() - if err != nil { - return err - } - dstDir := fmt.Sprintf("%s/database/mysql/%s/upload", localDir, mysqlName) - if _, err := os.Stat(dstDir); err != nil && os.IsNotExist(err) { - if err = os.MkdirAll(dstDir, os.ModePerm); err != nil { - if err != nil { - return fmt.Errorf("mkdir %s failed, err: %v", dstDir, err) - } - } - } - for _, file := range files { - src, err := file.Open() - if err != nil { - return err - } - defer src.Close() - out, err := os.Create(dstDir + "/" + file.Filename) - if err != nil { - return err - } - defer out.Close() - _, _ = io.Copy(out, src) - } - return nil -} - func (u *MysqlService) RecoverByUpload(req dto.UploadRecover) error { app, err := appInstallRepo.LoadBaseInfoByKey("mysql") if err != nil { diff --git a/backend/app/service/file.go b/backend/app/service/file.go index b1ecefd95..344f3d48e 100644 --- a/backend/app/service/file.go +++ b/backend/app/service/file.go @@ -2,17 +2,18 @@ package service import ( "fmt" + "io/fs" + "os" + "path/filepath" + "strings" + "time" + "github.com/1Panel-dev/1Panel/backend/app/dto" "github.com/1Panel-dev/1Panel/backend/global" "github.com/1Panel-dev/1Panel/backend/utils/common" "github.com/1Panel-dev/1Panel/backend/utils/files" "github.com/pkg/errors" uuid "github.com/satori/go.uuid" - "io/fs" - "os" - "path/filepath" - "strings" - "time" ) type FileService struct { @@ -20,6 +21,9 @@ type FileService struct { func (f FileService) GetFileList(op dto.FileOption) (dto.FileInfo, error) { var fileInfo dto.FileInfo + if _, err := os.Stat(op.Path); err != nil && os.IsNotExist(err) { + return fileInfo, nil + } info, err := files.NewFileInfo(op.FileOption) if err != nil { return fileInfo, err @@ -77,6 +81,24 @@ func (f FileService) Delete(op dto.FileDelete) error { } } +func (f FileService) BatchDelete(op dto.FileBatchDelete) error { + fo := files.NewFileOp() + if op.IsDir { + for _, file := range op.Paths { + if err := fo.DeleteDir(file); err != nil { + return err + } + } + } else { + for _, file := range op.Paths { + if err := fo.DeleteFile(file); err != nil { + return err + } + } + } + return nil +} + func (f FileService) ChangeMode(op dto.FileCreate) error { fo := files.NewFileOp() return fo.Chmod(op.Path, fs.FileMode(op.Mode)) diff --git a/backend/router/ro_database.go b/backend/router/ro_database.go index 506bd2395..078562adc 100644 --- a/backend/router/ro_database.go +++ b/backend/router/ro_database.go @@ -24,8 +24,6 @@ func (s *DatabaseRouter) InitDatabaseRouter(Router *gin.RouterGroup) { withRecordRouter.POST("", baseApi.CreateMysql) withRecordRouter.PUT("/:id", baseApi.UpdateMysql) withRecordRouter.POST("/backup", baseApi.BackupMysql) - cmdRouter.POST("/uplist", baseApi.MysqlUpList) - cmdRouter.POST("/uplist/upload/:mysqlName", baseApi.UploadMysqlFiles) withRecordRouter.POST("/recover/byupload", baseApi.RecoverMysqlByUpload) withRecordRouter.POST("/recover", baseApi.RecoverMysql) withRecordRouter.POST("/del", baseApi.DeleteMysql) @@ -44,7 +42,6 @@ func (s *DatabaseRouter) InitDatabaseRouter(Router *gin.RouterGroup) { cmdRouter.POST("/redis/backup", baseApi.RedisBackup) cmdRouter.POST("/redis/recover", baseApi.RedisRecover) cmdRouter.POST("/redis/backup/records", baseApi.RedisBackupList) - cmdRouter.POST("/redis/backup/del", baseApi.RedisBackupDelete) cmdRouter.POST("/redis/conf/update", baseApi.UpdateRedisConf) cmdRouter.POST("/redis/conf/update/byfile", baseApi.UpdateRedisConfByFile) cmdRouter.POST("/redis/conf/update/persistence", baseApi.UpdateRedisPersistenceConf) diff --git a/backend/router/ro_file.go b/backend/router/ro_file.go index 92961fa73..8dbc12ecb 100644 --- a/backend/router/ro_file.go +++ b/backend/router/ro_file.go @@ -19,6 +19,7 @@ func (f *FileRouter) InitFileRouter(Router *gin.RouterGroup) { fileRouter.POST("/tree", baseApi.GetFileTree) fileRouter.POST("", baseApi.CreateFile) fileRouter.POST("/del", baseApi.DeleteFile) + fileRouter.POST("/batch/del", baseApi.BatchDeleteFile) fileRouter.POST("/mode", baseApi.ChangeFileMode) fileRouter.POST("/compress", baseApi.CompressFile) fileRouter.POST("/decompress", baseApi.DeCompressFile) diff --git a/frontend/src/api/interface/database.ts b/frontend/src/api/interface/database.ts index ab07ee9e3..22f809401 100644 --- a/frontend/src/api/interface/database.ts +++ b/frontend/src/api/interface/database.ts @@ -169,10 +169,6 @@ export namespace Database { createdAt: string; size: string; } - export interface FileRecordDelete { - fileDir: string; - names: Array; - } export interface RedisRecover { fileName: string; fileDir: string; diff --git a/frontend/src/api/interface/file.ts b/frontend/src/api/interface/file.ts index 1d4fd6486..4fb4f2f1a 100644 --- a/frontend/src/api/interface/file.ts +++ b/frontend/src/api/interface/file.ts @@ -51,6 +51,11 @@ export namespace File { isDir: boolean; } + export interface FileBatchDelete { + isDir: boolean; + paths: Array; + } + export interface FileCompress { files: string[]; type: string; diff --git a/frontend/src/api/modules/database.ts b/frontend/src/api/modules/database.ts index fab012758..c2a5d0371 100644 --- a/frontend/src/api/modules/database.ts +++ b/frontend/src/api/modules/database.ts @@ -1,18 +1,11 @@ import http from '@/api'; import { ReqPage, ResPage } from '../interface'; import { Database } from '../interface/database'; -import { File } from '@/api/interface/file'; export const searchMysqlDBs = (params: ReqPage) => { return http.post>(`/databases/search`, params); }; -export const searchUpList = (params: ReqPage) => { - return http.post>(`/databases/uplist`, params); -}; -export const uploadFile = (mysqlName: string, params: FormData) => { - return http.upload(`/databases/uplist/upload/${mysqlName}`, params); -}; export const backup = (params: Database.Backup) => { return http.post(`/databases/backup`, params); }; @@ -80,6 +73,3 @@ export const recoverRedis = (param: Database.RedisRecover) => { export const redisBackupRedisRecords = (param: ReqPage) => { return http.post>(`/databases/redis/backup/records`, param); }; -export const deleteDatabaseFile = (param: Database.FileRecordDelete) => { - return http.post(`/databases/redis/backup/del`, param); -}; diff --git a/frontend/src/api/modules/files.ts b/frontend/src/api/modules/files.ts index 1bf6df210..43babce3e 100644 --- a/frontend/src/api/modules/files.ts +++ b/frontend/src/api/modules/files.ts @@ -18,6 +18,10 @@ export const DeleteFile = (form: File.FileDelete) => { return http.post('files/del', form); }; +export const BatchDeleteFile = (form: File.FileBatchDelete) => { + return http.post('files/batch/del', form); +}; + export const ChangeFileMode = (form: File.FileCreate) => { return http.post('files/mode', form); }; diff --git a/frontend/src/views/database/mysql/upload/index.vue b/frontend/src/views/database/mysql/upload/index.vue index f0403fde4..f376f43bb 100644 --- a/frontend/src/views/database/mysql/upload/index.vue +++ b/frontend/src/views/database/mysql/upload/index.vue @@ -40,14 +40,17 @@ - - + - + + + import ComplexTable from '@/components/complex-table/index.vue'; import { reactive, ref } from 'vue'; -import { computeSize } from '@/utils/util'; +import { computeSize, dateFromat } from '@/utils/util'; import { useDeleteData } from '@/hooks/use-delete-data'; -import { deleteDatabaseFile, recoverByUpload, searchUpList, uploadFile } from '@/api/modules/database'; +import { recoverByUpload } from '@/api/modules/database'; import i18n from '@/lang'; import { ElMessage, UploadFile, UploadFiles, UploadInstance, UploadProps } from 'element-plus'; -import { Database } from '@/api/interface/database'; +import { File } from '@/api/interface/file'; +import { BatchDeleteFile, GetFilesList, UploadFileData } from '@/api/modules/files'; const selects = ref([]); +const baseDir = '/opt/1Panel/data/uploads/database/'; const data = ref(); const paginationConfig = reactive({ @@ -97,19 +102,20 @@ const search = async () => { let params = { page: paginationConfig.currentPage, pageSize: paginationConfig.pageSize, - mysqlName: mysqlName.value, + path: baseDir, + expand: true, }; - const res = await searchUpList(params); + const res = await GetFilesList(params); data.value = res.data.items || []; - paginationConfig.total = res.data.total; + paginationConfig.total = res.data.itemTotal; }; -const onRecover = async (row: Database.FileRecord) => { +const onRecover = async (row: File.File) => { let params = { mysqlName: mysqlName.value, dbName: dbName.value, - fileDir: row.fileDir, - fileName: row.fileName, + fileDir: baseDir, + fileName: row.name, }; await recoverByUpload(params); ElMessage.success(i18n.global.t('commons.msg.operationSuccess')); @@ -150,39 +156,37 @@ const onSubmit = () => { if (uploaderFiles.value[0]!.raw != undefined) { formData.append('file', uploaderFiles.value[0]!.raw); } - uploadFile(mysqlName.value, formData).then(() => { + formData.append('path', baseDir); + UploadFileData(formData, {}).then(() => { ElMessage.success(i18n.global.t('file.uploadSuccess')); handleClose(); search(); }); }; -const onBatchDelete = async (row: Database.FileRecord | null) => { - let names: Array = []; - let fileDir: string = ''; +const onBatchDelete = async (row: File.File | null) => { + let files: Array = []; if (row) { - fileDir = row.fileDir; - names.push(row.fileName); + files.push(baseDir + row.name); } else { - selects.value.forEach((item: Database.FileRecord) => { - fileDir = item.fileDir; - names.push(item.fileName); + selects.value.forEach((item: File.File) => { + files.push(baseDir + item.name); }); } - await useDeleteData(deleteDatabaseFile, { fileDir: fileDir, names: names }, 'commons.msg.delete', true); + await useDeleteData(BatchDeleteFile, { paths: files, isDir: false }, 'commons.msg.delete', true); search(); }; const buttons = [ { label: i18n.global.t('commons.button.recover'), - click: (row: Database.FileRecord) => { + click: (row: File.File) => { onRecover(row); }, }, { label: i18n.global.t('commons.button.delete'), - click: (row: Database.FileRecord) => { + click: (row: File.File) => { onBatchDelete(row); }, }, diff --git a/frontend/src/views/database/redis/setting/persistence/index.vue b/frontend/src/views/database/redis/setting/persistence/index.vue index 4d237d0a1..8d79a7612 100644 --- a/frontend/src/views/database/redis/setting/persistence/index.vue +++ b/frontend/src/views/database/redis/setting/persistence/index.vue @@ -124,7 +124,6 @@ import ConfirmDialog from '@/components/confirm-dialog/index.vue'; import { Database } from '@/api/interface/database'; import { backupRedis, - deleteDatabaseFile, recoverRedis, redisBackupRedisRecords, RedisPersistenceConf, @@ -136,6 +135,7 @@ import { ElMessage, FormInstance } from 'element-plus'; import { reactive, ref } from 'vue'; import { useDeleteData } from '@/hooks/use-delete-data'; import { computeSize } from '@/utils/util'; +import { BatchDeleteFile } from '@/api/modules/files'; interface saveStruct { second: number; @@ -207,18 +207,15 @@ const onRecover = async () => { }; const onBatchDelete = async (row: Database.FileRecord | null) => { - let names: Array = []; - let fileDir: string = ''; + let files: Array = []; if (row) { - fileDir = row.fileDir; - names.push(row.fileName); + files.push(row.fileDir + '/' + row.fileName); } else { selects.value.forEach((item: Database.FileRecord) => { - fileDir = item.fileDir; - names.push(item.fileName); + files.push(item.fileDir + '/' + item.fileName); }); } - await useDeleteData(deleteDatabaseFile, { fileDir: fileDir, names: names }, 'commons.msg.delete', true); + await useDeleteData(BatchDeleteFile, { isDir: false, paths: files }, 'commons.msg.delete', true); loadBackupRecords(); }; const buttons = [ diff --git a/frontend/src/views/website/website/index.vue b/frontend/src/views/website/website/index.vue index 40e2f8630..0279e815c 100644 --- a/frontend/src/views/website/website/index.vue +++ b/frontend/src/views/website/website/index.vue @@ -63,7 +63,7 @@