fonchain-fiee/pkg/service/file/file.go

255 lines
5.8 KiB
Go
Raw Normal View History

2025-06-03 07:58:52 +00:00
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))
}