mirror of
https://github.com/PeernetOfficial/WebGatewayUpload.git
synced 2026-07-17 03:57:51 +01:00
557 lines
16 KiB
Go
557 lines
16 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/hex"
|
|
"encoding/json"
|
|
"errors"
|
|
"flag"
|
|
"fmt"
|
|
"github.com/Akilan1999/p2p-rendering-computation/p2p/frp"
|
|
"github.com/PeernetOfficial/core"
|
|
"github.com/PeernetOfficial/core/btcec"
|
|
"github.com/PeernetOfficial/core/webapi"
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/google/uuid"
|
|
limiter "github.com/julianshen/gin-limiter"
|
|
"io"
|
|
"io/ioutil"
|
|
"log"
|
|
"mime/multipart"
|
|
"net"
|
|
"net/http"
|
|
"time"
|
|
)
|
|
|
|
// Variables for the flags to get the address
|
|
var (
|
|
// BackEndApiAddress Refers to the Peernet address ex: <address>:<port no>
|
|
BackEndApiAddress *string
|
|
// WebpageAddress Refers to the address for upload webgate server
|
|
WebpageAddress *string
|
|
// SSL To ensure SSL is required checks if SSL is required and subsequently
|
|
// checks if the certificate is provided
|
|
SSL *bool
|
|
// Certificate SSL Certificate file
|
|
Certificate *string
|
|
// Key SSL Key file
|
|
Key *string
|
|
// BackendAddressWithHTTP ex: http://<address>:<port no>
|
|
BackendAddressWithHTTP string
|
|
// Production mode
|
|
Production *bool
|
|
// P2PRC mode
|
|
P2PRC *bool
|
|
// P2PRCRootNode Root node of P2PRC
|
|
P2PRCRootNode *string
|
|
// P2PRCExposePort Public exposed port
|
|
P2PRCExposePort *string
|
|
// DownloadGatewayURL Download Gateway URL
|
|
DownloadGatewayURL *string
|
|
)
|
|
|
|
// -------------------------------------------------------------------------------------------------------------
|
|
// ---------------------------------------- Initialize flags and run Peernet ---------------------------------
|
|
// -------------------------------------------------------------------------------------------------------------
|
|
|
|
// init reading flags before any part of the code is executed
|
|
func init() {
|
|
BackEndApiAddress = flag.String("BackEndApiAddress", "localhost:8081", "current environment")
|
|
WebpageAddress = flag.String("WebpageAddress", "localhost:8098", "current environment")
|
|
SSL = flag.Bool("SSL", false, "Flag to check if the SSL certificate is enabled or not")
|
|
Certificate = flag.String("Certificate", "server.crt", "SSL Certificate file")
|
|
Key = flag.String("Key", "server.key", "SSL Key file")
|
|
Production = flag.Bool("Production", false, "Flag to check if required to run on production mode")
|
|
P2PRC = flag.Bool("P2PRC", false, "Run P2PRC mode to selfnode node")
|
|
P2PRCRootNode = flag.String("P2PRCHost", "", "Run P2PRC mode to selfnode node")
|
|
P2PRCExposePort = flag.String("P2PRCExposedPort", "", "Port exposed externally for P2PRC")
|
|
DownloadGatewayURL = flag.String("DownloadGatewayURL", "localhost:8081", "Download gateway URL (Start with http: or https:)")
|
|
}
|
|
|
|
// InitPeernet Initializes Peernet backend
|
|
func InitPeernet() *core.Backend {
|
|
backend, status, err := core.Init("Peernet Upload Application/1.0", "Config.yaml", nil, nil)
|
|
if status != core.ExitSuccess {
|
|
fmt.Printf("Error %d initializing backend: %s\n", status, err.Error())
|
|
return nil
|
|
}
|
|
|
|
return backend
|
|
}
|
|
|
|
// RunPeernet Starts the WebAPI and peernet
|
|
func RunPeernet(backend *core.Backend) {
|
|
webapi.Start(backend, []string{*BackEndApiAddress}, false, "", "", 200000*time.Second, 200000*time.Second, uuid.Nil)
|
|
backend.Connect()
|
|
|
|
for {
|
|
|
|
}
|
|
}
|
|
|
|
// -------------------------------------------------------------------------------------------------------------
|
|
// ---------------------- Custom Structs required parse information from the Peernet backend API ---------------
|
|
// -------------------------------------------------------------------------------------------------------------
|
|
// 1. Storing file in the warehouse
|
|
// 2. Storing file metadata in the blockchain
|
|
|
|
// -----------------------------------------------------------------------------------------
|
|
// ---------------------------------- Warehouse related structs ---------------------------
|
|
|
|
type WarehouseResult struct {
|
|
Status int `json:"status"`
|
|
Hash []byte `json:"hash"`
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------------------
|
|
// -------------------------------- Blockchain related structs -----------------------------
|
|
|
|
// BlockchainRequest blockchain backend API request struct
|
|
type BlockchainRequest struct {
|
|
Files []File `json:"files"`
|
|
}
|
|
|
|
type File struct {
|
|
Hash []byte `json:"hash"`
|
|
Type uint16 `json:"type"`
|
|
Name string `json:"name"`
|
|
FileFormat uint16 `json:"format"`
|
|
}
|
|
|
|
// BlockchainResponse blockchain backend API response struct
|
|
type BlockchainResponse struct {
|
|
Status int `json:"status"`
|
|
Height int `json:"height"`
|
|
Version int `json:"version"`
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------------------
|
|
// -----------------------------------------------------------------------------------------
|
|
|
|
// -------------------------------------------------------------------------------------------------------------
|
|
// -------------------------------------- Functions to call Peernet Apis ---------------------------------------
|
|
// -------------------------------------------------------------------------------------------------------------
|
|
// 1. Storing file in the warehouse (/warehouse/create)
|
|
// 2. Storing file metadata in the blockchain (/blockchain/file/add)
|
|
|
|
// -----------------------------------------------------------------------------------------
|
|
// ---------------------------------- Warehouse related ------------------------------------
|
|
|
|
// AddFileWarehouse API call for (Storing file in the warehouse)
|
|
//func AddFileWarehouse(file io.Reader, id string) *WarehouseResult {
|
|
// route := BackendAddressWithHTTP + "/warehouse/create"
|
|
//
|
|
// form := url.Values{}
|
|
// form.Add("file", file)
|
|
// form.Add("ip", c.ip)
|
|
// form.Add("ua", c.ua)
|
|
// req.PostForm = form
|
|
//
|
|
// req, err := http.NewRequest("POST", route, file)
|
|
// //req.Header.Set("X-Custom-Header", "myvalue")
|
|
// req.Header.Set("Content-Type", "multipart/form-data")
|
|
//
|
|
// client := &http.Client{}
|
|
// resp, err := client.Do(req)
|
|
// if err != nil {
|
|
// panic(err)
|
|
// }
|
|
// defer resp.Body.Close()
|
|
//
|
|
// body, err := ioutil.ReadAll(resp.Body)
|
|
// if err != nil {
|
|
// fmt.Println(err)
|
|
// }
|
|
// var result WarehouseResult
|
|
// err = json.Unmarshal(body, &result)
|
|
// if err != nil {
|
|
// fmt.Println(err)
|
|
// }
|
|
//
|
|
// return &result
|
|
//}
|
|
|
|
func AddFileWarehouse(File io.Reader, uuid string) (resp *WarehouseResult, err error) {
|
|
//// Prepare a form that you will submit to that URL.
|
|
//var b bytes.Buffer
|
|
//w := multipart.NewWriter(&b)
|
|
//for key, r := range values {
|
|
// var fw io.Writer
|
|
// if x, ok := r.(io.Closer); ok {
|
|
// defer x.Close()
|
|
// }
|
|
// // Add an image file
|
|
// if x, ok := r.(*os.File); ok {
|
|
// if fw, err = w.CreateFormFile(key, x.Name()); err != nil {
|
|
// return
|
|
// }
|
|
// } else {
|
|
// // Add other fields
|
|
// if fw, err = w.CreateFormField(key); err != nil {
|
|
// return
|
|
// }
|
|
// }
|
|
// if _, err = io.Copy(fw, r); err != nil {
|
|
// return nil, err
|
|
// }
|
|
//
|
|
//}
|
|
//// Don't forget to close the multipart writer.
|
|
//// If you don't close it, your request will be missing the terminating boundary.
|
|
//w.Close()
|
|
//
|
|
//// Now that you have a form, you can submit it to your handler.
|
|
//req, err := http.NewRequest("POST", BackendAddressWithHTTP+"/warehouse/create", &b)
|
|
//if err != nil {
|
|
// return
|
|
//}
|
|
//// Don't forget to set the content type, this will contain the boundary.
|
|
//req.Header.Set("Content-Type", w.FormDataContentType())
|
|
//
|
|
//client := &http.Client{}
|
|
//
|
|
//// Submit the request
|
|
//res, err := client.Do(req)
|
|
//if err != nil {
|
|
// return
|
|
//}
|
|
//
|
|
//// Check the response
|
|
//if res.StatusCode != http.StatusOK {
|
|
// err = fmt.Errorf("bad status: %s", res.Status)
|
|
//}
|
|
//
|
|
//body, err := ioutil.ReadAll(res.Body)
|
|
//if err != nil {
|
|
// return
|
|
//}
|
|
//
|
|
//err = json.Unmarshal(body, &resp)
|
|
//if err != nil {
|
|
// return
|
|
//}
|
|
//
|
|
//fmt.Println(resp)
|
|
//
|
|
//return
|
|
|
|
url := BackendAddressWithHTTP + "/warehouse/create"
|
|
method := "POST"
|
|
|
|
payload := &bytes.Buffer{}
|
|
writer := multipart.NewWriter(payload)
|
|
_ = writer.WriteField("id", uuid)
|
|
part2, errFile2 := writer.CreateFormFile("File", "test")
|
|
_, errFile2 = io.Copy(part2, File)
|
|
if errFile2 != nil {
|
|
fmt.Println(errFile2)
|
|
return
|
|
}
|
|
err = writer.Close()
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
return
|
|
}
|
|
|
|
client := &http.Client{}
|
|
req, err := http.NewRequest(method, url, payload)
|
|
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
return
|
|
}
|
|
req.Header.Set("Content-Type", writer.FormDataContentType())
|
|
res, err := client.Do(req)
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
return
|
|
}
|
|
defer res.Body.Close()
|
|
|
|
body, err := ioutil.ReadAll(res.Body)
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
return
|
|
}
|
|
|
|
err = json.Unmarshal(body, &resp)
|
|
if err != nil {
|
|
return
|
|
}
|
|
return
|
|
}
|
|
|
|
// UploadFile Simple abstracted function to add files to peernet core
|
|
func UploadFile(backend *core.Backend, file *multipart.File, header *multipart.FileHeader, uuid string) (*btcec.PublicKey, *WarehouseResult, error) {
|
|
buf := bytes.NewBuffer(nil)
|
|
if _, err := io.Copy(buf, *file); err != nil {
|
|
return nil, nil, errors.New("io.Copy not successful")
|
|
}
|
|
|
|
// adds file to warehouse
|
|
warehouseResult, err := AddFileWarehouse(buf, uuid)
|
|
if err != nil {
|
|
fmt.Println(err.Error())
|
|
return nil, nil, err
|
|
}
|
|
// current using default port for Peernet api which is 8080
|
|
// First add file to warehouse
|
|
|
|
// Adds the file to a blockchain
|
|
Blockchainfo := AddFileToBlockchain(warehouseResult.Hash, header.Filename)
|
|
if Blockchainfo == nil {
|
|
return nil, nil, errors.New("add file to blockchain not successful")
|
|
}
|
|
|
|
_, publicKey := backend.ExportPrivateKey()
|
|
|
|
return publicKey, warehouseResult, nil
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------------------
|
|
// ----------------------------------- Blockchain related ---------------------------------
|
|
|
|
// AddFileToBlockchain The follwoing function adds the filename and hash to the blockchain
|
|
func AddFileToBlockchain(hash []byte, filename string) *BlockchainResponse {
|
|
url := BackendAddressWithHTTP + "/blockchain/file/add"
|
|
|
|
// Get file type
|
|
detectType, fileFormat, err := webapi.FileDetectType(filename)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
// Create file object for post
|
|
var blockchainRequest BlockchainRequest
|
|
var files File
|
|
files.Name = filename
|
|
files.Hash = hash
|
|
files.Type = detectType
|
|
files.FileFormat = fileFormat
|
|
blockchainRequest.Files = append(blockchainRequest.Files, files)
|
|
|
|
Byte, err := json.Marshal(blockchainRequest)
|
|
|
|
// convert bytes
|
|
req, err := http.NewRequest("POST", url, bytes.NewBuffer(Byte))
|
|
//req.Header.Set("X-Custom-Header", "myvalue")
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
client := &http.Client{}
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
body, _ := ioutil.ReadAll(resp.Body)
|
|
|
|
var result BlockchainResponse
|
|
err = json.Unmarshal(body, &result)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
|
|
fmt.Println(result)
|
|
|
|
return &result
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------------------------
|
|
// ----------------------------------- Get status upload file ---------------------------------
|
|
|
|
func GetStatusUploadFile(uuid string) (uploadStatus *webapi.UploadStatus) {
|
|
url := BackendAddressWithHTTP + "/create/track/uploadID?id=" + uuid
|
|
|
|
resp, err := http.Get(url)
|
|
if err != nil {
|
|
log.Fatalln(err)
|
|
}
|
|
//We Read the response body on the line below.
|
|
body, err := ioutil.ReadAll(resp.Body)
|
|
if err != nil {
|
|
log.Fatalln(err)
|
|
}
|
|
|
|
err = json.Unmarshal(body, uploadStatus)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
|
|
fmt.Println(uploadStatus.Progress.Percentage)
|
|
|
|
return
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------------------
|
|
// -----------------------------------------------------------------------------------------
|
|
|
|
// -------------------------------------------------------------------------------------------------------------
|
|
// --------------------------------------------- Main function -------------------------------------------------
|
|
// -------------------------------------------------------------------------------------------------------------
|
|
|
|
func main() {
|
|
// Parsing flags
|
|
flag.Parse()
|
|
|
|
// Start peernet
|
|
backend := InitPeernet()
|
|
go RunPeernet(backend)
|
|
|
|
var r *gin.Engine
|
|
if *Production {
|
|
gin.SetMode(gin.ReleaseMode)
|
|
r = gin.New()
|
|
} else {
|
|
r = gin.Default()
|
|
}
|
|
|
|
r.Use(CORSMiddleware())
|
|
|
|
r.LoadHTMLGlob("templates/*.html")
|
|
r.Static("/templates", "./templates")
|
|
|
|
// --------------------------------- Middleware rate limiter -----------------------------------
|
|
lm := limiter.NewRateLimiter(time.Minute, 10, func(ctx *gin.Context) (string, error) {
|
|
return "", nil
|
|
})
|
|
// ---------------------------------------------------------------------------------------------
|
|
|
|
// ---------------------------------------- Routes ---------------------------------------------
|
|
// GET /upload to open upload page from webgateway
|
|
r.GET("/", lm.Middleware(), func(c *gin.Context) {
|
|
c.HTML(http.StatusOK, "upload.html", gin.H{"backendUrl": *BackEndApiAddress})
|
|
})
|
|
|
|
// POST /uploadFile Uploads file to peernet from Webgateway
|
|
r.POST("/upload", lm.Middleware(), func(c *gin.Context) {
|
|
file, header, err := c.Request.FormFile("file")
|
|
defer file.Close()
|
|
|
|
uuid := c.Request.FormValue("uuid")
|
|
|
|
if err != nil {
|
|
c.HTML(http.StatusBadRequest, "upload.html", gin.H{
|
|
"error": err,
|
|
})
|
|
return
|
|
}
|
|
|
|
publicKey, warehouseResult, err := UploadFile(backend, &file, header, uuid)
|
|
if err != nil {
|
|
c.HTML(http.StatusBadRequest, "upload.html", gin.H{
|
|
"backendUrl": *BackEndApiAddress,
|
|
"error": err,
|
|
})
|
|
return
|
|
//fmt.Println(err)
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"hash": hex.EncodeToString(warehouseResult.Hash),
|
|
"filename": header.Filename,
|
|
"size": header.Size,
|
|
"link": *DownloadGatewayURL + "/" + hex.EncodeToString(publicKey.SerializeCompressed()) + "/" + hex.EncodeToString(warehouseResult.Hash) + "/?filename=" + header.Filename,
|
|
"address": *WebpageAddress,
|
|
"backendUrl": *BackEndApiAddress,
|
|
})
|
|
|
|
})
|
|
|
|
r.GET("/uploadStatus", lm.Middleware(), func(c *gin.Context) {
|
|
|
|
uuid := c.Query("uuid")
|
|
|
|
c.JSON(http.StatusOK, GetStatusUploadFile(uuid))
|
|
|
|
})
|
|
// Implement CURL script to ensure linux users can upload directly
|
|
// the Cli like https://bashupload.com
|
|
// Ex: curl http://localhost:8080/uploadCurl -F add=@<file name>
|
|
//r.POST("/uploadCurl", lm.Middleware(), func(c *gin.Context) {
|
|
// file, header, err := c.Request.FormFile("add")
|
|
// defer file.Close()
|
|
//
|
|
// if err != nil {
|
|
// fmt.Println(err)
|
|
// }
|
|
//
|
|
// publicKey, warehouseResult, err := UploadFile(backend, &file, header, "")
|
|
// if err != nil {
|
|
// fmt.Println(err)
|
|
// }
|
|
//
|
|
// link := "http://peer.ae/" + hex.EncodeToString(publicKey.SerializeCompressed()) + "/" + hex.EncodeToString(warehouseResult.Hash)
|
|
// c.Data(http.StatusOK, "plain/text", []byte(link))
|
|
//})
|
|
|
|
// ---------------------------------------------------------------------------------------------
|
|
|
|
// ----------------------- Check if P2PRC mode is selected for hosting ------------------------
|
|
if *P2PRC && *P2PRCRootNode != "" && *P2PRCExposePort != "" {
|
|
EscapeNATWebGateway()
|
|
}
|
|
|
|
// ---------------------------------- Start Gin server -----------------------------------------
|
|
// check if SSL is used or not
|
|
if *SSL {
|
|
BackendAddressWithHTTP = "http://" + *BackEndApiAddress
|
|
err := r.RunTLS(*WebpageAddress, *Certificate, *Key)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
*WebpageAddress = "https://" + *WebpageAddress
|
|
} else {
|
|
BackendAddressWithHTTP = "http://" + *BackEndApiAddress
|
|
r.Run(*WebpageAddress)
|
|
*WebpageAddress = "http://" + *WebpageAddress
|
|
}
|
|
// ---------------------------------------------------------------------------------------------
|
|
}
|
|
|
|
func EscapeNATWebGateway() (ExposePortP2PRC string, err error) {
|
|
host, port, err := net.SplitHostPort(*P2PRCRootNode)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
serverPort, err := frp.GetFRPServerPort("http://" + host + ":" + port)
|
|
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
time.Sleep(1 * time.Second)
|
|
|
|
_, port, err = net.SplitHostPort(*WebpageAddress)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
//port for the barrierKVM server
|
|
ExposePortP2PRC, err = frp.StartFRPClientForServer(host, serverPort, port, *P2PRCExposePort)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
// CORSMiddleware Use Cors middleware
|
|
func CORSMiddleware() gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
|
|
c.Header("Access-Control-Allow-Origin", "*")
|
|
c.Header("Access-Control-Allow-Credentials", "true")
|
|
c.Header("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With")
|
|
c.Header("Access-Control-Allow-Methods", "POST,HEAD,PATCH, OPTIONS, GET, PUT")
|
|
|
|
if c.Request.Method == "OPTIONS" {
|
|
c.AbortWithStatus(204)
|
|
return
|
|
}
|
|
|
|
c.Next()
|
|
}
|
|
}
|