2024-01-15 04:54:43 +00:00
|
|
|
package s5
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
2024-01-16 16:31:33 +00:00
|
|
|
"crypto/rand"
|
|
|
|
"encoding/base64"
|
2024-01-16 07:01:18 +00:00
|
|
|
"encoding/hex"
|
2024-01-15 04:54:43 +00:00
|
|
|
"errors"
|
|
|
|
"git.lumeweb.com/LumeWeb/libs5-go/encoding"
|
2024-01-15 19:41:24 +00:00
|
|
|
"git.lumeweb.com/LumeWeb/libs5-go/types"
|
2024-01-16 07:01:18 +00:00
|
|
|
"git.lumeweb.com/LumeWeb/portal/db/models"
|
2024-01-15 04:54:43 +00:00
|
|
|
"git.lumeweb.com/LumeWeb/portal/interfaces"
|
|
|
|
"go.sia.tech/jape"
|
|
|
|
"go.uber.org/zap"
|
|
|
|
"io"
|
|
|
|
"mime/multipart"
|
|
|
|
"net/http"
|
2024-01-15 13:29:09 +00:00
|
|
|
"strings"
|
2024-01-15 04:54:43 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
2024-01-16 16:31:33 +00:00
|
|
|
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"
|
|
|
|
errAccountGenerateChallenge = "Error generating challenge"
|
2024-01-15 04:54:43 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
2024-01-16 16:31:33 +00:00
|
|
|
errUploadingFileErr = errors.New(errUploadingFile)
|
|
|
|
errAccountGenerateChallengeErr = errors.New(errAccountGenerateChallenge)
|
2024-01-15 04:54:43 +00:00
|
|
|
)
|
|
|
|
|
2024-01-16 16:42:50 +00:00
|
|
|
type HttpHandler struct {
|
2024-01-15 04:54:43 +00:00
|
|
|
portal interfaces.Portal
|
|
|
|
}
|
|
|
|
|
2024-01-16 16:42:50 +00:00
|
|
|
func NewHttpHandler(portal interfaces.Portal) *HttpHandler {
|
|
|
|
return &HttpHandler{portal: portal}
|
2024-01-15 04:54:43 +00:00
|
|
|
}
|
|
|
|
|
2024-01-16 16:42:50 +00:00
|
|
|
func (h *HttpHandler) SmallFileUpload(jc jape.Context) {
|
2024-01-15 20:16:48 +00:00
|
|
|
var rs io.ReadSeeker
|
|
|
|
var bufferSize int64
|
2024-01-15 04:54:43 +00:00
|
|
|
|
|
|
|
r := jc.Request
|
2024-01-15 13:29:09 +00:00
|
|
|
contentType := r.Header.Get("Content-Type")
|
|
|
|
|
|
|
|
if strings.HasPrefix(contentType, "multipart/form-data") {
|
2024-01-15 04:54:43 +00:00
|
|
|
// 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)
|
|
|
|
|
2024-01-15 20:16:48 +00:00
|
|
|
rs = file
|
2024-01-15 04:54:43 +00:00
|
|
|
} else {
|
2024-01-15 20:16:48 +00:00
|
|
|
data, err := io.ReadAll(r.Body)
|
2024-01-15 04:54:43 +00:00
|
|
|
if jc.Check(errReadFile, err) != nil {
|
|
|
|
h.portal.Logger().Error(errReadFile, zap.Error(err))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-01-15 20:16:48 +00:00
|
|
|
buffer := bytes.NewReader(data)
|
|
|
|
bufferSize = int64(buffer.Len())
|
|
|
|
rs = buffer
|
|
|
|
|
2024-01-15 04:54:43 +00:00
|
|
|
defer func(Body io.ReadCloser) {
|
|
|
|
err := Body.Close()
|
|
|
|
if err != nil {
|
|
|
|
h.portal.Logger().Error(errClosingStream, zap.Error(err))
|
|
|
|
}
|
|
|
|
}(r.Body)
|
|
|
|
}
|
|
|
|
|
2024-01-16 06:05:09 +00:00
|
|
|
hash, err := h.portal.Storage().GetHash(rs)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
_ = jc.Error(errUploadingFileErr, http.StatusInternalServerError)
|
|
|
|
h.portal.Logger().Error(errUploadingFile, zap.Error(err))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if exists, upload := h.portal.Storage().FileExists(hash); exists {
|
|
|
|
cid, err := encoding.CIDFromHash(hash, upload.Size, types.CIDTypeRaw)
|
|
|
|
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})
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
hash, err = h.portal.Storage().PutFile(rs, "s5", false)
|
2024-01-15 04:54:43 +00:00
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
_ = jc.Error(errUploadingFileErr, http.StatusInternalServerError)
|
|
|
|
h.portal.Logger().Error(errUploadingFile, zap.Error(err))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-01-15 20:16:48 +00:00
|
|
|
cid, err := encoding.CIDFromHash(hash, uint64(bufferSize), types.CIDTypeRaw)
|
2024-01-15 04:54:43 +00:00
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2024-01-16 07:01:18 +00:00
|
|
|
tx := h.portal.Database().Create(&models.Upload{
|
|
|
|
Hash: hex.EncodeToString(hash),
|
|
|
|
Size: uint64(bufferSize),
|
|
|
|
Protocol: "s5",
|
|
|
|
UserID: 0,
|
|
|
|
})
|
|
|
|
|
|
|
|
if tx.Error != nil {
|
|
|
|
_ = jc.Error(errUploadingFileErr, http.StatusInternalServerError)
|
|
|
|
h.portal.Logger().Error(errUploadingFile, zap.Error(err))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-01-15 04:54:43 +00:00
|
|
|
jc.Encode(map[string]string{"hash": cidStr})
|
|
|
|
}
|
2024-01-16 16:31:33 +00:00
|
|
|
|
2024-01-16 16:42:50 +00:00
|
|
|
func (h *HttpHandler) AccountRegisterChallenge(jc jape.Context) {
|
2024-01-16 16:31:33 +00:00
|
|
|
var pubkey string
|
|
|
|
if jc.DecodeForm("pubKey", &pubkey) != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
challenge := make([]byte, 32)
|
|
|
|
|
|
|
|
_, err := rand.Read(challenge)
|
|
|
|
if err != nil {
|
|
|
|
_ = jc.Error(errAccountGenerateChallengeErr, http.StatusInternalServerError)
|
|
|
|
h.portal.Logger().Error(errAccountGenerateChallenge, zap.Error(err))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
decodedKey, err := base64.RawURLEncoding.DecodeString(pubkey)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
_ = jc.Error(errAccountGenerateChallengeErr, http.StatusInternalServerError)
|
|
|
|
h.portal.Logger().Error(errAccountGenerateChallenge, zap.Error(err))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(decodedKey) != 32 {
|
|
|
|
_ = jc.Error(errAccountGenerateChallengeErr, http.StatusInternalServerError)
|
|
|
|
h.portal.Logger().Error(errAccountGenerateChallenge, zap.Error(err))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
result := h.portal.Database().Create(&models.S5Challenge{
|
|
|
|
Challenge: hex.EncodeToString(challenge),
|
|
|
|
})
|
|
|
|
|
|
|
|
if result.Error != nil {
|
|
|
|
_ = jc.Error(errAccountGenerateChallengeErr, http.StatusInternalServerError)
|
|
|
|
h.portal.Logger().Error(errAccountGenerateChallenge, zap.Error(err))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
jc.Encode(map[string]string{"challenge": base64.RawURLEncoding.EncodeToString(challenge)})
|
|
|
|
}
|
|
|
|
|
2024-01-16 16:42:50 +00:00
|
|
|
func (h *HttpHandler) AccountRegister(context jape.Context) {
|
2024-01-16 16:31:33 +00:00
|
|
|
//TODO implement me
|
|
|
|
panic("implement me")
|
|
|
|
}
|
|
|
|
|
2024-01-16 16:42:50 +00:00
|
|
|
func (h *HttpHandler) AccountLoginChallenge(context jape.Context) {
|
2024-01-16 16:31:33 +00:00
|
|
|
//TODO implement me
|
|
|
|
panic("implement me")
|
|
|
|
}
|
|
|
|
|
2024-01-16 16:42:50 +00:00
|
|
|
func (h *HttpHandler) AccountLogin(context jape.Context) {
|
2024-01-16 16:31:33 +00:00
|
|
|
//TODO implement me
|
|
|
|
panic("implement me")
|
|
|
|
}
|