1Panel/backend/app/service/file.go

381 lines
10 KiB
Go
Raw Normal View History

2022-08-24 11:10:50 +08:00
package service
import (
"fmt"
2022-12-01 10:36:49 +08:00
"io/fs"
"os"
2023-11-23 11:00:08 +08:00
"path"
2022-12-01 10:36:49 +08:00
"path/filepath"
"strings"
"time"
2023-03-02 13:54:07 +08:00
"github.com/1Panel-dev/1Panel/backend/app/dto/request"
"github.com/1Panel-dev/1Panel/backend/app/dto/response"
"github.com/1Panel-dev/1Panel/backend/buserr"
"github.com/1Panel-dev/1Panel/backend/constant"
"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"
2022-08-24 11:10:50 +08:00
)
type FileService struct {
}
2023-03-28 18:00:06 +08:00
type IFileService interface {
GetFileList(op request.FileOption) (response.FileInfo, error)
SearchUploadWithPage(req request.SearchUploadWithPage) (int64, interface{}, error)
GetFileTree(op request.FileOption) ([]response.FileTree, error)
Create(op request.FileCreate) error
Delete(op request.FileDelete) error
BatchDelete(op request.FileBatchDelete) error
Compress(c request.FileCompress) error
DeCompress(c request.FileDeCompress) error
GetContent(op request.FileContentReq) (response.FileInfo, error)
2023-03-28 18:00:06 +08:00
SaveContent(edit request.FileEdit) error
FileDownload(d request.FileDownload) (string, error)
DirSize(req request.DirSizeReq) (response.DirSizeRes, error)
ChangeName(req request.FileRename) error
Wget(w request.FileWget) (string, error)
MvFile(m request.FileMove) error
ChangeOwner(req request.FileRoleUpdate) error
ChangeMode(op request.FileCreate) error
BatchChangeModeAndOwner(op request.FileRoleReq) error
2023-11-23 11:00:08 +08:00
ReadLogByLine(req request.FileReadByLineReq) (*response.FileLineContent, error)
2023-03-28 18:00:06 +08:00
}
func NewIFileService() IFileService {
return &FileService{}
}
func (f *FileService) GetFileList(op request.FileOption) (response.FileInfo, error) {
2022-12-14 15:39:13 +08:00
var fileInfo response.FileInfo
2022-12-01 10:36:49 +08:00
if _, err := os.Stat(op.Path); err != nil && os.IsNotExist(err) {
return fileInfo, nil
}
2022-08-24 11:10:50 +08:00
info, err := files.NewFileInfo(op.FileOption)
if err != nil {
return fileInfo, err
}
fileInfo.FileInfo = *info
return fileInfo, nil
}
2022-08-24 17:34:21 +08:00
2023-03-28 18:00:06 +08:00
func (f *FileService) SearchUploadWithPage(req request.SearchUploadWithPage) (int64, interface{}, error) {
2023-03-02 13:54:07 +08:00
var (
files []response.UploadInfo
backData []response.UploadInfo
2023-03-02 13:54:07 +08:00
)
_ = filepath.Walk(req.Path, func(path string, info os.FileInfo, err error) error {
if err != nil {
return nil
}
if !info.IsDir() {
files = append(files, response.UploadInfo{
CreatedAt: info.ModTime().Format("2006-01-02 15:04:05"),
Size: int(info.Size()),
Name: info.Name(),
})
}
return nil
})
total, start, end := len(files), (req.Page-1)*req.PageSize, req.Page*req.PageSize
if start > total {
backData = make([]response.UploadInfo, 0)
2023-03-02 13:54:07 +08:00
} else {
if end >= total {
end = total
}
backData = files[start:end]
2023-03-02 13:54:07 +08:00
}
return int64(total), backData, nil
2023-03-02 13:54:07 +08:00
}
2023-03-28 18:00:06 +08:00
func (f *FileService) GetFileTree(op request.FileOption) ([]response.FileTree, error) {
2022-12-14 15:39:13 +08:00
var treeArray []response.FileTree
2022-08-24 17:34:21 +08:00
info, err := files.NewFileInfo(op.FileOption)
if err != nil {
return nil, err
}
2022-12-14 15:39:13 +08:00
node := response.FileTree{
ID: common.GetUuid(),
2022-08-24 17:34:21 +08:00
Name: info.Name,
Path: info.Path,
}
for _, v := range info.Items {
if v.IsDir {
2022-12-14 15:39:13 +08:00
node.Children = append(node.Children, response.FileTree{
ID: common.GetUuid(),
2022-08-24 17:34:21 +08:00
Name: v.Name,
Path: v.Path,
})
}
}
return append(treeArray, node), nil
}
2023-03-28 18:00:06 +08:00
func (f *FileService) Create(op request.FileCreate) error {
if files.IsInvalidChar(op.Path) {
return buserr.New("ErrInvalidChar")
}
2022-08-25 17:54:52 +08:00
fo := files.NewFileOp()
if fo.Stat(op.Path) {
return buserr.New(constant.ErrFileIsExit)
}
2024-04-09 15:34:08 +08:00
mode := op.Mode
if mode == 0 {
fileInfo, err := os.Stat(filepath.Dir(op.Path))
if err == nil {
mode = int64(fileInfo.Mode().Perm())
} else {
2024-04-09 15:34:08 +08:00
mode = 0755
}
}
if op.IsDir {
return fo.CreateDir(op.Path, fs.FileMode(mode))
}
if op.IsLink {
if !fo.Stat(op.LinkPath) {
return buserr.New(constant.ErrLinkPathNotFound)
}
2024-04-09 15:34:08 +08:00
return fo.LinkFile(op.LinkPath, op.Path, op.IsSymlink)
2022-08-25 17:54:52 +08:00
}
2024-04-09 15:34:08 +08:00
return fo.CreateFileWithMode(op.Path, fs.FileMode(mode))
2022-08-25 17:54:52 +08:00
}
2023-03-28 18:00:06 +08:00
func (f *FileService) Delete(op request.FileDelete) error {
2022-08-25 18:48:03 +08:00
fo := files.NewFileOp()
recycleBinStatus, _ := settingRepo.Get(settingRepo.WithByKey("FileRecycleBin"))
if recycleBinStatus.Value == "disable" {
op.ForceDelete = true
}
if op.ForceDelete {
if op.IsDir {
return fo.DeleteDir(op.Path)
} else {
return fo.DeleteFile(op.Path)
}
2022-08-25 18:48:03 +08:00
}
if err := NewIRecycleBinService().Create(request.RecycleBinCreate{SourcePath: op.Path}); err != nil {
return err
}
return favoriteRepo.Delete(favoriteRepo.WithByPath(op.Path))
2022-08-25 18:48:03 +08:00
}
2023-03-28 18:00:06 +08:00
func (f *FileService) BatchDelete(op request.FileBatchDelete) error {
2022-12-01 10:36:49 +08:00
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
}
2023-03-28 18:00:06 +08:00
func (f *FileService) ChangeMode(op request.FileCreate) error {
fo := files.NewFileOp()
return fo.ChmodR(op.Path, op.Mode, op.Sub)
}
func (f *FileService) BatchChangeModeAndOwner(op request.FileRoleReq) error {
fo := files.NewFileOp()
for _, path := range op.Paths {
if !fo.Stat(path) {
return buserr.New(constant.ErrPathNotFound)
}
if err := fo.ChownR(path, op.User, op.Group, op.Sub); err != nil {
return err
}
if err := fo.ChmodR(path, op.Mode, op.Sub); err != nil {
return err
}
}
return nil
}
func (f *FileService) ChangeOwner(req request.FileRoleUpdate) error {
fo := files.NewFileOp()
return fo.ChownR(req.Path, req.User, req.Group, req.Sub)
}
2023-03-28 18:00:06 +08:00
func (f *FileService) Compress(c request.FileCompress) error {
2022-08-30 17:59:59 +08:00
fo := files.NewFileOp()
if !c.Replace && fo.Stat(filepath.Join(c.Dst, c.Name)) {
return buserr.New(constant.ErrFileIsExit)
2022-08-30 17:59:59 +08:00
}
return fo.Compress(c.Files, c.Dst, c.Name, files.CompressType(c.Type))
}
2023-03-28 18:00:06 +08:00
func (f *FileService) DeCompress(c request.FileDeCompress) error {
2022-08-31 13:59:02 +08:00
fo := files.NewFileOp()
return fo.Decompress(c.Path, c.Dst, files.CompressType(c.Type))
}
func (f *FileService) GetContent(op request.FileContentReq) (response.FileInfo, error) {
info, err := files.NewFileInfo(files.FileOption{
Path: op.Path,
Expand: true,
})
2022-09-01 19:02:33 +08:00
if err != nil {
2022-12-14 15:39:13 +08:00
return response.FileInfo{}, err
2022-09-01 19:02:33 +08:00
}
2022-12-14 15:39:13 +08:00
return response.FileInfo{FileInfo: *info}, nil
2022-09-01 19:02:33 +08:00
}
2023-03-28 18:00:06 +08:00
func (f *FileService) SaveContent(edit request.FileEdit) error {
2022-09-01 19:02:33 +08:00
info, err := files.NewFileInfo(files.FileOption{
2022-09-06 17:48:49 +08:00
Path: edit.Path,
2022-09-01 19:02:33 +08:00
Expand: false,
})
if err != nil {
return err
}
fo := files.NewFileOp()
2022-09-06 17:48:49 +08:00
return fo.WriteFile(edit.Path, strings.NewReader(edit.Content), info.FileMode)
2022-09-01 19:02:33 +08:00
}
2023-03-28 18:00:06 +08:00
func (f *FileService) ChangeName(req request.FileRename) error {
if files.IsInvalidChar(req.NewName) {
return buserr.New("ErrInvalidChar")
}
2022-09-03 22:22:40 +08:00
fo := files.NewFileOp()
2022-12-14 15:39:13 +08:00
return fo.Rename(req.OldName, req.NewName)
2022-09-03 22:22:40 +08:00
}
2023-03-28 18:00:06 +08:00
func (f *FileService) Wget(w request.FileWget) (string, error) {
2022-09-05 16:25:26 +08:00
fo := files.NewFileOp()
2023-03-11 19:55:37 +08:00
key := "file-wget-" + common.GetUuid()
return key, fo.DownloadFileWithProcess(w.Url, filepath.Join(w.Path, w.Name), key, w.IgnoreCertificate)
2022-09-05 16:25:26 +08:00
}
2023-03-28 18:00:06 +08:00
func (f *FileService) MvFile(m request.FileMove) error {
2022-09-06 10:35:35 +08:00
fo := files.NewFileOp()
2023-02-09 14:31:05 +08:00
if !fo.Stat(m.NewPath) {
return buserr.New(constant.ErrPathNotFound)
}
for _, oldPath := range m.OldPaths {
if !fo.Stat(oldPath) {
return buserr.WithName(constant.ErrFileNotFound, oldPath)
}
if oldPath == m.NewPath || strings.Contains(m.NewPath, filepath.Clean(oldPath)+"/") {
2023-03-10 11:44:58 +08:00
return buserr.New(constant.ErrMovePathFailed)
}
}
2022-09-06 17:48:49 +08:00
if m.Type == "cut" {
return fo.Cut(m.OldPaths, m.NewPath, m.Name, m.Cover)
2022-09-06 10:35:35 +08:00
}
2022-09-06 15:46:46 +08:00
var errs []error
2022-09-06 17:48:49 +08:00
if m.Type == "copy" {
for _, src := range m.OldPaths {
if err := fo.CopyAndReName(src, m.NewPath, m.Name, m.Cover); err != nil {
2022-09-06 15:46:46 +08:00
errs = append(errs, err)
2022-09-06 17:48:49 +08:00
global.LOG.Errorf("copy file [%s] to [%s] failed, err: %s", src, m.NewPath, err.Error())
2022-09-06 15:46:46 +08:00
}
}
}
var errString string
for _, err := range errs {
errString += err.Error() + "\n"
}
if errString != "" {
return errors.New(errString)
}
2022-09-06 10:35:35 +08:00
return nil
}
2023-03-28 18:00:06 +08:00
func (f *FileService) FileDownload(d request.FileDownload) (string, error) {
filePath := d.Paths[0]
if d.Compress {
tempPath := filepath.Join(os.TempDir(), fmt.Sprintf("%d", time.Now().UnixNano()))
if err := os.MkdirAll(tempPath, os.ModePerm); err != nil {
return "", err
}
fo := files.NewFileOp()
if err := fo.Compress(d.Paths, tempPath, d.Name, files.CompressType(d.Type)); err != nil {
return "", err
}
filePath = filepath.Join(tempPath, d.Name)
2022-09-06 17:48:49 +08:00
}
return filePath, nil
}
2023-03-28 18:00:06 +08:00
func (f *FileService) DirSize(req request.DirSizeReq) (response.DirSizeRes, error) {
fo := files.NewFileOp()
size, err := fo.GetDirSize(req.Path)
if err != nil {
2022-12-14 15:39:13 +08:00
return response.DirSizeRes{}, err
}
2022-12-14 15:39:13 +08:00
return response.DirSizeRes{Size: size}, nil
}
2023-11-23 11:00:08 +08:00
func (f *FileService) ReadLogByLine(req request.FileReadByLineReq) (*response.FileLineContent, error) {
logFilePath := ""
switch req.Type {
case constant.TypeWebsite:
website, err := websiteRepo.GetFirst(commonRepo.WithByID(req.ID))
if err != nil {
return nil, err
}
nginx, err := getNginxFull(&website)
if err != nil {
return nil, err
}
sitePath := path.Join(nginx.SiteDir, "sites", website.Alias)
logFilePath = path.Join(sitePath, "log", req.Name)
case constant.TypePhp:
php, err := runtimeRepo.GetFirst(commonRepo.WithByID(req.ID))
if err != nil {
return nil, err
}
logFilePath = php.GetLogPath()
case constant.TypeSSL:
ssl, err := websiteSSLRepo.GetFirst(commonRepo.WithByID(req.ID))
if err != nil {
return nil, err
}
logFilePath = ssl.GetLogPath()
case constant.TypeSystem:
fileName := ""
if len(req.Name) == 0 || req.Name == time.Now().Format("2006-01-02") {
2023-11-23 11:00:08 +08:00
fileName = "1Panel.log"
} else {
fileName = "1Panel-" + req.Name + ".log"
}
logFilePath = path.Join(global.CONF.System.DataDir, "log", fileName)
if _, err := os.Stat(logFilePath); err != nil {
fileGzPath := path.Join(global.CONF.System.DataDir, "log", fileName+".gz")
if _, err := os.Stat(fileGzPath); err != nil {
return nil, buserr.New("ErrHttpReqNotFound")
}
if err := handleGunzip(fileGzPath); err != nil {
return nil, fmt.Errorf("handle ungzip file %s failed, err: %v", fileGzPath, err)
}
}
case "image-pull", "image-push", "image-build", "compose-create":
logFilePath = path.Join(global.CONF.System.TmpDir, fmt.Sprintf("docker_logs/%s", req.Name))
}
lines, isEndOfFile, err := files.ReadFileByLine(logFilePath, req.Page, req.PageSize)
if err != nil {
return nil, err
}
res := &response.FileLineContent{
Content: strings.Join(lines, "\n"),
End: isEndOfFile,
Path: logFilePath,
}
return res, nil
}