From 908411f33f216dcc54c8195e2e1417519e03b24b Mon Sep 17 00:00:00 2001 From: Derrick Hammer Date: Sun, 14 Jan 2024 23:54:43 -0500 Subject: [PATCH] feat: initial s5 basic upload --- protocols/s5.go | 2 + protocols/s5/http.go | 115 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 117 insertions(+) create mode 100644 protocols/s5/http.go diff --git a/protocols/s5.go b/protocols/s5.go index 4f25e1c..61d61e2 100644 --- a/protocols/s5.go +++ b/protocols/s5.go @@ -8,6 +8,7 @@ import ( s5interfaces "git.lumeweb.com/LumeWeb/libs5-go/interfaces" s5node "git.lumeweb.com/LumeWeb/libs5-go/node" "git.lumeweb.com/LumeWeb/portal/interfaces" + "git.lumeweb.com/LumeWeb/portal/protocols/s5" bolt "go.etcd.io/bbolt" "go.uber.org/zap" ) @@ -82,6 +83,7 @@ func (s *S5Protocol) Initialize(portal interfaces.Portal) error { cfg.DB = db s.node = s5node.NewNode(cfg) + s.node.Services().HTTP().SetHttpHandler(s5.NewHttpHandler(s.portal)) return nil } diff --git a/protocols/s5/http.go b/protocols/s5/http.go new file mode 100644 index 0000000..ce729fd --- /dev/null +++ b/protocols/s5/http.go @@ -0,0 +1,115 @@ +package s5 + +import ( + "bytes" + "errors" + "git.lumeweb.com/LumeWeb/libs5-go/encoding" + s5interface "git.lumeweb.com/LumeWeb/libs5-go/interfaces" + "git.lumeweb.com/LumeWeb/portal/interfaces" + "go.sia.tech/jape" + "go.uber.org/zap" + "io" + "mime/multipart" + "net/http" +) + +var ( + _ s5interface.HTTPHandler = (*HttpHandlerImpl)(nil) +) + +const ( + errMultiformParse = "Error parsing multipart form" + errRetrievingFile = "Error retrieving the file" + errReadFile = "Error reading the file" + errClosingStream = "Error closing the stream" + errUploadingFile = "Error uploading the file" +) + +var ( + errUploadingFileErr = errors.New(errUploadingFile) +) + +type HttpHandlerImpl struct { + portal interfaces.Portal +} + +func NewHttpHandler(portal interfaces.Portal) *HttpHandlerImpl { + return &HttpHandlerImpl{portal: portal} +} + +func (h *HttpHandlerImpl) SmallFileUpload(jc *jape.Context) { + buffer := bytes.NewBuffer(nil) + + r := jc.Request + // Check if the content type is multipart/form-data + if r.Header.Get("Content-Type") == "multipart/form-data" { + // Parse the multipart form + err := r.ParseMultipartForm(h.portal.Config().GetInt64("core.post-upload-limit")) + + if jc.Check(errMultiformParse, err) != nil { + h.portal.Logger().Error(errMultiformParse, zap.Error(err)) + return + } + + // Retrieve the file from the form data + file, _, err := r.FormFile("file") + if jc.Check(errRetrievingFile, err) != nil { + h.portal.Logger().Error(errRetrievingFile, zap.Error(err)) + return + } + defer func(file multipart.File) { + err := file.Close() + if err != nil { + h.portal.Logger().Error(errClosingStream, zap.Error(err)) + } + }(file) + + // Copy file contents to buffer + _, err = io.Copy(buffer, file) + if jc.Check(errReadFile, err) != nil { + h.portal.Logger().Error(errReadFile, zap.Error(err)) + return + } + } else { + // For other content types, read the body into the buffer + _, err := io.Copy(buffer, r.Body) + + if jc.Check(errReadFile, err) != nil { + h.portal.Logger().Error(errReadFile, zap.Error(err)) + return + } + + defer func(Body io.ReadCloser) { + err := Body.Close() + if err != nil { + h.portal.Logger().Error(errClosingStream, zap.Error(err)) + } + }(r.Body) + } + + hash, err := h.portal.Storage().PutFile(interface{}(buffer).(io.ReadSeeker), "test", false) + + if err != nil { + _ = jc.Error(errUploadingFileErr, http.StatusInternalServerError) + h.portal.Logger().Error(errUploadingFile, zap.Error(err)) + return + } + + cid, err := encoding.CIDFromBytes(hash) + + if err != nil { + _ = jc.Error(errUploadingFileErr, http.StatusInternalServerError) + h.portal.Logger().Error(errUploadingFile, zap.Error(err)) + return + } + + cidStr, err := cid.ToString() + + if err != nil { + _ = jc.Error(errUploadingFileErr, http.StatusInternalServerError) + h.portal.Logger().Error(errUploadingFile, zap.Error(err)) + return + } + + jc.Encode(map[string]string{"hash": cidStr}) +}