2023-05-10 11:07:56 +00:00
|
|
|
package controller
|
2023-05-04 08:18:38 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"git.lumeweb.com/LumeWeb/portal/cid"
|
2023-06-07 17:17:11 +00:00
|
|
|
"git.lumeweb.com/LumeWeb/portal/controller/response"
|
2023-05-22 23:05:38 +00:00
|
|
|
"git.lumeweb.com/LumeWeb/portal/logger"
|
2023-06-09 08:06:03 +00:00
|
|
|
"git.lumeweb.com/LumeWeb/portal/middleware"
|
2023-05-10 18:40:29 +00:00
|
|
|
"git.lumeweb.com/LumeWeb/portal/service/files"
|
2023-05-04 08:18:38 +00:00
|
|
|
"github.com/kataras/iris/v12"
|
2023-05-19 13:04:47 +00:00
|
|
|
"go.uber.org/zap"
|
2023-05-04 08:18:38 +00:00
|
|
|
"io"
|
|
|
|
)
|
|
|
|
|
2023-06-11 07:19:07 +00:00
|
|
|
var errStreamDone = errors.New("done")
|
|
|
|
|
2023-05-10 11:07:56 +00:00
|
|
|
type FilesController struct {
|
2023-06-09 08:05:19 +00:00
|
|
|
Controller
|
2023-05-04 08:18:38 +00:00
|
|
|
}
|
2023-06-06 20:34:05 +00:00
|
|
|
|
2023-06-09 08:06:03 +00:00
|
|
|
func (f *FilesController) BeginRequest(ctx iris.Context) {
|
2023-06-09 08:16:58 +00:00
|
|
|
middleware.VerifyJwt(ctx)
|
|
|
|
}
|
|
|
|
func (f *FilesController) EndRequest(ctx iris.Context) {
|
2023-06-09 08:06:03 +00:00
|
|
|
}
|
|
|
|
|
2023-05-10 11:07:56 +00:00
|
|
|
func (f *FilesController) PostUpload() {
|
2023-05-04 08:18:38 +00:00
|
|
|
ctx := f.Ctx
|
|
|
|
|
2023-05-04 12:16:44 +00:00
|
|
|
file, meta, err := f.Ctx.FormFile("file")
|
2023-05-04 08:18:38 +00:00
|
|
|
if internalErrorCustom(ctx, err, errors.New("invalid file data")) {
|
2023-05-22 23:05:38 +00:00
|
|
|
logger.Get().Debug("invalid file data", zap.Error(err))
|
2023-05-04 08:18:38 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2023-05-22 15:00:24 +00:00
|
|
|
upload, err := files.Upload(file, meta.Size, nil)
|
2023-05-04 08:18:38 +00:00
|
|
|
|
2023-05-04 13:11:31 +00:00
|
|
|
if internalError(ctx, err) {
|
2023-05-22 23:05:38 +00:00
|
|
|
logger.Get().Debug("failed uploading file", zap.Error(err))
|
2023-05-04 13:11:31 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2023-06-09 11:06:33 +00:00
|
|
|
err = files.Pin(upload.Hash, upload.AccountID)
|
|
|
|
|
|
|
|
if internalError(ctx, err) {
|
|
|
|
logger.Get().Debug("failed pinning file", zap.Error(err))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2023-05-10 18:40:29 +00:00
|
|
|
cidString, err := cid.EncodeString(upload.Hash, uint64(meta.Size))
|
2023-05-04 08:18:38 +00:00
|
|
|
|
|
|
|
if internalError(ctx, err) {
|
2023-05-22 23:05:38 +00:00
|
|
|
logger.Get().Debug("failed creating cid", zap.Error(err))
|
2023-05-04 08:18:38 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2023-06-07 17:17:11 +00:00
|
|
|
err = ctx.JSON(&response.UploadResponse{Cid: cidString})
|
2023-05-19 13:04:47 +00:00
|
|
|
|
|
|
|
if err != nil {
|
2023-05-22 23:05:38 +00:00
|
|
|
logger.Get().Error("failed to create response", zap.Error(err))
|
2023-05-19 13:04:47 +00:00
|
|
|
}
|
2023-05-04 08:18:38 +00:00
|
|
|
}
|
|
|
|
|
2023-05-10 19:09:18 +00:00
|
|
|
func (f *FilesController) GetDownloadBy(cidString string) {
|
2023-05-08 14:10:57 +00:00
|
|
|
ctx := f.Ctx
|
|
|
|
|
2023-06-06 20:34:05 +00:00
|
|
|
hashHex, valid := validateCid(cidString, true, ctx)
|
|
|
|
|
|
|
|
if !valid {
|
2023-05-08 14:10:57 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2023-05-10 18:40:29 +00:00
|
|
|
download, err := files.Download(hashHex)
|
2023-05-08 14:16:47 +00:00
|
|
|
if internalError(ctx, err) {
|
2023-05-22 23:05:38 +00:00
|
|
|
logger.Get().Debug("failed fetching file", zap.Error(err))
|
2023-05-08 14:16:47 +00:00
|
|
|
return
|
|
|
|
}
|
2023-05-08 14:10:57 +00:00
|
|
|
|
2023-06-11 07:19:07 +00:00
|
|
|
err = passThroughStream(download, ctx)
|
|
|
|
if err != errStreamDone && internalError(ctx, err) {
|
2023-05-22 23:05:38 +00:00
|
|
|
logger.Get().Debug("failed streaming file", zap.Error(err))
|
2023-05-19 13:04:47 +00:00
|
|
|
}
|
2023-05-08 14:10:57 +00:00
|
|
|
}
|
|
|
|
|
2023-06-09 19:52:58 +00:00
|
|
|
func (f *FilesController) GetProofBy(cidString string) {
|
|
|
|
ctx := f.Ctx
|
|
|
|
|
|
|
|
hashHex, valid := validateCid(cidString, true, ctx)
|
|
|
|
|
|
|
|
if !valid {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2023-06-11 07:19:07 +00:00
|
|
|
proof, err := files.DownloadProof(hashHex)
|
2023-06-09 19:52:58 +00:00
|
|
|
if internalError(ctx, err) {
|
|
|
|
logger.Get().Debug("failed fetching file proof", zap.Error(err))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2023-06-11 07:19:07 +00:00
|
|
|
err = passThroughStream(proof, ctx)
|
2023-06-09 19:52:58 +00:00
|
|
|
if internalError(ctx, err) {
|
|
|
|
logger.Get().Debug("failed streaming file proof", zap.Error(err))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-06 20:34:05 +00:00
|
|
|
func (f *FilesController) GetStatusBy(cidString string) {
|
|
|
|
ctx := f.Ctx
|
|
|
|
|
|
|
|
hashHex, valid := validateCid(cidString, false, ctx)
|
|
|
|
|
|
|
|
if !valid {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
status := files.Status(hashHex)
|
|
|
|
|
|
|
|
var statusCode string
|
|
|
|
|
|
|
|
switch status {
|
|
|
|
case files.STATUS_UPLOADED:
|
|
|
|
statusCode = "uploaded"
|
|
|
|
break
|
|
|
|
case files.STATUS_UPLOADING:
|
|
|
|
statusCode = "uploading"
|
|
|
|
break
|
|
|
|
case files.STATUS_NOT_FOUND:
|
2023-06-10 06:50:20 +00:00
|
|
|
statusCode = "not_found"
|
2023-06-06 20:34:05 +00:00
|
|
|
break
|
|
|
|
}
|
|
|
|
|
2023-06-15 04:25:38 +00:00
|
|
|
f.respondJSON(&response.FileStatusResponse{Status: statusCode})
|
2023-06-06 20:34:05 +00:00
|
|
|
|
|
|
|
}
|
2023-06-09 11:39:43 +00:00
|
|
|
|
|
|
|
func (f *FilesController) PostPinBy(cidString string) {
|
|
|
|
ctx := f.Ctx
|
|
|
|
|
|
|
|
hashHex, valid := validateCid(cidString, true, ctx)
|
|
|
|
|
|
|
|
if !valid {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2023-06-29 09:41:26 +00:00
|
|
|
err := files.Pin(hashHex, auth.GetCurrentUserId(ctx))
|
2023-06-09 11:39:43 +00:00
|
|
|
if internalError(ctx, err) {
|
|
|
|
logger.Get().Error(err.Error())
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
f.Ctx.StatusCode(iris.StatusCreated)
|
|
|
|
}
|
|
|
|
|
2023-06-10 05:58:45 +00:00
|
|
|
func (f *FilesController) GetUploadLimit() {
|
|
|
|
f.respondJSON(&response.UploadLimit{Limit: f.Ctx.Application().ConfigurationReadOnly().GetPostMaxMemory()})
|
|
|
|
}
|
|
|
|
|
2023-06-06 20:34:05 +00:00
|
|
|
func validateCid(cidString string, validateStatus bool, ctx iris.Context) (string, bool) {
|
|
|
|
_, err := cid.Valid(cidString)
|
|
|
|
if sendError(ctx, err, iris.StatusBadRequest) {
|
|
|
|
logger.Get().Debug("invalid cid", zap.Error(err))
|
|
|
|
return "", false
|
|
|
|
}
|
|
|
|
|
|
|
|
cidObject, _ := cid.Decode(cidString)
|
|
|
|
hashHex := cidObject.StringHash()
|
|
|
|
|
|
|
|
if validateStatus {
|
|
|
|
status := files.Status(hashHex)
|
|
|
|
|
|
|
|
if status == files.STATUS_NOT_FOUND {
|
|
|
|
err := errors.New("cid not found")
|
|
|
|
sendError(ctx, errors.New("cid not found"), iris.StatusNotFound)
|
|
|
|
logger.Get().Debug("cid not found", zap.Error(err))
|
|
|
|
return "", false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return hashHex, true
|
|
|
|
}
|
2023-06-11 07:19:07 +00:00
|
|
|
|
|
|
|
func passThroughStream(stream io.Reader, ctx iris.Context) error {
|
|
|
|
closed := false
|
|
|
|
|
|
|
|
err := ctx.StreamWriter(func(w io.Writer) error {
|
|
|
|
if closed {
|
|
|
|
return errStreamDone
|
|
|
|
}
|
|
|
|
|
|
|
|
count, err := io.CopyN(w, stream, 1024)
|
|
|
|
if count == 0 || err == io.EOF {
|
|
|
|
err = stream.(io.Closer).Close()
|
|
|
|
if err != nil {
|
|
|
|
logger.Get().Error("failed closing stream", zap.Error(err))
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
closed = true
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
if err == errStreamDone {
|
|
|
|
err = nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return err
|
|
|
|
}
|