255 lines
5.8 KiB
Go
255 lines
5.8 KiB
Go
|
package file
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"errors"
|
||
|
"fmt"
|
||
|
"fonchain-fiee/api/files"
|
||
|
"fonchain-fiee/pkg/model/login"
|
||
|
"fonchain-fiee/pkg/service"
|
||
|
"io"
|
||
|
"net/http"
|
||
|
"net/url"
|
||
|
"strconv"
|
||
|
"strings"
|
||
|
"time"
|
||
|
|
||
|
"github.com/gin-gonic/gin"
|
||
|
)
|
||
|
|
||
|
func Raw(ctx *gin.Context) {
|
||
|
r := ctx.Request
|
||
|
w := ctx.Writer
|
||
|
|
||
|
w.Header().Add("Content-Security-Policy", `script-src 'none';`)
|
||
|
w.Header().Set("Cache-Control", "private")
|
||
|
rs, err := newGrpcReaderSeeker(getUserSpacePath(ctx), ctx.Query("path"))
|
||
|
if err != nil {
|
||
|
service.Error(ctx, err)
|
||
|
return
|
||
|
}
|
||
|
if r.URL.Query().Get("inline") == "true" {
|
||
|
w.Header().Set("Content-Disposition", "inline")
|
||
|
} else {
|
||
|
// As per RFC6266 section 4.3
|
||
|
w.Header().Set("Content-Disposition", "attachment; filename*=utf-8''"+rs.FileName)
|
||
|
}
|
||
|
http.ServeContent(ctx.Writer, r, rs.FileName, time.Now(), rs)
|
||
|
}
|
||
|
|
||
|
func List(ctx *gin.Context) {
|
||
|
path := ctx.DefaultQuery("path", "/")
|
||
|
sortBy := ctx.DefaultQuery("sortBy", "name")
|
||
|
sortAsc, _ := strconv.ParseBool(ctx.DefaultQuery("sortAsc", "true"))
|
||
|
resp, err := service.FilesProvider.List(ctx, &files.FileListReq{
|
||
|
Path: path,
|
||
|
UserSpacePath: getUserSpacePath(ctx),
|
||
|
Sorting: &files.Sorting{
|
||
|
By: sortBy,
|
||
|
Asc: sortAsc,
|
||
|
},
|
||
|
})
|
||
|
if err != nil {
|
||
|
service.Error(ctx, err)
|
||
|
return
|
||
|
}
|
||
|
service.Success(ctx, resp)
|
||
|
}
|
||
|
|
||
|
func Info(ctx *gin.Context) {
|
||
|
resp, err := service.FilesProvider.Info(ctx, &files.FileInfoReq{
|
||
|
Path: ctx.DefaultQuery("path", "/"),
|
||
|
UserSpacePath: getUserSpacePath(ctx),
|
||
|
})
|
||
|
if err != nil {
|
||
|
service.Error(ctx, err)
|
||
|
return
|
||
|
}
|
||
|
service.Success(ctx, resp)
|
||
|
}
|
||
|
|
||
|
func Create(ctx *gin.Context) {
|
||
|
var req files.CreateReq
|
||
|
if err := ctx.ShouldBindJSON(&req); err != nil {
|
||
|
service.Error(ctx, err)
|
||
|
return
|
||
|
}
|
||
|
req.UserSpacePath = getUserSpacePath(ctx)
|
||
|
resp, err := service.FilesProvider.Create(ctx, &req)
|
||
|
if err != nil {
|
||
|
service.Error(ctx, err)
|
||
|
return
|
||
|
}
|
||
|
service.Success(ctx, resp)
|
||
|
}
|
||
|
|
||
|
func Delete(ctx *gin.Context) {
|
||
|
resp, err := service.FilesProvider.Delete(ctx, &files.DeleteReq{
|
||
|
Path: ctx.DefaultQuery("path", "/"),
|
||
|
UserSpacePath: getUserSpacePath(ctx),
|
||
|
})
|
||
|
if err != nil {
|
||
|
service.Error(ctx, err)
|
||
|
return
|
||
|
}
|
||
|
service.Success(ctx, resp)
|
||
|
}
|
||
|
|
||
|
func Search(ctx *gin.Context) {
|
||
|
resp, err := service.FilesProvider.Search(ctx, &files.SearchReq{
|
||
|
UserSpacePath: getUserSpacePath(ctx),
|
||
|
Path: ctx.Query("path"),
|
||
|
Query: ctx.Query("query"),
|
||
|
})
|
||
|
if err != nil {
|
||
|
service.Error(ctx, err)
|
||
|
return
|
||
|
}
|
||
|
service.Success(ctx, resp)
|
||
|
}
|
||
|
|
||
|
func Upload(ctx *gin.Context) {
|
||
|
path, ok := ctx.GetQuery("path")
|
||
|
if !ok {
|
||
|
service.Error(ctx, errors.New("缺失参数路径"))
|
||
|
return
|
||
|
}
|
||
|
b, err := io.ReadAll(ctx.Request.Body)
|
||
|
if !ok {
|
||
|
service.Error(ctx, err)
|
||
|
return
|
||
|
}
|
||
|
resp, err := service.FilesProvider.Upload(ctx, &files.UploadReq{
|
||
|
Path: path,
|
||
|
UserSpacePath: getUserSpacePath(ctx),
|
||
|
Content: b,
|
||
|
})
|
||
|
if err != nil {
|
||
|
service.Error(ctx, err)
|
||
|
return
|
||
|
}
|
||
|
service.Success(ctx, resp)
|
||
|
}
|
||
|
|
||
|
func TusCreate(ctx *gin.Context) {
|
||
|
var req files.TusCreateReq
|
||
|
if err := ctx.ShouldBindJSON(&req); err != nil {
|
||
|
service.Error(ctx, err)
|
||
|
return
|
||
|
}
|
||
|
req.UserSpacePath = getUserSpacePath(ctx)
|
||
|
resp, err := service.FilesProvider.TusCreate(ctx, &req)
|
||
|
if err != nil {
|
||
|
service.Error(ctx, err)
|
||
|
return
|
||
|
}
|
||
|
service.Success(ctx, resp)
|
||
|
}
|
||
|
|
||
|
func TusUpload(ctx *gin.Context) {
|
||
|
path, ok := ctx.GetQuery("path")
|
||
|
if !ok {
|
||
|
service.Error(ctx, errors.New("文件路径缺失"))
|
||
|
return
|
||
|
}
|
||
|
uploadOffset, err := getUploadOffset(ctx.Request)
|
||
|
if err != nil {
|
||
|
service.Error(ctx, fmt.Errorf("invalid upload offset: %w", err))
|
||
|
return
|
||
|
}
|
||
|
b, err := io.ReadAll(ctx.Request.Body)
|
||
|
if !ok {
|
||
|
service.Error(ctx, err)
|
||
|
return
|
||
|
}
|
||
|
resp, err := service.FilesProvider.TusUpload(ctx, &files.TusUploadReq{
|
||
|
Path: path,
|
||
|
UploadOffset: uploadOffset,
|
||
|
Content: b,
|
||
|
UserSpacePath: getUserSpacePath(ctx),
|
||
|
})
|
||
|
if err != nil {
|
||
|
service.Error(ctx, err)
|
||
|
return
|
||
|
}
|
||
|
ctx.Writer.Header().Set("Upload-Offset", strconv.FormatInt(resp.UploadOffset, 10))
|
||
|
service.Success(ctx, nil)
|
||
|
}
|
||
|
|
||
|
func Preview(ctx *gin.Context) {
|
||
|
var size int
|
||
|
size, err := strconv.Atoi(ctx.Query("size"))
|
||
|
if err != nil {
|
||
|
size = 1
|
||
|
}
|
||
|
resp, err := service.FilesProvider.Preview(ctx, &files.PreviewReq{
|
||
|
Path: ctx.Query("path"),
|
||
|
UserSpacePath: getUserSpacePath(ctx),
|
||
|
Size: uint32(size),
|
||
|
})
|
||
|
if err != nil {
|
||
|
service.Error(ctx, err)
|
||
|
return
|
||
|
}
|
||
|
ctx.Writer.Header().Set("Cache-Control", "private")
|
||
|
http.ServeContent(ctx.Writer, ctx.Request, resp.FileName, time.UnixMilli(resp.ModTime), bytes.NewReader(resp.Content))
|
||
|
}
|
||
|
|
||
|
func Action(ctx *gin.Context) {
|
||
|
var req files.ActionReq
|
||
|
if err := ctx.ShouldBindJSON(&req); err != nil {
|
||
|
service.Error(ctx, err)
|
||
|
return
|
||
|
}
|
||
|
req.UserSpacePath = getUserSpacePath(ctx)
|
||
|
resp, err := service.FilesProvider.Action(ctx, &req)
|
||
|
if err != nil {
|
||
|
service.Error(ctx, err)
|
||
|
return
|
||
|
}
|
||
|
service.Success(ctx, resp)
|
||
|
}
|
||
|
|
||
|
func DirDownload(ctx *gin.Context) {
|
||
|
path := ctx.Query("path")
|
||
|
fileList := strings.Split(ctx.Query("files"), ",")
|
||
|
algo := ctx.Query("algo")
|
||
|
stream, err := service.FilesProvider.DirDownload(ctx, &files.DirDownloadReq{
|
||
|
Algo: algo,
|
||
|
Files: fileList,
|
||
|
Path: path,
|
||
|
UserSpacePath: getUserSpacePath(ctx),
|
||
|
})
|
||
|
if err != nil {
|
||
|
service.Error(ctx, err)
|
||
|
return
|
||
|
}
|
||
|
header, err := stream.Header()
|
||
|
if err != nil {
|
||
|
service.Error(ctx, err)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
ctx.Writer.Header().Set("Content-Disposition", "attachment; filename*=utf-8''"+url.PathEscape(header.Get("filename")[0]))
|
||
|
for {
|
||
|
recvMsg, err := stream.Recv()
|
||
|
if err != nil {
|
||
|
break
|
||
|
}
|
||
|
ctx.Writer.Write(recvMsg.Content)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func getUploadOffset(r *http.Request) (int64, error) {
|
||
|
uploadOffset, err := strconv.ParseInt(r.Header.Get("Upload-Offset"), 10, 64)
|
||
|
if err != nil {
|
||
|
return 0, fmt.Errorf("invalid upload offset: %w", err)
|
||
|
}
|
||
|
return uploadOffset, nil
|
||
|
}
|
||
|
|
||
|
func getUserSpacePath(ctx *gin.Context) string {
|
||
|
user := login.GetUserInfoFromC(ctx)
|
||
|
return strconv.Itoa(int(user.ID))
|
||
|
}
|