next-terminal/server/service/storage.go
2022-10-23 20:05:13 +08:00

314 lines
8.0 KiB
Go

package service
import (
"bufio"
"context"
"errors"
"fmt"
"io"
"io/ioutil"
"mime/multipart"
"net/http"
"os"
"path"
"strconv"
"strings"
"next-terminal/server/common"
"next-terminal/server/config"
"next-terminal/server/model"
"next-terminal/server/repository"
"next-terminal/server/utils"
"github.com/labstack/echo/v4"
"gorm.io/gorm"
)
var StorageService = new(storageService)
type storageService struct {
}
func (service storageService) InitStorages() error {
users, err := repository.UserRepository.FindAll(context.TODO())
if err != nil {
return err
}
for i := range users {
userId := users[i].ID
_, err := repository.StorageRepository.FindByOwnerIdAndDefault(context.TODO(), userId, true)
if errors.Is(err, gorm.ErrRecordNotFound) {
err = service.CreateStorageByUser(context.TODO(), &users[i])
if err != nil {
return err
}
}
}
drivePath := service.GetBaseDrivePath()
storages, err := repository.StorageRepository.FindAll(context.TODO())
if err != nil {
return err
}
for i := 0; i < len(storages); i++ {
storage := storages[i]
// 判断是否为遗留的数据:磁盘空间在,但用户已删除
if storage.IsDefault {
var userExist = false
for j := range users {
if storage.ID == users[j].ID {
userExist = true
break
}
}
if !userExist {
if err := service.DeleteStorageById(context.TODO(), storage.ID, true); err != nil {
return err
}
}
}
storageDir := path.Join(drivePath, storage.ID)
if !utils.FileExists(storageDir) {
if err := os.MkdirAll(storageDir, os.ModePerm); err != nil {
return err
}
}
}
return nil
}
func (service storageService) CreateStorageByUser(c context.Context, user *model.User) error {
drivePath := service.GetBaseDrivePath()
var limitSize int64
property, err := repository.PropertyRepository.FindByName(c, "user-default-storage-size")
if err != nil {
return err
}
limitSize, err = strconv.ParseInt(property.Value, 10, 64)
if err != nil {
return err
}
limitSize = limitSize * 1024 * 1024
if limitSize < 0 {
limitSize = -1
}
storage := model.Storage{
ID: user.ID,
Name: user.Nickname + "的默认空间",
IsShare: false,
IsDefault: true,
LimitSize: limitSize,
Owner: user.ID,
Created: common.NowJsonTime(),
}
storageDir := path.Join(drivePath, storage.ID)
if err := os.MkdirAll(storageDir, os.ModePerm); err != nil {
return err
}
err = repository.StorageRepository.Create(c, &storage)
if err != nil {
_ = os.RemoveAll(storageDir)
return err
}
return nil
}
type File struct {
Name string `json:"name"`
Path string `json:"path"`
IsDir bool `json:"isDir"`
Mode string `json:"mode"`
IsLink bool `json:"isLink"`
ModTime common.JsonTime `json:"modTime"`
Size int64 `json:"size"`
}
func (service storageService) Ls(drivePath, remoteDir string) ([]File, error) {
fileInfos, err := ioutil.ReadDir(path.Join(drivePath, remoteDir))
if err != nil {
return nil, err
}
var files = make([]File, 0)
for i := range fileInfos {
file := File{
Name: fileInfos[i].Name(),
Path: path.Join(remoteDir, fileInfos[i].Name()),
IsDir: fileInfos[i].IsDir(),
Mode: fileInfos[i].Mode().String(),
IsLink: fileInfos[i].Mode()&os.ModeSymlink == os.ModeSymlink,
ModTime: common.NewJsonTime(fileInfos[i].ModTime()),
Size: fileInfos[i].Size(),
}
files = append(files, file)
}
return files, nil
}
func (service storageService) GetBaseDrivePath() string {
return config.GlobalCfg.Guacd.Drive
}
func (service storageService) DeleteStorageById(c context.Context, id string, force bool) error {
drivePath := service.GetBaseDrivePath()
storage, err := repository.StorageRepository.FindById(c, id)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil
}
return err
}
if !force && storage.IsDefault {
return errors.New("默认空间不能删除")
}
// 删除对应的本地目录
if err := os.RemoveAll(path.Join(drivePath, id)); err != nil {
return err
}
if err := repository.StorageRepository.DeleteById(c, id); err != nil {
return err
}
return nil
}
func (service storageService) StorageUpload(c echo.Context, file *multipart.FileHeader, storageId string) error {
drivePath := service.GetBaseDrivePath()
storage, _ := repository.StorageRepository.FindById(context.TODO(), storageId)
if storage.LimitSize > 0 {
dirSize, err := utils.DirSize(path.Join(drivePath, storageId))
if err != nil {
return err
}
if dirSize+file.Size > storage.LimitSize {
return errors.New("可用空间不足")
}
}
filename := file.Filename
src, err := file.Open()
if err != nil {
return err
}
remoteDir := c.QueryParam("dir")
remoteFile := path.Join(remoteDir, filename)
if strings.Contains(remoteDir, "../") {
return errors.New("非法请求 :(")
}
if strings.Contains(remoteFile, "../") {
return errors.New("非法请求 :(")
}
// 判断文件夹不存在时自动创建
dir := path.Join(path.Join(drivePath, storageId), remoteDir)
if !utils.FileExists(dir) {
if err := os.MkdirAll(dir, os.ModePerm); err != nil {
return err
}
}
// Destination
dst, err := os.Create(path.Join(path.Join(drivePath, storageId), remoteFile))
if err != nil {
return err
}
defer dst.Close()
// Copy
if _, err = io.Copy(dst, src); err != nil {
return err
}
return nil
}
func (service storageService) StorageEdit(file string, fileContent string, storageId string) error {
drivePath := service.GetBaseDrivePath()
if strings.Contains(file, "../") {
return errors.New("非法请求 :(")
}
realFilePath := path.Join(path.Join(drivePath, storageId), file)
dstFile, err := os.OpenFile(realFilePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
if err != nil {
return err
}
defer dstFile.Close()
write := bufio.NewWriter(dstFile)
if _, err := write.WriteString(fileContent); err != nil {
return err
}
if err := write.Flush(); err != nil {
return err
}
return nil
}
func (service storageService) StorageDownload(c echo.Context, file, storageId string) error {
drivePath := service.GetBaseDrivePath()
if strings.Contains(file, "../") {
return errors.New("非法请求 :(")
}
// 获取带后缀的文件名称
filenameWithSuffix := path.Base(file)
p := path.Join(path.Join(drivePath, storageId), file)
//log.Infof("download %v", p)
c.Response().Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%s", filenameWithSuffix))
c.Response().Header().Set("Content-Type", "application/octet-stream")
http.ServeFile(c.Response(), c.Request(), p)
return nil
}
func (service storageService) StorageLs(remoteDir, storageId string) (error, []File) {
drivePath := service.GetBaseDrivePath()
if strings.Contains(remoteDir, "../") {
return errors.New("非法请求 :("), nil
}
files, err := service.Ls(path.Join(drivePath, storageId), remoteDir)
if err != nil {
return err, nil
}
return nil, files
}
func (service storageService) StorageMkDir(remoteDir, storageId string) error {
drivePath := service.GetBaseDrivePath()
if strings.Contains(remoteDir, "../") {
return errors.New("非法请求 :(")
}
if err := os.MkdirAll(path.Join(path.Join(drivePath, storageId), remoteDir), os.ModePerm); err != nil {
return err
}
return nil
}
func (service storageService) StorageRm(file, storageId string) error {
drivePath := service.GetBaseDrivePath()
if strings.Contains(file, "../") {
return errors.New("非法请求 :(")
}
if err := os.RemoveAll(path.Join(path.Join(drivePath, storageId), file)); err != nil {
return err
}
return nil
}
func (service storageService) StorageRename(oldName, newName, storageId string) error {
drivePath := service.GetBaseDrivePath()
if strings.Contains(oldName, "../") {
return errors.New("非法请求 :(")
}
if strings.Contains(newName, "../") {
return errors.New("非法请求 :(")
}
if err := os.Rename(path.Join(path.Join(drivePath, storageId), oldName), path.Join(path.Join(drivePath, storageId), newName)); err != nil {
return err
}
return nil
}