mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-01-31 14:08:06 +08:00
feat: 文件增加收藏夹功能 (#2612)
Refs https://github.com/1Panel-dev/1Panel/issues/492
This commit is contained in:
parent
8715cfc343
commit
b38abf852d
@ -55,4 +55,5 @@ var (
|
|||||||
hostToolService = service.NewIHostToolService()
|
hostToolService = service.NewIHostToolService()
|
||||||
|
|
||||||
recycleBinService = service.NewIRecycleBinService()
|
recycleBinService = service.NewIRecycleBinService()
|
||||||
|
favoriteService = service.NewIFavoriteService()
|
||||||
)
|
)
|
||||||
|
76
backend/app/api/v1/favorite.go
Normal file
76
backend/app/api/v1/favorite.go
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
package v1
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// @Tags File
|
||||||
|
// @Summary List favorites
|
||||||
|
// @Description 获取收藏列表
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body dto.PageInfo true "request"
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Router /files/favorite/search [post]
|
||||||
|
func (b *BaseApi) SearchFavorite(c *gin.Context) {
|
||||||
|
var req dto.PageInfo
|
||||||
|
if err := helper.CheckBind(req, c); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
total, list, err := favoriteService.Page(req)
|
||||||
|
if err != nil {
|
||||||
|
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.SuccessWithData(c, dto.PageResult{
|
||||||
|
Total: total,
|
||||||
|
Items: list,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Tags File
|
||||||
|
// @Summary Create favorite
|
||||||
|
// @Description 创建收藏
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body request.FavoriteCreate true "request"
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Router /files/favorite [post]
|
||||||
|
// @x-panel-log {"bodyKeys":["path"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"收藏文件/文件夹 [path]","formatEN":"收藏文件/文件夹 [path]"}
|
||||||
|
func (b *BaseApi) CreateFavorite(c *gin.Context) {
|
||||||
|
var req request.FavoriteCreate
|
||||||
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
favorite, err := favoriteService.Create(req)
|
||||||
|
if err != nil {
|
||||||
|
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.SuccessWithData(c, favorite)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Tags File
|
||||||
|
// @Summary Delete favorite
|
||||||
|
// @Description 删除收藏
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body request.FavoriteDelete true "request"
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Router /files/favorite/del [post]
|
||||||
|
// @x-panel-log {"bodyKeys":["id"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"id","isList":false,"db":"favorites","output_column":"path","output_value":"path"}],"formatZH":"删除收藏 [path]","formatEN":"delete avorite [path]"}
|
||||||
|
func (b *BaseApi) DeleteFavorite(c *gin.Context) {
|
||||||
|
var req request.FavoriteDelete
|
||||||
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := favoriteService.Delete(req.ID); err != nil {
|
||||||
|
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.SuccessWithOutData(c)
|
||||||
|
}
|
@ -115,7 +115,7 @@ func GetTxAndContext() (tx *gorm.DB, ctx context.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func CheckBindAndValidate(req interface{}, c *gin.Context) error {
|
func CheckBindAndValidate(req interface{}, c *gin.Context) error {
|
||||||
if err := c.ShouldBindJSON(&req); err != nil {
|
if err := c.ShouldBindJSON(req); err != nil {
|
||||||
ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -125,3 +125,11 @@ func CheckBindAndValidate(req interface{}, c *gin.Context) error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func CheckBind(req interface{}, c *gin.Context) error {
|
||||||
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
|
ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -8,14 +8,14 @@ import (
|
|||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
// @Tags RecycleBin
|
// @Tags File
|
||||||
// @Summary List RecycleBin files
|
// @Summary List RecycleBin files
|
||||||
// @Description 获取回收站文件列表
|
// @Description 获取回收站文件列表
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Param request body dto.PageInfo true "request"
|
// @Param request body dto.PageInfo true "request"
|
||||||
// @Success 200
|
// @Success 200
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Router /recycle/search [post]
|
// @Router /files/recycle/search [post]
|
||||||
func (b *BaseApi) SearchRecycleBinFile(c *gin.Context) {
|
func (b *BaseApi) SearchRecycleBinFile(c *gin.Context) {
|
||||||
var req dto.PageInfo
|
var req dto.PageInfo
|
||||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
@ -32,15 +32,15 @@ func (b *BaseApi) SearchRecycleBinFile(c *gin.Context) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// @Tags RecycleBin
|
// @Tags File
|
||||||
// @Summary Reduce RecycleBin files
|
// @Summary Reduce RecycleBin files
|
||||||
// @Description 还原回收站文件
|
// @Description 还原回收站文件
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Param request body request.RecycleBinReduce true "request"
|
// @Param request body request.RecycleBinReduce true "request"
|
||||||
// @Success 200
|
// @Success 200
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Router /recycle/reduce [post]
|
// @Router /files/recycle/reduce [post]
|
||||||
// @x-panel-log {"bodyKeys":["name],"paramKeys":[],"BeforeFunctions":[],"formatZH":"还原回收站文件 [name]","formatEN":"Reduce RecycleBin file [name]"}
|
// @x-panel-log {"bodyKeys":["name"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"还原回收站文件 [name]","formatEN":"Reduce RecycleBin file [name]"}
|
||||||
func (b *BaseApi) ReduceRecycleBinFile(c *gin.Context) {
|
func (b *BaseApi) ReduceRecycleBinFile(c *gin.Context) {
|
||||||
var req request.RecycleBinReduce
|
var req request.RecycleBinReduce
|
||||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
@ -53,13 +53,13 @@ func (b *BaseApi) ReduceRecycleBinFile(c *gin.Context) {
|
|||||||
helper.SuccessWithOutData(c)
|
helper.SuccessWithOutData(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
// @Tags RecycleBin
|
// @Tags File
|
||||||
// @Summary Clear RecycleBin files
|
// @Summary Clear RecycleBin files
|
||||||
// @Description 清空回收站文件
|
// @Description 清空回收站文件
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Success 200
|
// @Success 200
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Router /recycle/clear [post]
|
// @Router /files/recycle/clear [post]
|
||||||
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"清空回收站","formatEN":"清空回收站"}
|
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"清空回收站","formatEN":"清空回收站"}
|
||||||
func (b *BaseApi) ClearRecycleBinFile(c *gin.Context) {
|
func (b *BaseApi) ClearRecycleBinFile(c *gin.Context) {
|
||||||
if err := recycleBinService.Clear(); err != nil {
|
if err := recycleBinService.Clear(); err != nil {
|
||||||
|
9
backend/app/dto/request/favorite.go
Normal file
9
backend/app/dto/request/favorite.go
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
package request
|
||||||
|
|
||||||
|
type FavoriteCreate struct {
|
||||||
|
Path string `json:"path" validate:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type FavoriteDelete struct {
|
||||||
|
ID uint `json:"id" validate:"required"`
|
||||||
|
}
|
7
backend/app/dto/response/favorite.go
Normal file
7
backend/app/dto/response/favorite.go
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package response
|
||||||
|
|
||||||
|
import "github.com/1Panel-dev/1Panel/backend/app/model"
|
||||||
|
|
||||||
|
type FavoriteDTO struct {
|
||||||
|
model.Favorite
|
||||||
|
}
|
10
backend/app/model/favorite.go
Normal file
10
backend/app/model/favorite.go
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
type Favorite struct {
|
||||||
|
BaseModel
|
||||||
|
Name string `gorm:"type:varchar(256);not null;" json:"name" `
|
||||||
|
Path string `gorm:"type:varchar(256);not null;unique" json:"path"`
|
||||||
|
Type string `gorm:"type:varchar(64);" json:"type"`
|
||||||
|
IsDir bool `json:"isDir"`
|
||||||
|
IsTxt bool `json:"isTxt"`
|
||||||
|
}
|
66
backend/app/repo/favorite.go
Normal file
66
backend/app/repo/favorite.go
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
package repo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/global"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type FavoriteRepo struct{}
|
||||||
|
|
||||||
|
type IFavoriteRepo interface {
|
||||||
|
Page(page, size int, opts ...DBOption) (int64, []model.Favorite, error)
|
||||||
|
Create(group *model.Favorite) error
|
||||||
|
Delete(opts ...DBOption) error
|
||||||
|
GetFirst(opts ...DBOption) (model.Favorite, error)
|
||||||
|
All() ([]model.Favorite, error)
|
||||||
|
WithByPath(path string) DBOption
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewIFavoriteRepo() IFavoriteRepo {
|
||||||
|
return &FavoriteRepo{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FavoriteRepo) WithByPath(path string) DBOption {
|
||||||
|
return func(db *gorm.DB) *gorm.DB {
|
||||||
|
return db.Where("path = ?", path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FavoriteRepo) Page(page, size int, opts ...DBOption) (int64, []model.Favorite, error) {
|
||||||
|
var (
|
||||||
|
favorites []model.Favorite
|
||||||
|
count int64
|
||||||
|
)
|
||||||
|
count = int64(0)
|
||||||
|
db := getDb(opts...).Model(&model.Favorite{})
|
||||||
|
db = db.Count(&count)
|
||||||
|
err := db.Limit(size).Offset(size * (page - 1)).Find(&favorites).Error
|
||||||
|
return count, favorites, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FavoriteRepo) Create(favorite *model.Favorite) error {
|
||||||
|
return global.DB.Create(favorite).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FavoriteRepo) GetFirst(opts ...DBOption) (model.Favorite, error) {
|
||||||
|
var favorite model.Favorite
|
||||||
|
db := getDb(opts...).Model(&model.Favorite{})
|
||||||
|
if err := db.First(&favorite).Error; err != nil {
|
||||||
|
return favorite, err
|
||||||
|
}
|
||||||
|
return favorite, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FavoriteRepo) Delete(opts ...DBOption) error {
|
||||||
|
db := getDb(opts...).Model(&model.Favorite{})
|
||||||
|
return db.Delete(&model.Favorite{}).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FavoriteRepo) All() ([]model.Favorite, error) {
|
||||||
|
var favorites []model.Favorite
|
||||||
|
if err := getDb().Find(&favorites).Error; err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return favorites, nil
|
||||||
|
}
|
@ -37,4 +37,6 @@ var (
|
|||||||
snapshotRepo = repo.NewISnapshotRepo()
|
snapshotRepo = repo.NewISnapshotRepo()
|
||||||
|
|
||||||
runtimeRepo = repo.NewIRunTimeRepo()
|
runtimeRepo = repo.NewIRunTimeRepo()
|
||||||
|
|
||||||
|
favoriteRepo = repo.NewIFavoriteRepo()
|
||||||
)
|
)
|
||||||
|
83
backend/app/service/favorite.go
Normal file
83
backend/app/service/favorite.go
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/app/dto/response"
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/buserr"
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/utils/files"
|
||||||
|
"github.com/spf13/afero"
|
||||||
|
)
|
||||||
|
|
||||||
|
type FavoriteService struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
type IFavoriteService interface {
|
||||||
|
Page(req dto.PageInfo) (int64, []response.FavoriteDTO, error)
|
||||||
|
Create(req request.FavoriteCreate) (*model.Favorite, error)
|
||||||
|
Delete(id uint) error
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewIFavoriteService() IFavoriteService {
|
||||||
|
return &FavoriteService{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FavoriteService) Page(req dto.PageInfo) (int64, []response.FavoriteDTO, error) {
|
||||||
|
total, favorites, err := favoriteRepo.Page(req.Page, req.PageSize)
|
||||||
|
if err != nil {
|
||||||
|
return 0, nil, err
|
||||||
|
}
|
||||||
|
var dtoFavorites []response.FavoriteDTO
|
||||||
|
for _, favorite := range favorites {
|
||||||
|
dtoFavorites = append(dtoFavorites, response.FavoriteDTO{
|
||||||
|
Favorite: favorite,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return total, dtoFavorites, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FavoriteService) Create(req request.FavoriteCreate) (*model.Favorite, error) {
|
||||||
|
exist, _ := favoriteRepo.GetFirst(favoriteRepo.WithByPath(req.Path))
|
||||||
|
if exist.ID > 0 {
|
||||||
|
return nil, buserr.New(constant.ErrFavoriteExist)
|
||||||
|
}
|
||||||
|
op := files.NewFileOp()
|
||||||
|
if !op.Stat(req.Path) {
|
||||||
|
return nil, buserr.New(constant.ErrLinkPathNotFound)
|
||||||
|
}
|
||||||
|
openFile, err := op.OpenFile(req.Path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
fileInfo, err := openFile.Stat()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
favorite := &model.Favorite{
|
||||||
|
Name: fileInfo.Name(),
|
||||||
|
IsDir: fileInfo.IsDir(),
|
||||||
|
Path: req.Path,
|
||||||
|
}
|
||||||
|
if fileInfo.Size() <= 10*1024*1024 {
|
||||||
|
afs := &afero.Afero{Fs: op.Fs}
|
||||||
|
cByte, err := afs.ReadFile(req.Path)
|
||||||
|
if err == nil {
|
||||||
|
if len(cByte) > 0 && !files.DetectBinary(cByte) {
|
||||||
|
favorite.IsTxt = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := favoriteRepo.Create(favorite); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return favorite, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FavoriteService) Delete(id uint) error {
|
||||||
|
if err := favoriteRepo.Delete(commonRepo.WithByID(id)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
@ -90,6 +90,7 @@ var (
|
|||||||
ErrFileUpload = "ErrFileUpload"
|
ErrFileUpload = "ErrFileUpload"
|
||||||
ErrFileDownloadDir = "ErrFileDownloadDir"
|
ErrFileDownloadDir = "ErrFileDownloadDir"
|
||||||
ErrCmdNotFound = "ErrCmdNotFound"
|
ErrCmdNotFound = "ErrCmdNotFound"
|
||||||
|
ErrFavoriteExist = "ErrFavoriteExist"
|
||||||
)
|
)
|
||||||
|
|
||||||
// mysql
|
// mysql
|
||||||
|
@ -64,6 +64,7 @@ ErrFileUpload: "Failed to upload file {{.name}} {{.detail}}"
|
|||||||
ErrFileDownloadDir: "Download folder not supported"
|
ErrFileDownloadDir: "Download folder not supported"
|
||||||
ErrCmdNotFound: "{{ .name}} command does not exist, please install this command on the host first"
|
ErrCmdNotFound: "{{ .name}} command does not exist, please install this command on the host first"
|
||||||
ErrSourcePathNotFound: "Source directory does not exist"
|
ErrSourcePathNotFound: "Source directory does not exist"
|
||||||
|
ErrFavoriteExist: "This path has been collected"
|
||||||
|
|
||||||
#website
|
#website
|
||||||
ErrDomainIsExist: "Domain is already exist"
|
ErrDomainIsExist: "Domain is already exist"
|
||||||
|
@ -64,6 +64,7 @@ ErrFileUpload: "{{ .name }} 上傳文件失敗 {{ .detail}}"
|
|||||||
ErrFileDownloadDir: "不支持下載文件夾"
|
ErrFileDownloadDir: "不支持下載文件夾"
|
||||||
ErrCmdNotFound: "{{ .name}} 命令不存在,請先在宿主機安裝此命令"
|
ErrCmdNotFound: "{{ .name}} 命令不存在,請先在宿主機安裝此命令"
|
||||||
ErrSourcePathNotFound: "源目錄不存在"
|
ErrSourcePathNotFound: "源目錄不存在"
|
||||||
|
ErrFavoriteExist: "已收藏此路徑"
|
||||||
|
|
||||||
#website
|
#website
|
||||||
ErrDomainIsExist: "域名已存在"
|
ErrDomainIsExist: "域名已存在"
|
||||||
|
@ -64,6 +64,7 @@ ErrFileUpload: "{{ .name }} 上传文件失败 {{ .detail}}"
|
|||||||
ErrFileDownloadDir: "不支持下载文件夹"
|
ErrFileDownloadDir: "不支持下载文件夹"
|
||||||
ErrCmdNotFound: "{{ .name}} 命令不存在,请先在宿主机安装此命令"
|
ErrCmdNotFound: "{{ .name}} 命令不存在,请先在宿主机安装此命令"
|
||||||
ErrSourcePathNotFound: "源目录不存在"
|
ErrSourcePathNotFound: "源目录不存在"
|
||||||
|
ErrFavoriteExist: "已收藏此路径"
|
||||||
|
|
||||||
#website
|
#website
|
||||||
ErrDomainIsExist: "域名已存在"
|
ErrDomainIsExist: "域名已存在"
|
||||||
|
@ -47,6 +47,7 @@ func Init() {
|
|||||||
migrations.AddDefaultNetwork,
|
migrations.AddDefaultNetwork,
|
||||||
migrations.UpdateRuntime,
|
migrations.UpdateRuntime,
|
||||||
migrations.UpdateTag,
|
migrations.UpdateTag,
|
||||||
|
migrations.AddFavorite,
|
||||||
})
|
})
|
||||||
if err := m.Migrate(); err != nil {
|
if err := m.Migrate(); err != nil {
|
||||||
global.LOG.Error(err)
|
global.LOG.Error(err)
|
||||||
|
17
backend/init/migration/migrations/v_1_8.go
Normal file
17
backend/init/migration/migrations/v_1_8.go
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package migrations
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||||
|
"github.com/go-gormigrate/gormigrate/v2"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
var AddFavorite = &gormigrate.Migration{
|
||||||
|
ID: "20231020-add-favorite",
|
||||||
|
Migrate: func(tx *gorm.DB) error {
|
||||||
|
if err := tx.AutoMigrate(&model.Favorite{}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
@ -41,5 +41,10 @@ func (f *FileRouter) InitFileRouter(Router *gin.RouterGroup) {
|
|||||||
fileRouter.POST("/recycle/search", baseApi.SearchRecycleBinFile)
|
fileRouter.POST("/recycle/search", baseApi.SearchRecycleBinFile)
|
||||||
fileRouter.POST("/recycle/reduce", baseApi.ReduceRecycleBinFile)
|
fileRouter.POST("/recycle/reduce", baseApi.ReduceRecycleBinFile)
|
||||||
fileRouter.POST("/recycle/clear", baseApi.ClearRecycleBinFile)
|
fileRouter.POST("/recycle/clear", baseApi.ClearRecycleBinFile)
|
||||||
|
|
||||||
|
fileRouter.POST("/favorite/search", baseApi.SearchFavorite)
|
||||||
|
fileRouter.POST("/favorite", baseApi.CreateFavorite)
|
||||||
|
fileRouter.POST("/favorite/del", baseApi.DeleteFavorite)
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package files
|
|||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/app/repo"
|
||||||
"github.com/1Panel-dev/1Panel/backend/buserr"
|
"github.com/1Panel-dev/1Panel/backend/buserr"
|
||||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
@ -42,6 +43,7 @@ type FileInfo struct {
|
|||||||
FileMode os.FileMode `json:"-"`
|
FileMode os.FileMode `json:"-"`
|
||||||
Items []*FileInfo `json:"items"`
|
Items []*FileInfo `json:"items"`
|
||||||
ItemTotal int `json:"itemTotal"`
|
ItemTotal int `json:"itemTotal"`
|
||||||
|
FavoriteID uint `json:"favoriteID"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type FileOption struct {
|
type FileOption struct {
|
||||||
@ -88,6 +90,12 @@ func NewFileInfo(op FileOption) (*FileInfo, error) {
|
|||||||
Group: GetGroup(info.Sys().(*syscall.Stat_t).Gid),
|
Group: GetGroup(info.Sys().(*syscall.Stat_t).Gid),
|
||||||
MimeType: GetMimeType(op.Path),
|
MimeType: GetMimeType(op.Path),
|
||||||
}
|
}
|
||||||
|
favoriteRepo := repo.NewIFavoriteRepo()
|
||||||
|
favorite, _ := favoriteRepo.GetFirst(favoriteRepo.WithByPath(op.Path))
|
||||||
|
if favorite.ID > 0 {
|
||||||
|
file.FavoriteID = favorite.ID
|
||||||
|
}
|
||||||
|
|
||||||
if file.IsSymlink {
|
if file.IsSymlink {
|
||||||
file.LinkPath = GetSymlink(op.Path)
|
file.LinkPath = GetSymlink(op.Path)
|
||||||
}
|
}
|
||||||
@ -266,7 +274,11 @@ func (f *FileInfo) listChildren(option FileOption) error {
|
|||||||
Uid: strconv.FormatUint(uint64(df.Sys().(*syscall.Stat_t).Uid), 10),
|
Uid: strconv.FormatUint(uint64(df.Sys().(*syscall.Stat_t).Uid), 10),
|
||||||
Gid: strconv.FormatUint(uint64(df.Sys().(*syscall.Stat_t).Gid), 10),
|
Gid: strconv.FormatUint(uint64(df.Sys().(*syscall.Stat_t).Gid), 10),
|
||||||
}
|
}
|
||||||
|
favoriteRepo := repo.NewIFavoriteRepo()
|
||||||
|
favorite, _ := favoriteRepo.GetFirst(favoriteRepo.WithByPath(fPath))
|
||||||
|
if favorite.ID > 0 {
|
||||||
|
file.FavoriteID = favorite.ID
|
||||||
|
}
|
||||||
if isSymlink {
|
if isSymlink {
|
||||||
file.LinkPath = GetSymlink(fPath)
|
file.LinkPath = GetSymlink(fPath)
|
||||||
}
|
}
|
||||||
@ -305,7 +317,7 @@ func (f *FileInfo) getContent() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if len(cByte) > 0 && detectBinary(cByte) {
|
if len(cByte) > 0 && DetectBinary(cByte) {
|
||||||
return buserr.New(constant.ErrFileCanNotRead)
|
return buserr.New(constant.ErrFileCanNotRead)
|
||||||
}
|
}
|
||||||
f.Content = string(cByte)
|
f.Content = string(cByte)
|
||||||
@ -315,7 +327,7 @@ func (f *FileInfo) getContent() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func detectBinary(buf []byte) bool {
|
func DetectBinary(buf []byte) bool {
|
||||||
whiteByte := 0
|
whiteByte := 0
|
||||||
n := min(1024, len(buf))
|
n := min(1024, len(buf))
|
||||||
for i := 0; i < n; i++ {
|
for i := 0; i < n; i++ {
|
||||||
|
@ -5368,6 +5368,132 @@ const docTemplate = `{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/files/favorite": {
|
||||||
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"ApiKeyAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "创建收藏",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"File"
|
||||||
|
],
|
||||||
|
"summary": "Create favorite",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "request",
|
||||||
|
"name": "request",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/request.FavoriteCreate"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-panel-log": {
|
||||||
|
"BeforeFunctions": [],
|
||||||
|
"bodyKeys": [
|
||||||
|
"path"
|
||||||
|
],
|
||||||
|
"formatEN": "收藏文件/文件夹 [path]",
|
||||||
|
"formatZH": "收藏文件/文件夹 [path]",
|
||||||
|
"paramKeys": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/files/favorite/del": {
|
||||||
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"ApiKeyAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "删除收藏",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"File"
|
||||||
|
],
|
||||||
|
"summary": "Delete favorite",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "request",
|
||||||
|
"name": "request",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/request.FavoriteDelete"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-panel-log": {
|
||||||
|
"BeforeFunctions": [
|
||||||
|
{
|
||||||
|
"db": "favorites",
|
||||||
|
"input_column": "id",
|
||||||
|
"input_value": "id",
|
||||||
|
"isList": false,
|
||||||
|
"output_column": "path",
|
||||||
|
"output_value": "path"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"bodyKeys": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"formatEN": "delete avorite [path]",
|
||||||
|
"formatZH": "删除收藏 [path]",
|
||||||
|
"paramKeys": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/files/favorite/search": {
|
||||||
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"ApiKeyAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "获取收藏列表",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"File"
|
||||||
|
],
|
||||||
|
"summary": "List favorites",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "request",
|
||||||
|
"name": "request",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/dto.PageInfo"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/files/mode": {
|
"/files/mode": {
|
||||||
"post": {
|
"post": {
|
||||||
"security": [
|
"security": [
|
||||||
@ -5498,6 +5624,110 @@ const docTemplate = `{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/files/recycle/clear": {
|
||||||
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"ApiKeyAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "清空回收站文件",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"File"
|
||||||
|
],
|
||||||
|
"summary": "Clear RecycleBin files",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-panel-log": {
|
||||||
|
"BeforeFunctions": [],
|
||||||
|
"bodyKeys": [],
|
||||||
|
"formatEN": "清空回收站",
|
||||||
|
"formatZH": "清空回收站",
|
||||||
|
"paramKeys": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/files/recycle/reduce": {
|
||||||
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"ApiKeyAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "还原回收站文件",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"File"
|
||||||
|
],
|
||||||
|
"summary": "Reduce RecycleBin files",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "request",
|
||||||
|
"name": "request",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/request.RecycleBinReduce"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-panel-log": {
|
||||||
|
"BeforeFunctions": [],
|
||||||
|
"bodyKeys": [
|
||||||
|
"name"
|
||||||
|
],
|
||||||
|
"formatEN": "Reduce RecycleBin file [name]",
|
||||||
|
"formatZH": "还原回收站文件 [name]",
|
||||||
|
"paramKeys": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/files/recycle/search": {
|
||||||
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"ApiKeyAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "获取回收站文件列表",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"File"
|
||||||
|
],
|
||||||
|
"summary": "List RecycleBin files",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "request",
|
||||||
|
"name": "request",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/dto.PageInfo"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/files/rename": {
|
"/files/rename": {
|
||||||
"post": {
|
"post": {
|
||||||
"security": [
|
"security": [
|
||||||
@ -7826,94 +8056,6 @@ const docTemplate = `{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/recycle/clear": {
|
|
||||||
"post": {
|
|
||||||
"security": [
|
|
||||||
{
|
|
||||||
"ApiKeyAuth": []
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"description": "清空回收站文件",
|
|
||||||
"consumes": [
|
|
||||||
"application/json"
|
|
||||||
],
|
|
||||||
"tags": [
|
|
||||||
"RecycleBin"
|
|
||||||
],
|
|
||||||
"summary": "Clear RecycleBin files",
|
|
||||||
"responses": {
|
|
||||||
"200": {
|
|
||||||
"description": "OK"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/recycle/reduce": {
|
|
||||||
"post": {
|
|
||||||
"security": [
|
|
||||||
{
|
|
||||||
"ApiKeyAuth": []
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"description": "还原回收站文件",
|
|
||||||
"consumes": [
|
|
||||||
"application/json"
|
|
||||||
],
|
|
||||||
"tags": [
|
|
||||||
"RecycleBin"
|
|
||||||
],
|
|
||||||
"summary": "Reduce RecycleBin files",
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"description": "request",
|
|
||||||
"name": "request",
|
|
||||||
"in": "body",
|
|
||||||
"required": true,
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/definitions/request.RecycleBinReduce"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"responses": {
|
|
||||||
"200": {
|
|
||||||
"description": "OK"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/recycle/search": {
|
|
||||||
"post": {
|
|
||||||
"security": [
|
|
||||||
{
|
|
||||||
"ApiKeyAuth": []
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"description": "获取回收站文件列表",
|
|
||||||
"consumes": [
|
|
||||||
"application/json"
|
|
||||||
],
|
|
||||||
"tags": [
|
|
||||||
"RecycleBin"
|
|
||||||
],
|
|
||||||
"summary": "List RecycleBin files",
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"description": "request",
|
|
||||||
"name": "request",
|
|
||||||
"in": "body",
|
|
||||||
"required": true,
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/definitions/dto.PageInfo"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"responses": {
|
|
||||||
"200": {
|
|
||||||
"description": "OK"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/runtimes": {
|
"/runtimes": {
|
||||||
"post": {
|
"post": {
|
||||||
"security": [
|
"security": [
|
||||||
@ -15563,6 +15705,9 @@ const docTemplate = `{
|
|||||||
"extension": {
|
"extension": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"favoriteID": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
"gid": {
|
"gid": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@ -16212,6 +16357,28 @@ const docTemplate = `{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"request.FavoriteCreate": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"path"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"path": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"request.FavoriteDelete": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"request.FileBatchDelete": {
|
"request.FileBatchDelete": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [
|
"required": [
|
||||||
@ -16938,6 +17105,9 @@ const docTemplate = `{
|
|||||||
"from": {
|
"from": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"rName": {
|
"rName": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
}
|
}
|
||||||
@ -18062,6 +18232,9 @@ const docTemplate = `{
|
|||||||
"extension": {
|
"extension": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"favoriteID": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
"gid": {
|
"gid": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
@ -5361,6 +5361,132 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/files/favorite": {
|
||||||
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"ApiKeyAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "创建收藏",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"File"
|
||||||
|
],
|
||||||
|
"summary": "Create favorite",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "request",
|
||||||
|
"name": "request",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/request.FavoriteCreate"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-panel-log": {
|
||||||
|
"BeforeFunctions": [],
|
||||||
|
"bodyKeys": [
|
||||||
|
"path"
|
||||||
|
],
|
||||||
|
"formatEN": "收藏文件/文件夹 [path]",
|
||||||
|
"formatZH": "收藏文件/文件夹 [path]",
|
||||||
|
"paramKeys": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/files/favorite/del": {
|
||||||
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"ApiKeyAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "删除收藏",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"File"
|
||||||
|
],
|
||||||
|
"summary": "Delete favorite",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "request",
|
||||||
|
"name": "request",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/request.FavoriteDelete"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-panel-log": {
|
||||||
|
"BeforeFunctions": [
|
||||||
|
{
|
||||||
|
"db": "favorites",
|
||||||
|
"input_column": "id",
|
||||||
|
"input_value": "id",
|
||||||
|
"isList": false,
|
||||||
|
"output_column": "path",
|
||||||
|
"output_value": "path"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"bodyKeys": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"formatEN": "delete avorite [path]",
|
||||||
|
"formatZH": "删除收藏 [path]",
|
||||||
|
"paramKeys": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/files/favorite/search": {
|
||||||
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"ApiKeyAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "获取收藏列表",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"File"
|
||||||
|
],
|
||||||
|
"summary": "List favorites",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "request",
|
||||||
|
"name": "request",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/dto.PageInfo"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/files/mode": {
|
"/files/mode": {
|
||||||
"post": {
|
"post": {
|
||||||
"security": [
|
"security": [
|
||||||
@ -5491,6 +5617,110 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/files/recycle/clear": {
|
||||||
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"ApiKeyAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "清空回收站文件",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"File"
|
||||||
|
],
|
||||||
|
"summary": "Clear RecycleBin files",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-panel-log": {
|
||||||
|
"BeforeFunctions": [],
|
||||||
|
"bodyKeys": [],
|
||||||
|
"formatEN": "清空回收站",
|
||||||
|
"formatZH": "清空回收站",
|
||||||
|
"paramKeys": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/files/recycle/reduce": {
|
||||||
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"ApiKeyAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "还原回收站文件",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"File"
|
||||||
|
],
|
||||||
|
"summary": "Reduce RecycleBin files",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "request",
|
||||||
|
"name": "request",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/request.RecycleBinReduce"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-panel-log": {
|
||||||
|
"BeforeFunctions": [],
|
||||||
|
"bodyKeys": [
|
||||||
|
"name"
|
||||||
|
],
|
||||||
|
"formatEN": "Reduce RecycleBin file [name]",
|
||||||
|
"formatZH": "还原回收站文件 [name]",
|
||||||
|
"paramKeys": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/files/recycle/search": {
|
||||||
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"ApiKeyAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "获取回收站文件列表",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"File"
|
||||||
|
],
|
||||||
|
"summary": "List RecycleBin files",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"description": "request",
|
||||||
|
"name": "request",
|
||||||
|
"in": "body",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/dto.PageInfo"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/files/rename": {
|
"/files/rename": {
|
||||||
"post": {
|
"post": {
|
||||||
"security": [
|
"security": [
|
||||||
@ -7819,94 +8049,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/recycle/clear": {
|
|
||||||
"post": {
|
|
||||||
"security": [
|
|
||||||
{
|
|
||||||
"ApiKeyAuth": []
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"description": "清空回收站文件",
|
|
||||||
"consumes": [
|
|
||||||
"application/json"
|
|
||||||
],
|
|
||||||
"tags": [
|
|
||||||
"RecycleBin"
|
|
||||||
],
|
|
||||||
"summary": "Clear RecycleBin files",
|
|
||||||
"responses": {
|
|
||||||
"200": {
|
|
||||||
"description": "OK"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/recycle/reduce": {
|
|
||||||
"post": {
|
|
||||||
"security": [
|
|
||||||
{
|
|
||||||
"ApiKeyAuth": []
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"description": "还原回收站文件",
|
|
||||||
"consumes": [
|
|
||||||
"application/json"
|
|
||||||
],
|
|
||||||
"tags": [
|
|
||||||
"RecycleBin"
|
|
||||||
],
|
|
||||||
"summary": "Reduce RecycleBin files",
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"description": "request",
|
|
||||||
"name": "request",
|
|
||||||
"in": "body",
|
|
||||||
"required": true,
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/definitions/request.RecycleBinReduce"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"responses": {
|
|
||||||
"200": {
|
|
||||||
"description": "OK"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/recycle/search": {
|
|
||||||
"post": {
|
|
||||||
"security": [
|
|
||||||
{
|
|
||||||
"ApiKeyAuth": []
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"description": "获取回收站文件列表",
|
|
||||||
"consumes": [
|
|
||||||
"application/json"
|
|
||||||
],
|
|
||||||
"tags": [
|
|
||||||
"RecycleBin"
|
|
||||||
],
|
|
||||||
"summary": "List RecycleBin files",
|
|
||||||
"parameters": [
|
|
||||||
{
|
|
||||||
"description": "request",
|
|
||||||
"name": "request",
|
|
||||||
"in": "body",
|
|
||||||
"required": true,
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/definitions/dto.PageInfo"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"responses": {
|
|
||||||
"200": {
|
|
||||||
"description": "OK"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/runtimes": {
|
"/runtimes": {
|
||||||
"post": {
|
"post": {
|
||||||
"security": [
|
"security": [
|
||||||
@ -15556,6 +15698,9 @@
|
|||||||
"extension": {
|
"extension": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"favoriteID": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
"gid": {
|
"gid": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@ -16205,6 +16350,28 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"request.FavoriteCreate": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"path"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"path": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"request.FavoriteDelete": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"request.FileBatchDelete": {
|
"request.FileBatchDelete": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [
|
"required": [
|
||||||
@ -16931,6 +17098,9 @@
|
|||||||
"from": {
|
"from": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"rName": {
|
"rName": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
}
|
}
|
||||||
@ -18055,6 +18225,9 @@
|
|||||||
"extension": {
|
"extension": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"favoriteID": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
"gid": {
|
"gid": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
@ -2280,6 +2280,8 @@ definitions:
|
|||||||
type: string
|
type: string
|
||||||
extension:
|
extension:
|
||||||
type: string
|
type: string
|
||||||
|
favoriteID:
|
||||||
|
type: integer
|
||||||
gid:
|
gid:
|
||||||
type: string
|
type: string
|
||||||
group:
|
group:
|
||||||
@ -2710,6 +2712,20 @@ definitions:
|
|||||||
required:
|
required:
|
||||||
- path
|
- path
|
||||||
type: object
|
type: object
|
||||||
|
request.FavoriteCreate:
|
||||||
|
properties:
|
||||||
|
path:
|
||||||
|
type: string
|
||||||
|
required:
|
||||||
|
- path
|
||||||
|
type: object
|
||||||
|
request.FavoriteDelete:
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
type: integer
|
||||||
|
required:
|
||||||
|
- id
|
||||||
|
type: object
|
||||||
request.FileBatchDelete:
|
request.FileBatchDelete:
|
||||||
properties:
|
properties:
|
||||||
isDir:
|
isDir:
|
||||||
@ -3196,6 +3212,8 @@ definitions:
|
|||||||
properties:
|
properties:
|
||||||
from:
|
from:
|
||||||
type: string
|
type: string
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
rName:
|
rName:
|
||||||
type: string
|
type: string
|
||||||
required:
|
required:
|
||||||
@ -3955,6 +3973,8 @@ definitions:
|
|||||||
type: string
|
type: string
|
||||||
extension:
|
extension:
|
||||||
type: string
|
type: string
|
||||||
|
favoriteID:
|
||||||
|
type: integer
|
||||||
gid:
|
gid:
|
||||||
type: string
|
type: string
|
||||||
group:
|
group:
|
||||||
@ -7604,6 +7624,86 @@ paths:
|
|||||||
formatEN: Download file [name]
|
formatEN: Download file [name]
|
||||||
formatZH: 下载文件 [name]
|
formatZH: 下载文件 [name]
|
||||||
paramKeys: []
|
paramKeys: []
|
||||||
|
/files/favorite:
|
||||||
|
post:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: 创建收藏
|
||||||
|
parameters:
|
||||||
|
- description: request
|
||||||
|
in: body
|
||||||
|
name: request
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/request.FavoriteCreate'
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
security:
|
||||||
|
- ApiKeyAuth: []
|
||||||
|
summary: Create favorite
|
||||||
|
tags:
|
||||||
|
- File
|
||||||
|
x-panel-log:
|
||||||
|
BeforeFunctions: []
|
||||||
|
bodyKeys:
|
||||||
|
- path
|
||||||
|
formatEN: 收藏文件/文件夹 [path]
|
||||||
|
formatZH: 收藏文件/文件夹 [path]
|
||||||
|
paramKeys: []
|
||||||
|
/files/favorite/del:
|
||||||
|
post:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: 删除收藏
|
||||||
|
parameters:
|
||||||
|
- description: request
|
||||||
|
in: body
|
||||||
|
name: request
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/request.FavoriteDelete'
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
security:
|
||||||
|
- ApiKeyAuth: []
|
||||||
|
summary: Delete favorite
|
||||||
|
tags:
|
||||||
|
- File
|
||||||
|
x-panel-log:
|
||||||
|
BeforeFunctions:
|
||||||
|
- db: favorites
|
||||||
|
input_column: id
|
||||||
|
input_value: id
|
||||||
|
isList: false
|
||||||
|
output_column: path
|
||||||
|
output_value: path
|
||||||
|
bodyKeys:
|
||||||
|
- id
|
||||||
|
formatEN: delete avorite [path]
|
||||||
|
formatZH: 删除收藏 [path]
|
||||||
|
paramKeys: []
|
||||||
|
/files/favorite/search:
|
||||||
|
post:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: 获取收藏列表
|
||||||
|
parameters:
|
||||||
|
- description: request
|
||||||
|
in: body
|
||||||
|
name: request
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/dto.PageInfo'
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
security:
|
||||||
|
- ApiKeyAuth: []
|
||||||
|
summary: List favorites
|
||||||
|
tags:
|
||||||
|
- File
|
||||||
/files/mode:
|
/files/mode:
|
||||||
post:
|
post:
|
||||||
consumes:
|
consumes:
|
||||||
@ -7689,6 +7789,72 @@ paths:
|
|||||||
formatEN: Change owner [paths] => [user]/[group]
|
formatEN: Change owner [paths] => [user]/[group]
|
||||||
formatZH: 修改用户/组 [paths] => [user]/[group]
|
formatZH: 修改用户/组 [paths] => [user]/[group]
|
||||||
paramKeys: []
|
paramKeys: []
|
||||||
|
/files/recycle/clear:
|
||||||
|
post:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: 清空回收站文件
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
security:
|
||||||
|
- ApiKeyAuth: []
|
||||||
|
summary: Clear RecycleBin files
|
||||||
|
tags:
|
||||||
|
- File
|
||||||
|
x-panel-log:
|
||||||
|
BeforeFunctions: []
|
||||||
|
bodyKeys: []
|
||||||
|
formatEN: 清空回收站
|
||||||
|
formatZH: 清空回收站
|
||||||
|
paramKeys: []
|
||||||
|
/files/recycle/reduce:
|
||||||
|
post:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: 还原回收站文件
|
||||||
|
parameters:
|
||||||
|
- description: request
|
||||||
|
in: body
|
||||||
|
name: request
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/request.RecycleBinReduce'
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
security:
|
||||||
|
- ApiKeyAuth: []
|
||||||
|
summary: Reduce RecycleBin files
|
||||||
|
tags:
|
||||||
|
- File
|
||||||
|
x-panel-log:
|
||||||
|
BeforeFunctions: []
|
||||||
|
bodyKeys:
|
||||||
|
- name
|
||||||
|
formatEN: Reduce RecycleBin file [name]
|
||||||
|
formatZH: 还原回收站文件 [name]
|
||||||
|
paramKeys: []
|
||||||
|
/files/recycle/search:
|
||||||
|
post:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: 获取回收站文件列表
|
||||||
|
parameters:
|
||||||
|
- description: request
|
||||||
|
in: body
|
||||||
|
name: request
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/dto.PageInfo'
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
security:
|
||||||
|
- ApiKeyAuth: []
|
||||||
|
summary: List RecycleBin files
|
||||||
|
tags:
|
||||||
|
- File
|
||||||
/files/rename:
|
/files/rename:
|
||||||
post:
|
post:
|
||||||
consumes:
|
consumes:
|
||||||
@ -9159,59 +9325,6 @@ paths:
|
|||||||
formatEN: 结束进程 [PID]
|
formatEN: 结束进程 [PID]
|
||||||
formatZH: 结束进程 [PID]
|
formatZH: 结束进程 [PID]
|
||||||
paramKeys: []
|
paramKeys: []
|
||||||
/recycle/clear:
|
|
||||||
post:
|
|
||||||
consumes:
|
|
||||||
- application/json
|
|
||||||
description: 清空回收站文件
|
|
||||||
responses:
|
|
||||||
"200":
|
|
||||||
description: OK
|
|
||||||
security:
|
|
||||||
- ApiKeyAuth: []
|
|
||||||
summary: Clear RecycleBin files
|
|
||||||
tags:
|
|
||||||
- RecycleBin
|
|
||||||
/recycle/reduce:
|
|
||||||
post:
|
|
||||||
consumes:
|
|
||||||
- application/json
|
|
||||||
description: 还原回收站文件
|
|
||||||
parameters:
|
|
||||||
- description: request
|
|
||||||
in: body
|
|
||||||
name: request
|
|
||||||
required: true
|
|
||||||
schema:
|
|
||||||
$ref: '#/definitions/request.RecycleBinReduce'
|
|
||||||
responses:
|
|
||||||
"200":
|
|
||||||
description: OK
|
|
||||||
security:
|
|
||||||
- ApiKeyAuth: []
|
|
||||||
summary: Reduce RecycleBin files
|
|
||||||
tags:
|
|
||||||
- RecycleBin
|
|
||||||
/recycle/search:
|
|
||||||
post:
|
|
||||||
consumes:
|
|
||||||
- application/json
|
|
||||||
description: 获取回收站文件列表
|
|
||||||
parameters:
|
|
||||||
- description: request
|
|
||||||
in: body
|
|
||||||
name: request
|
|
||||||
required: true
|
|
||||||
schema:
|
|
||||||
$ref: '#/definitions/dto.PageInfo'
|
|
||||||
responses:
|
|
||||||
"200":
|
|
||||||
description: OK
|
|
||||||
security:
|
|
||||||
- ApiKeyAuth: []
|
|
||||||
summary: List RecycleBin files
|
|
||||||
tags:
|
|
||||||
- RecycleBin
|
|
||||||
/runtimes:
|
/runtimes:
|
||||||
post:
|
post:
|
||||||
consumes:
|
consumes:
|
||||||
|
@ -21,6 +21,7 @@ export namespace File {
|
|||||||
items: File[];
|
items: File[];
|
||||||
extension: string;
|
extension: string;
|
||||||
itemTotal: number;
|
itemTotal: number;
|
||||||
|
favoriteID: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ReqFile extends ReqPage {
|
export interface ReqFile extends ReqPage {
|
||||||
@ -162,4 +163,10 @@ export namespace File {
|
|||||||
from: string;
|
from: string;
|
||||||
name: string;
|
name: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface Favorite extends CommonModel {
|
||||||
|
path: string;
|
||||||
|
isDir: boolean;
|
||||||
|
isTxt: boolean;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -100,3 +100,15 @@ export const reduceFile = (params: File.RecycleBinReduce) => {
|
|||||||
export const clearRecycle = () => {
|
export const clearRecycle = () => {
|
||||||
return http.post<any>('files/recycle/clear');
|
return http.post<any>('files/recycle/clear');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const SearchFavorite = (params: ReqPage) => {
|
||||||
|
return http.post<ResPage<File.Favorite>>('files/favorite/search', params);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const AddFavorite = (path: string) => {
|
||||||
|
return http.post<any>('files/favorite', { path: path });
|
||||||
|
};
|
||||||
|
|
||||||
|
export const RemoveFavorite = (id: number) => {
|
||||||
|
return http.post<any>('files/favorite/del', { id: id });
|
||||||
|
};
|
||||||
|
@ -122,6 +122,7 @@ const message = {
|
|||||||
'You can upload only files whose name contains 1 to 256 characters, including English, Chinese, digits, or periods (.-_)',
|
'You can upload only files whose name contains 1 to 256 characters, including English, Chinese, digits, or periods (.-_)',
|
||||||
confirmNoNull: 'Make sure the value {0} is not empty',
|
confirmNoNull: 'Make sure the value {0} is not empty',
|
||||||
errPort: 'Incorrect port information, please confirm!',
|
errPort: 'Incorrect port information, please confirm!',
|
||||||
|
remove: 'Remove',
|
||||||
},
|
},
|
||||||
login: {
|
login: {
|
||||||
username: 'UserName',
|
username: 'UserName',
|
||||||
@ -970,7 +971,10 @@ const message = {
|
|||||||
reduceHelper:
|
reduceHelper:
|
||||||
'Restore the file to its original path. If a file or directory with the same name exists at the original address of the file, it will be overwritten. Do you want to continue?',
|
'Restore the file to its original path. If a file or directory with the same name exists at the original address of the file, it will be overwritten. Do you want to continue?',
|
||||||
clearRecycleBin: 'Clear the recycle bin',
|
clearRecycleBin: 'Clear the recycle bin',
|
||||||
clearRecycleBinHelper: 'Do you want to clear the recycle bin? ',
|
clearRecycleBinHelper: 'Do you want to clear the recycle bin?',
|
||||||
|
favorite: 'favorites',
|
||||||
|
removeFavorite: 'Remove from favorites?',
|
||||||
|
addFavorite: 'Add to favorites',
|
||||||
},
|
},
|
||||||
ssh: {
|
ssh: {
|
||||||
autoStart: 'Auto Start',
|
autoStart: 'Auto Start',
|
||||||
|
@ -123,6 +123,7 @@ const message = {
|
|||||||
fileNameErr: '僅支持上傳名稱包含英文、中文、數字或者 .-_ ,長度 1-256 位的文件',
|
fileNameErr: '僅支持上傳名稱包含英文、中文、數字或者 .-_ ,長度 1-256 位的文件',
|
||||||
confirmNoNull: '請確認 {0} 值不為空',
|
confirmNoNull: '請確認 {0} 值不為空',
|
||||||
errPort: '錯誤的端口信息,請確認!',
|
errPort: '錯誤的端口信息,請確認!',
|
||||||
|
remove: '移出',
|
||||||
},
|
},
|
||||||
login: {
|
login: {
|
||||||
username: '用戶名',
|
username: '用戶名',
|
||||||
@ -931,7 +932,10 @@ const message = {
|
|||||||
reduce: '還原',
|
reduce: '還原',
|
||||||
reduceHelper: '恢復檔案到原路徑,如果檔案原始位址,存在同名檔案或目錄,將會覆蓋,是否繼續? ',
|
reduceHelper: '恢復檔案到原路徑,如果檔案原始位址,存在同名檔案或目錄,將會覆蓋,是否繼續? ',
|
||||||
clearRecycleBin: '清空回收站',
|
clearRecycleBin: '清空回收站',
|
||||||
clearRecycleBinHelper: '是否清空回收站? ',
|
clearRecycleBinHelper: '是否清空回收站?',
|
||||||
|
favorite: '收藏夾',
|
||||||
|
removeFavorite: '是否從收藏夾移出?',
|
||||||
|
addFavorite: '加入收藏夾',
|
||||||
},
|
},
|
||||||
ssh: {
|
ssh: {
|
||||||
autoStart: '開機自啟',
|
autoStart: '開機自啟',
|
||||||
|
@ -123,6 +123,7 @@ const message = {
|
|||||||
fileNameErr: '仅支持上传名称包含英文、中文、数字或者 .-_ ,长度 1-256 位的文件',
|
fileNameErr: '仅支持上传名称包含英文、中文、数字或者 .-_ ,长度 1-256 位的文件',
|
||||||
confirmNoNull: '请确认 {0} 值不为空',
|
confirmNoNull: '请确认 {0} 值不为空',
|
||||||
errPort: '错误的端口信息,请确认!',
|
errPort: '错误的端口信息,请确认!',
|
||||||
|
remove: '移出',
|
||||||
},
|
},
|
||||||
login: {
|
login: {
|
||||||
username: '用户名',
|
username: '用户名',
|
||||||
@ -933,6 +934,9 @@ const message = {
|
|||||||
reduceHelper: '恢复文件到原路径,如果文件原地址,存在同名文件或目录,将会覆盖,是否继续?',
|
reduceHelper: '恢复文件到原路径,如果文件原地址,存在同名文件或目录,将会覆盖,是否继续?',
|
||||||
clearRecycleBin: '清空回收站',
|
clearRecycleBin: '清空回收站',
|
||||||
clearRecycleBinHelper: '是否清空回收站?',
|
clearRecycleBinHelper: '是否清空回收站?',
|
||||||
|
favorite: '收藏夹',
|
||||||
|
removeFavorite: '是否从收藏夹移出?',
|
||||||
|
addFavorite: '加入收藏夹子',
|
||||||
},
|
},
|
||||||
ssh: {
|
ssh: {
|
||||||
autoStart: '开机自启',
|
autoStart: '开机自启',
|
||||||
|
72
frontend/src/views/host/file-management/favorite/index.vue
Normal file
72
frontend/src/views/host/file-management/favorite/index.vue
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
<template>
|
||||||
|
<el-drawer v-model="open" :before-close="handleClose" :close-on-click-modal="false" size="50%">
|
||||||
|
<template #header>
|
||||||
|
<DrawerHeader :header="$t('file.favorite')" :back="handleClose" />
|
||||||
|
</template>
|
||||||
|
<ComplexTable :pagination-config="paginationConfig" :data="data" @search="search">
|
||||||
|
<el-table-column :label="$t('file.path')" show-overflow-tooltip prop="path"></el-table-column>
|
||||||
|
<fu-table-operations :buttons="buttons" :label="$t('commons.table.operate')" fix />
|
||||||
|
</ComplexTable>
|
||||||
|
</el-drawer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { SearchFavorite, RemoveFavorite } from '@/api/modules/files';
|
||||||
|
import i18n from '@/lang';
|
||||||
|
import { reactive, ref } from 'vue';
|
||||||
|
|
||||||
|
const paginationConfig = reactive({
|
||||||
|
cacheSizeKey: 'favorite-page-size',
|
||||||
|
currentPage: 1,
|
||||||
|
pageSize: 100,
|
||||||
|
total: 0,
|
||||||
|
});
|
||||||
|
const req = reactive({
|
||||||
|
page: 1,
|
||||||
|
pageSize: 100,
|
||||||
|
});
|
||||||
|
const open = ref(false);
|
||||||
|
const data = ref([]);
|
||||||
|
const em = defineEmits(['close']);
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
open.value = false;
|
||||||
|
em('close', false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const acceptParams = () => {
|
||||||
|
search();
|
||||||
|
};
|
||||||
|
|
||||||
|
const search = async () => {
|
||||||
|
try {
|
||||||
|
const res = await SearchFavorite(req);
|
||||||
|
data.value = res.data.items;
|
||||||
|
paginationConfig.total = res.data.total;
|
||||||
|
open.value = true;
|
||||||
|
} catch (error) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
const singleDel = async (id: number) => {
|
||||||
|
ElMessageBox.confirm(i18n.global.t('file.removeFavorite'), i18n.global.t('commons.msg.remove'), {
|
||||||
|
confirmButtonText: i18n.global.t('commons.button.confirm'),
|
||||||
|
cancelButtonText: i18n.global.t('commons.button.cancel'),
|
||||||
|
}).then(async () => {
|
||||||
|
try {
|
||||||
|
await RemoveFavorite(id);
|
||||||
|
search();
|
||||||
|
} catch (error) {}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const buttons = [
|
||||||
|
{
|
||||||
|
label: i18n.global.t('commons.button.delete'),
|
||||||
|
click: (row: any) => {
|
||||||
|
singleDel(row.id);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
defineExpose({ acceptParams });
|
||||||
|
</script>
|
@ -98,6 +98,9 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="right-section">
|
<div class="right-section">
|
||||||
|
<el-button @click="openFavorite" :icon="Star">
|
||||||
|
{{ $t('file.favorite') }}
|
||||||
|
</el-button>
|
||||||
<el-button class="btn" @click="openRecycleBin" :icon="Delete">
|
<el-button class="btn" @click="openRecycleBin" :icon="Delete">
|
||||||
{{ $t('file.recycleBin') }}
|
{{ $t('file.recycleBin') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
@ -140,11 +143,42 @@
|
|||||||
sortable
|
sortable
|
||||||
prop="name"
|
prop="name"
|
||||||
>
|
>
|
||||||
<template #default="{ row }">
|
<template #default="{ row, $index }">
|
||||||
<svg-icon v-if="row.isDir" className="table-icon" iconName="p-file-folder"></svg-icon>
|
<div @mouseenter="showFavorite($index)" @mouseleave="hideFavorite">
|
||||||
<svg-icon v-else className="table-icon" :iconName="getIconName(row.extension)"></svg-icon>
|
<el-row>
|
||||||
<span class="table-link" @click="open(row)" type="primary">{{ row.name }}</span>
|
<el-col :span="23">
|
||||||
<span v-if="row.isSymlink">-> {{ row.linkPath }}</span>
|
<svg-icon
|
||||||
|
v-if="row.isDir"
|
||||||
|
className="table-icon"
|
||||||
|
iconName="p-file-folder"
|
||||||
|
></svg-icon>
|
||||||
|
<svg-icon
|
||||||
|
v-else
|
||||||
|
className="table-icon"
|
||||||
|
:iconName="getIconName(row.extension)"
|
||||||
|
></svg-icon>
|
||||||
|
<span class="table-link" @click="open(row)" type="primary">{{ row.name }}</span>
|
||||||
|
<span v-if="row.isSymlink">-> {{ row.linkPath }}</span>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="1">
|
||||||
|
<el-button
|
||||||
|
v-if="row.favoriteID > 0"
|
||||||
|
link
|
||||||
|
type="warning"
|
||||||
|
:icon="Star"
|
||||||
|
@click="removeFavorite(row.favoriteID)"
|
||||||
|
></el-button>
|
||||||
|
<div v-else>
|
||||||
|
<el-button
|
||||||
|
v-if="hoveredRowIndex === $index"
|
||||||
|
link
|
||||||
|
:icon="Star"
|
||||||
|
@click="addFavorite(row)"
|
||||||
|
></el-button>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column :label="$t('file.mode')" prop="mode" max-width="50">
|
<el-table-column :label="$t('file.mode')" prop="mode" max-width="50">
|
||||||
@ -213,16 +247,25 @@
|
|||||||
<Detail ref="detailRef" />
|
<Detail ref="detailRef" />
|
||||||
<DeleteFile ref="deleteRef" @close="search" />
|
<DeleteFile ref="deleteRef" @close="search" />
|
||||||
<RecycleBin ref="recycleBinRef" @close="search" />
|
<RecycleBin ref="recycleBinRef" @close="search" />
|
||||||
|
<Favorite ref="favoriteRef" @close="search" />
|
||||||
</LayoutContent>
|
</LayoutContent>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { nextTick, onMounted, reactive, ref, computed } from '@vue/runtime-core';
|
import { nextTick, onMounted, reactive, ref, computed } from '@vue/runtime-core';
|
||||||
import { GetFilesList, GetFileContent, ComputeDirSize } from '@/api/modules/files';
|
import { GetFilesList, GetFileContent, ComputeDirSize, AddFavorite, RemoveFavorite } from '@/api/modules/files';
|
||||||
import { computeSize, dateFormat, downloadFile, getIcon, getRandomStr } from '@/utils/util';
|
import { computeSize, dateFormat, downloadFile, getIcon, getRandomStr } from '@/utils/util';
|
||||||
import { Delete } from '@element-plus/icons-vue';
|
import { Delete, Star } from '@element-plus/icons-vue';
|
||||||
import { File } from '@/api/interface/file';
|
import { File } from '@/api/interface/file';
|
||||||
|
import { Mimetypes, Languages } from '@/global/mimetype';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
import { Back, Refresh } from '@element-plus/icons-vue';
|
||||||
|
import { MsgSuccess, MsgWarning } from '@/utils/message';
|
||||||
|
import { useSearchable } from './hooks/searchable';
|
||||||
|
import { ResultData } from '@/api/interface';
|
||||||
|
import { GlobalStore } from '@/store';
|
||||||
|
|
||||||
import i18n from '@/lang';
|
import i18n from '@/lang';
|
||||||
import CreateFile from './create/index.vue';
|
import CreateFile from './create/index.vue';
|
||||||
import ChangeRole from './change-role/index.vue';
|
import ChangeRole from './change-role/index.vue';
|
||||||
@ -236,16 +279,11 @@ import Move from './move/index.vue';
|
|||||||
import Download from './download/index.vue';
|
import Download from './download/index.vue';
|
||||||
import Owner from './chown/index.vue';
|
import Owner from './chown/index.vue';
|
||||||
import DeleteFile from './delete/index.vue';
|
import DeleteFile from './delete/index.vue';
|
||||||
import { Mimetypes, Languages } from '@/global/mimetype';
|
|
||||||
import Process from './process/index.vue';
|
import Process from './process/index.vue';
|
||||||
import Detail from './detail/index.vue';
|
import Detail from './detail/index.vue';
|
||||||
import RecycleBin from './recycle-bin/index.vue';
|
import RecycleBin from './recycle-bin/index.vue';
|
||||||
import { useRouter } from 'vue-router';
|
import Favorite from './favorite/index.vue';
|
||||||
import { Back, Refresh } from '@element-plus/icons-vue';
|
|
||||||
import { MsgSuccess, MsgWarning } from '@/utils/message';
|
|
||||||
import { useSearchable } from './hooks/searchable';
|
|
||||||
import { ResultData } from '@/api/interface';
|
|
||||||
import { GlobalStore } from '@/store';
|
|
||||||
const globalStore = GlobalStore();
|
const globalStore = GlobalStore();
|
||||||
|
|
||||||
interface FilePaths {
|
interface FilePaths {
|
||||||
@ -303,6 +341,8 @@ const chownRef = ref();
|
|||||||
const moveOpen = ref(false);
|
const moveOpen = ref(false);
|
||||||
const deleteRef = ref();
|
const deleteRef = ref();
|
||||||
const recycleBinRef = ref();
|
const recycleBinRef = ref();
|
||||||
|
const favoriteRef = ref();
|
||||||
|
const hoveredRowIndex = ref(-1);
|
||||||
|
|
||||||
// editablePath
|
// editablePath
|
||||||
const { searchableStatus, searchablePath, searchableInputRef, searchableInputBlur } = useSearchable(paths);
|
const { searchableStatus, searchablePath, searchableInputRef, searchableInputBlur } = useSearchable(paths);
|
||||||
@ -627,6 +667,10 @@ const openRecycleBin = () => {
|
|||||||
recycleBinRef.value.acceptParams();
|
recycleBinRef.value.acceptParams();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const openFavorite = () => {
|
||||||
|
favoriteRef.value.acceptParams();
|
||||||
|
};
|
||||||
|
|
||||||
const changeSort = ({ prop, order }) => {
|
const changeSort = ({ prop, order }) => {
|
||||||
req.sortBy = prop;
|
req.sortBy = prop;
|
||||||
req.sortOrder = order;
|
req.sortOrder = order;
|
||||||
@ -637,6 +681,33 @@ const changeSort = ({ prop, order }) => {
|
|||||||
search();
|
search();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const showFavorite = (index: any) => {
|
||||||
|
hoveredRowIndex.value = index;
|
||||||
|
};
|
||||||
|
|
||||||
|
const hideFavorite = () => {
|
||||||
|
hoveredRowIndex.value = -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
const addFavorite = async (row: File.File) => {
|
||||||
|
try {
|
||||||
|
await AddFavorite(row.path);
|
||||||
|
search();
|
||||||
|
} catch (error) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
const removeFavorite = async (id: number) => {
|
||||||
|
ElMessageBox.confirm(i18n.global.t('file.removeFavorite'), i18n.global.t('commons.msg.remove'), {
|
||||||
|
confirmButtonText: i18n.global.t('commons.button.confirm'),
|
||||||
|
cancelButtonText: i18n.global.t('commons.button.cancel'),
|
||||||
|
}).then(async () => {
|
||||||
|
try {
|
||||||
|
await RemoveFavorite(id);
|
||||||
|
search();
|
||||||
|
} catch (error) {}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const buttons = [
|
const buttons = [
|
||||||
{
|
{
|
||||||
label: i18n.global.t('file.open'),
|
label: i18n.global.t('file.open'),
|
||||||
@ -759,7 +830,9 @@ onMounted(() => {
|
|||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.right-section > *:not(:last-child) {
|
.right-section {
|
||||||
margin-right: 10px;
|
.btn {
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user