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)) }