Tiger Wang 2 years ago
parent
commit
dd66f73157
4 changed files with 94 additions and 108 deletions
  1. 0 64
      middleware/gin.go
  2. 9 3
      pkg/utils/file/file.go
  3. 16 4
      route/route.go
  4. 69 37
      route/v1/file.go

+ 0 - 64
middleware/gin.go

@@ -1,64 +0,0 @@
-/*
- * @Author: LinkLeong link@icewhale.com
- * @Date: 2021-10-08 10:29:08
- * @LastEditors: LinkLeong
- * @LastEditTime: 2022-07-22 11:06:07
- * @FilePath: /CasaOS/middleware/gin.go
- * @Description:
- * @Website: https://www.casaos.io
- * Copyright (c) 2022 by icewhale, All Rights Reserved.
- */
-package middleware
-
-import (
-	"fmt"
-	"net/http"
-	"runtime/debug"
-	"strings"
-
-	"github.com/IceWhaleTech/CasaOS/pkg/utils/loger"
-	"github.com/gin-gonic/gin"
-	"go.uber.org/zap"
-)
-
-func Cors() gin.HandlerFunc {
-	return func(c *gin.Context) {
-		method := c.Request.Method
-
-		c.Header("Access-Control-Allow-Origin", "*")
-		c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE,UPDATE")
-		// 允许跨域设置可以返回其他子段,可以自定义字段
-		c.Header("Access-Control-Allow-Headers", "Authorization, Content-Length, X-CSRF-Token, Token,session,Language,Content-Type,Access-Control-Allow-Origin,Access-Control-Allow-Headers,Access-Control-Allow-Methods,Connection,Host,Origin,Referer,User-Agent,X-Requested-With")
-		// 允许浏览器(客户端)可以解析的头部 (重要)
-		c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers")
-		// c.Writer.Header().Set("Access-Control-Allow-Headers", "Accept, Authorization, Content-Type, Content-Length, X-CSRF-Token, Token, session, Origin, Host, Connection, Accept-Encoding, Accept-Language, X-Requested-With")
-		// 设置缓存时间
-		c.Header("Access-Control-Max-Age", "172800")
-		c.Header("Access-Control-Allow-Credentials", "true")
-		c.Set("Content-Type", "application/json")
-		//}
-
-		// 允许类型校验
-		if method == "OPTIONS" {
-			c.JSON(http.StatusOK, "ok!")
-		}
-
-		defer func() {
-			if err := recover(); err != nil {
-				fmt.Println(err)
-				debug.PrintStack()
-			}
-		}()
-
-		c.Next()
-	}
-}
-
-func WriteLog() gin.HandlerFunc {
-	return func(c *gin.Context) {
-		if !strings.Contains(c.Request.URL.String(), "password") {
-			loger.Info("request:", zap.Any("path", c.Request.URL.String()), zap.Any("param", c.Params), zap.Any("query", c.Request.URL.Query()), zap.Any("method", c.Request.Method))
-			c.Next()
-		}
-	}
-}

+ 9 - 3
pkg/utils/file/file.go

@@ -362,21 +362,27 @@ func WriteToFullPath(data []byte, fullPath string, perm fs.FileMode) error {
 func SpliceFiles(dir, path string, length int, startPoint int) error {
 	fullPath := path
 
-	IsNotExistCreateFile(fullPath)
+	if err := IsNotExistCreateFile(fullPath); err != nil {
+		return err
+	}
 
 	file, _ := os.OpenFile(fullPath,
 		os.O_WRONLY|os.O_TRUNC|os.O_CREATE,
 		0o666,
 	)
+
 	defer file.Close()
+
 	bufferedWriter := bufio.NewWriter(file)
+
+	// todo: here should have a goroutine to remove each partial file after it is read, to save disk space
+
 	for i := 0; i < length+startPoint; i++ {
 		data, err := ioutil.ReadFile(dir + "/" + strconv.Itoa(i+startPoint))
 		if err != nil {
 			return err
 		}
-		_, err = bufferedWriter.Write(data)
-		if err != nil {
+		if _, err := bufferedWriter.Write(data); err != nil { // recommend to use https://github.com/iceber/iouring-go for faster write
 			return err
 		}
 	}

+ 16 - 4
route/route.go

@@ -1,8 +1,10 @@
 package route
 
 import (
+	"os"
+
+	"github.com/IceWhaleTech/CasaOS-Common/middleware"
 	"github.com/IceWhaleTech/CasaOS-Common/utils/jwt"
-	"github.com/IceWhaleTech/CasaOS/middleware"
 	"github.com/IceWhaleTech/CasaOS/pkg/config"
 	v1 "github.com/IceWhaleTech/CasaOS/route/v1"
 
@@ -11,12 +13,22 @@ import (
 )
 
 func InitRouter() *gin.Engine {
-	r := gin.Default()
+	ginMode := gin.ReleaseMode
+	if config.ServerInfo.RunMode != "" {
+		ginMode = config.ServerInfo.RunMode
+	}
+	if os.Getenv(gin.EnvGinMode) != "" {
+		ginMode = os.Getenv(gin.EnvGinMode)
+	}
+	gin.SetMode(ginMode)
 
+	r := gin.New()
+	r.Use(gin.Recovery())
 	r.Use(middleware.Cors())
-	r.Use(middleware.WriteLog())
 	r.Use(gzip.Gzip(gzip.DefaultCompression))
-	gin.SetMode(config.ServerInfo.RunMode)
+	if ginMode != gin.ReleaseMode {
+		r.Use(middleware.WriteLog())
+	}
 
 	// r.StaticFS("/ui", http.FS(web.Static))
 	// r.GET("/", WebUIHome)

+ 69 - 37
route/v1/file.go

@@ -18,9 +18,11 @@ import (
 	"github.com/IceWhaleTech/CasaOS/model"
 	"github.com/IceWhaleTech/CasaOS/pkg/utils/common_err"
 	"github.com/IceWhaleTech/CasaOS/pkg/utils/file"
+	"github.com/IceWhaleTech/CasaOS/pkg/utils/loger"
 	"github.com/IceWhaleTech/CasaOS/service"
 	"github.com/gin-gonic/gin"
 	uuid "github.com/satori/go.uuid"
+	"go.uber.org/zap"
 )
 
 // @Summary 读取文件
@@ -47,7 +49,7 @@ func GetFilerContent(c *gin.Context) {
 		})
 		return
 	}
-	//文件读取任务是将文件内容读取到内存中。
+	// 文件读取任务是将文件内容读取到内存中。
 	info, err := ioutil.ReadFile(filePath)
 	if err != nil {
 		c.JSON(common_err.SERVICE_ERROR, model.Result{
@@ -83,7 +85,6 @@ func GetLocalFile(c *gin.Context) {
 		return
 	}
 	c.File(path)
-	return
 }
 
 // @Summary download
@@ -96,7 +97,6 @@ func GetLocalFile(c *gin.Context) {
 // @Success 200 {string} string "ok"
 // @Router /file/download [get]
 func GetDownloadFile(c *gin.Context) {
-
 	t := c.Query("format")
 
 	files := c.Query("files")
@@ -135,11 +135,11 @@ func GetDownloadFile(c *gin.Context) {
 		}
 		if !info.IsDir() {
 
-			//打开文件
+			// 打开文件
 			fileTmp, _ := os.Open(filePath)
 			defer fileTmp.Close()
 
-			//获取文件的名称
+			// 获取文件的名称
 			fileName := path.Base(filePath)
 			c.Header("Content-Disposition", "attachment; filename*=utf-8''"+url2.PathEscape(fileName))
 			c.File(filePath)
@@ -179,7 +179,6 @@ func GetDownloadFile(c *gin.Context) {
 			log.Printf("Failed to archive %s: %v", fname, err)
 		}
 	}
-
 }
 
 func GetDownloadSingleFile(c *gin.Context) {
@@ -202,7 +201,7 @@ func GetDownloadSingleFile(c *gin.Context) {
 	defer fileTmp.Close()
 
 	fileName := path.Base(filePath)
-	//c.Header("Content-Disposition", "inline")
+	// c.Header("Content-Disposition", "inline")
 	c.Header("Content-Disposition", "attachment; filename*=utf-8''"+url2.PathEscape(fileName))
 	c.File(filePath)
 }
@@ -248,7 +247,7 @@ func DirPath(c *gin.Context) {
 			info[i].Extensions = ex
 		}
 	}
-	//Hide the files or folders in operation
+	// Hide the files or folders in operation
 	fileQueue := make(map[string]string)
 	if len(service.OpStrArr) > 0 {
 		for _, v := range service.OpStrArr {
@@ -361,7 +360,6 @@ func PostCreateFile(c *gin.Context) {
 // @Success 200 {string} string "ok"
 // @Router /file/upload [get]
 func GetFileUpload(c *gin.Context) {
-
 	relative := c.Query("relativePath")
 	fileName := c.Query("filename")
 	chunkNumber := c.Query("chunkNumber")
@@ -405,55 +403,92 @@ func PostFileUpload(c *gin.Context) {
 	hash := file.GetHashByContent([]byte(fileName))
 
 	if len(path) == 0 {
-		c.JSON(common_err.INVALID_PARAMS, model.Result{Success: common_err.INVALID_PARAMS, Message: common_err.GetMsg(common_err.INVALID_PARAMS)})
+		loger.Error("path should not be empty")
+		c.JSON(http.StatusBadRequest, model.Result{Success: common_err.INVALID_PARAMS, Message: common_err.GetMsg(common_err.INVALID_PARAMS)})
 		return
 	}
+
 	tempDir := filepath.Join(path, ".temp", hash+strconv.Itoa(totalChunks)) + "/"
 
 	if fileName != relative {
 		dirPath = strings.TrimSuffix(relative, fileName)
 		tempDir += dirPath
-		file.MkDir(path + "/" + dirPath)
+		if err := file.MkDir(path + "/" + dirPath); err != nil {
+			loger.Error("error when trying to create `"+path+"/"+dirPath+"`", zap.Error(err))
+			c.JSON(http.StatusInternalServerError, model.Result{Success: common_err.SERVICE_ERROR, Message: err.Error()})
+			return
+		}
 	}
 
 	path += "/" + relative
 
 	if !file.CheckNotExist(tempDir + chunkNumber) {
-		file.RMDir(tempDir + chunkNumber)
+		if err := file.RMDir(tempDir + chunkNumber); err != nil {
+			loger.Error("error when trying to remove existing `"+tempDir+chunkNumber+"`", zap.Error(err))
+			c.JSON(http.StatusInternalServerError, model.Result{Success: common_err.SERVICE_ERROR, Message: err.Error()})
+			return
+		}
 	}
 
 	if totalChunks > 1 {
-		file.IsNotExistMkDir(tempDir)
+		if err := file.IsNotExistMkDir(tempDir); err != nil {
+			loger.Error("error when trying to create `"+tempDir+"`", zap.Error(err))
+			c.JSON(http.StatusInternalServerError, model.Result{Success: common_err.SERVICE_ERROR, Message: err.Error()})
+			return
+		}
+
+		out, err := os.OpenFile(tempDir+chunkNumber, os.O_WRONLY|os.O_CREATE, 0o644)
+		if err != nil {
+			loger.Error("error when trying to open `"+tempDir+chunkNumber+"` for creation", zap.Error(err))
+			c.JSON(http.StatusInternalServerError, model.Result{Success: common_err.SERVICE_ERROR, Message: err.Error()})
+			return
+		}
 
-		out, _ := os.OpenFile(tempDir+chunkNumber, os.O_WRONLY|os.O_CREATE, 0644)
 		defer out.Close()
-		_, err := io.Copy(out, f)
+
+		if _, err := io.Copy(out, f); err != nil { // recommend to use https://github.com/iceber/iouring-go for faster copy
+			loger.Error("error when trying to write to `"+tempDir+chunkNumber+"`", zap.Error(err))
+			c.JSON(http.StatusInternalServerError, model.Result{Success: common_err.SERVICE_ERROR, Message: err.Error()})
+			return
+		}
+
+		fileNum, err := ioutil.ReadDir(tempDir)
 		if err != nil {
-			c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.SERVICE_ERROR, Message: common_err.GetMsg(common_err.SERVICE_ERROR), Data: err.Error()})
+			loger.Error("error when trying to read number of files under `"+tempDir+"`", zap.Error(err))
+			c.JSON(http.StatusInternalServerError, model.Result{Success: common_err.SERVICE_ERROR, Message: err.Error()})
 			return
 		}
+
+		if totalChunks == len(fileNum) {
+			if err := file.SpliceFiles(tempDir, path, totalChunks, 1); err != nil {
+				loger.Error("error when trying to splice files under `"+tempDir+"`", zap.Error(err))
+				c.JSON(http.StatusInternalServerError, model.Result{Success: common_err.SERVICE_ERROR, Message: err.Error()})
+				return
+			}
+
+			if err := file.RMDir(tempDir); err != nil {
+				loger.Error("error when trying to remove `"+tempDir+"`", zap.Error(err))
+				c.JSON(http.StatusInternalServerError, model.Result{Success: common_err.SERVICE_ERROR, Message: err.Error()})
+				return
+			}
+		}
 	} else {
-		out, _ := os.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0644)
-		defer out.Close()
-		_, err := io.Copy(out, f)
+		out, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0o644)
 		if err != nil {
-			c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.SERVICE_ERROR, Message: common_err.GetMsg(common_err.SERVICE_ERROR), Data: err.Error()})
+			loger.Error("error when trying to open `"+path+"` for creation", zap.Error(err))
+			c.JSON(http.StatusInternalServerError, model.Result{Success: common_err.SERVICE_ERROR, Message: err.Error()})
 			return
 		}
-		c.JSON(http.StatusOK, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS)})
-		return
-	}
-	fileNum, err := ioutil.ReadDir(tempDir)
-	if err != nil {
-		c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.SERVICE_ERROR, Message: common_err.GetMsg(common_err.SERVICE_ERROR), Data: err.Error()})
-		return
-	}
-	if totalChunks == len(fileNum) {
-		file.SpliceFiles(tempDir, path, totalChunks, 1)
-		file.RMDir(tempDir)
-	}
 
-	c.JSON(common_err.SUCCESS, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS)})
+		defer out.Close()
+
+		if _, err := io.Copy(out, f); err != nil { // recommend to use https://github.com/iceber/iouring-go for faster copy
+			loger.Error("error when trying to write to `"+path+"`", zap.Error(err))
+			c.JSON(http.StatusInternalServerError, model.Result{Success: common_err.SERVICE_ERROR, Message: common_err.GetMsg(common_err.SERVICE_ERROR), Data: err.Error()})
+			return
+		}
+	}
+	c.JSON(http.StatusOK, model.Result{Success: common_err.SUCCESS, Message: common_err.GetMsg(common_err.SUCCESS)})
 }
 
 // @Summary copy or move file
@@ -465,7 +500,6 @@ func PostFileUpload(c *gin.Context) {
 // @Success 200 {string} string "ok"
 // @Router /file/operate [post]
 func PostOperateFileOrDir(c *gin.Context) {
-
 	list := model.FileOperate{}
 	c.ShouldBind(&list)
 
@@ -515,7 +549,6 @@ func PostOperateFileOrDir(c *gin.Context) {
 // @Success 200 {string} string "ok"
 // @Router /file/delete [delete]
 func DeleteFile(c *gin.Context) {
-
 	paths := []string{}
 	c.ShouldBind(&paths)
 	if len(paths) == 0 {
@@ -547,7 +580,6 @@ func DeleteFile(c *gin.Context) {
 // @Success 200 {string} string "ok"
 // @Router /file/update [put]
 func PutFileContent(c *gin.Context) {
-
 	fi := model.FileUpdate{}
 	c.ShouldBind(&fi)
 
@@ -557,7 +589,7 @@ func PutFileContent(c *gin.Context) {
 		c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.FILE_ALREADY_EXISTS, Message: common_err.GetMsg(common_err.FILE_ALREADY_EXISTS)})
 		return
 	}
-	//err := os.Remove(path)
+	// err := os.Remove(path)
 	err := os.RemoveAll(fi.FilePath)
 	if err != nil {
 		c.JSON(common_err.SERVICE_ERROR, model.Result{Success: common_err.FILE_DELETE_ERROR, Message: common_err.GetMsg(common_err.FILE_DELETE_ERROR), Data: err})