mirror of
https://github.com/PeernetOfficial/core.git
synced 2026-07-17 02:47:51 +01:00
New features (#110)
* added upload status * added changes for progress bar with more logs and bug fixes, Documentation yet to be added * huge changes that need more doucmenting * added possibility to get profile using NodeID * added fix profile listing user profile information * removed profile image from the explore reult struct * saving current changes * added filter to search based on NodeID * Monday bug fixing * updates to the profile * changes for tracing the blockchain profile image not shown * added condition to ensure TAG is not sent and removed debug prints * updated webapi docs
This commit is contained in:
committed by
GitHub
parent
97c3001596
commit
ae5f1d2fd7
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
File Name: API.go
|
||||
File Username: API.go
|
||||
Copyright: 2021 Peernet Foundation s.r.o.
|
||||
Author: Peter Kleissner
|
||||
*/
|
||||
@@ -36,6 +36,17 @@ type WebapiInstance struct {
|
||||
// download info
|
||||
downloads map[uuid.UUID]*downloadInfo
|
||||
downloadsMutex sync.RWMutex
|
||||
|
||||
// upload info
|
||||
uploads map[uuid.UUID]*UploadStatus
|
||||
uploadsMutex sync.RWMutex
|
||||
}
|
||||
|
||||
// API error
|
||||
// This follows the same format as the logger
|
||||
type errorResponse struct {
|
||||
function string
|
||||
error string
|
||||
}
|
||||
|
||||
// WSUpgrader is used for websocket functionality. It allows all requests.
|
||||
@@ -62,6 +73,7 @@ func Start(Backend *core.Backend, ListenAddresses []string, UseSSL bool, Certifi
|
||||
AllowKeyInParam: []string{"/file/read", "/file/view"},
|
||||
allJobs: make(map[uuid.UUID]*SearchJob),
|
||||
downloads: make(map[uuid.UUID]*downloadInfo),
|
||||
uploads: make(map[uuid.UUID]*UploadStatus),
|
||||
}
|
||||
|
||||
if APIKey != uuid.Nil {
|
||||
@@ -96,7 +108,9 @@ func Start(Backend *core.Backend, ListenAddresses []string, UseSSL bool, Certifi
|
||||
api.Router.HandleFunc("/download/start", api.apiDownloadStart).Methods("GET")
|
||||
api.Router.HandleFunc("/download/status", api.apiDownloadStatus).Methods("GET")
|
||||
api.Router.HandleFunc("/download/action", api.apiDownloadAction).Methods("GET")
|
||||
api.Router.HandleFunc("/warehouse/create", api.apiWarehouseCreateFile).Methods("POST")
|
||||
api.Router.HandleFunc("/warehouse/create", api.ApiWarehouseCreateFile).Methods("POST")
|
||||
api.Router.HandleFunc("/warehouse/create/uploadID", api.apiUploadID).Methods("GET")
|
||||
api.Router.HandleFunc("/warehouse/create/track/uploadID", api.apiUploadInfo).Methods("GET")
|
||||
api.Router.HandleFunc("/warehouse/create/path", api.apiWarehouseCreateFilePath).Methods("GET")
|
||||
api.Router.HandleFunc("/warehouse/read", api.apiWarehouseReadFile).Methods("GET")
|
||||
api.Router.HandleFunc("/warehouse/read/path", api.apiWarehouseReadFilePath).Methods("GET")
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
File Name: Blockchain.go
|
||||
File Username: Blockchain.go
|
||||
Copyright: 2021 Peernet Foundation s.r.o.
|
||||
Author: Peter Kleissner
|
||||
*/
|
||||
@@ -8,10 +8,9 @@ package webapi
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"github.com/PeernetOfficial/core/blockchain"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/PeernetOfficial/core/blockchain"
|
||||
)
|
||||
|
||||
type apiBlockchainHeader struct {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
File Name: Download Transfer.go
|
||||
File Username: Download Transfer.go
|
||||
Copyright: 2021 Peernet Foundation s.r.o.
|
||||
Author: Peter Kleissner
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
File Name: Download.go
|
||||
File Username: Download.go
|
||||
Copyright: 2021 Peernet Foundation s.r.o.
|
||||
Author: Peter Kleissner
|
||||
*/
|
||||
@@ -78,6 +78,8 @@ func (api *WebapiInstance) apiDownloadStart(w http.ResponseWriter, r *http.Reque
|
||||
|
||||
info := &downloadInfo{backend: api.Backend, api: api, id: uuid.New(), created: time.Now(), hash: hash, nodeID: nodeID}
|
||||
|
||||
api.Backend.LogError("Download.DownloadStart", "output %v", downloadInfo{backend: api.Backend, api: api, id: uuid.New(), created: time.Now(), hash: hash, nodeID: nodeID})
|
||||
|
||||
// create the file immediately
|
||||
if info.initDiskFile(filePath) != nil {
|
||||
EncodeJSON(api.Backend, w, r, apiResponseDownloadStatus{APIStatus: DownloadResponseFileInvalid})
|
||||
@@ -90,6 +92,8 @@ func (api *WebapiInstance) apiDownloadStart(w http.ResponseWriter, r *http.Reque
|
||||
// start the download!
|
||||
go info.Start()
|
||||
|
||||
api.Backend.LogError("Download.DownloadStart", "output %v", apiResponseDownloadStatus{APIStatus: DownloadResponseSuccess, ID: info.id, DownloadStatus: DownloadWaitMetadata})
|
||||
|
||||
EncodeJSON(api.Backend, w, r, apiResponseDownloadStatus{APIStatus: DownloadResponseSuccess, ID: info.id, DownloadStatus: DownloadWaitMetadata})
|
||||
}
|
||||
|
||||
@@ -132,6 +136,8 @@ func (api *WebapiInstance) apiDownloadStatus(w http.ResponseWriter, r *http.Requ
|
||||
|
||||
info.RUnlock()
|
||||
|
||||
api.Backend.LogError("Download.DownloadStatus", "output %v", response)
|
||||
|
||||
EncodeJSON(api.Backend, w, r, response)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
File Name: File Detection.go
|
||||
File: File Detection.go
|
||||
Copyright: 2021 Peernet Foundation s.r.o.
|
||||
Author: Peter Kleissner
|
||||
*/
|
||||
@@ -94,6 +94,9 @@ func FileTranslateExtension(extension string) (fileType, fileFormat uint16) {
|
||||
case "iso":
|
||||
return core.TypeContainer, core.FormatISO
|
||||
|
||||
case "pnsearch":
|
||||
return core.TypeText, core.FormatPeernetSearch
|
||||
|
||||
default:
|
||||
return core.TypeBinary, core.FormatBinary
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
File Name: File IO.go
|
||||
File Username: File IO.go
|
||||
Copyright: 2021 Peernet Foundation s.r.o.
|
||||
Author: Peter Kleissner
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
File Name: File.go
|
||||
File Username: File.go
|
||||
Copyright: 2021 Peernet Foundation s.r.o.
|
||||
Author: Peter Kleissner
|
||||
*/
|
||||
@@ -8,6 +8,7 @@ package webapi
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/PeernetOfficial/core"
|
||||
@@ -42,14 +43,18 @@ type apiFile struct {
|
||||
Date time.Time `json:"date"` // Date shared
|
||||
NodeID []byte `json:"nodeid"` // Node ID, owner of the file. Read only.
|
||||
Metadata []apiFileMetadata `json:"metadata"` // Additional metadata.
|
||||
Username string `json:"username"` // Username of the user who uploaded the file
|
||||
}
|
||||
|
||||
// --- conversion from core to API data ---
|
||||
|
||||
func blockRecordFileToAPI(input blockchain.BlockRecordFile) (output apiFile) {
|
||||
output = apiFile{ID: input.ID, Hash: input.Hash, NodeID: input.NodeID, Type: input.Type, Format: input.Format, Size: input.Size, Metadata: []apiFileMetadata{}}
|
||||
output = apiFile{ID: input.ID, Hash: input.Hash, NodeID: input.NodeID, Type: input.Type, Format: input.Format, Size: input.Size, Username: input.Username, Metadata: []apiFileMetadata{}}
|
||||
|
||||
for _, tag := range input.Tags {
|
||||
if tag.Type == blockchain.TagSharedByCount && tag.Number() == 0 {
|
||||
return apiFile{}
|
||||
}
|
||||
switch tag.Type {
|
||||
case blockchain.TagName:
|
||||
output.Name = tag.Text()
|
||||
@@ -69,6 +74,10 @@ func blockRecordFileToAPI(input blockchain.BlockRecordFile) (output apiFile) {
|
||||
|
||||
case blockchain.TagSharedByCount:
|
||||
output.Metadata = append(output.Metadata, apiFileMetadata{Type: tag.Type, Name: "Shared By Count", Number: tag.Number()})
|
||||
// if a file has 0 peers sharing then do not add it to the list.
|
||||
if tag.Number() == 0 {
|
||||
return apiFile{}
|
||||
}
|
||||
|
||||
case blockchain.TagSharedByGeoIP:
|
||||
output.Metadata = append(output.Metadata, apiFileMetadata{Type: tag.Type, Name: "Shared By GeoIP", Text: tag.Text()})
|
||||
@@ -136,6 +145,7 @@ Response: 200 with JSON structure apiBlockchainBlockStatus
|
||||
func (api *WebapiInstance) apiBlockchainFileAdd(w http.ResponseWriter, r *http.Request) {
|
||||
var input apiBlockAddFiles
|
||||
if err := DecodeJSON(w, r, &input); err != nil {
|
||||
api.Backend.LogError("blockchain.AddFile", "error: %v", "error decoding JSON")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -143,6 +153,8 @@ func (api *WebapiInstance) apiBlockchainFileAdd(w http.ResponseWriter, r *http.R
|
||||
|
||||
for _, file := range input.Files {
|
||||
if len(file.Hash) != protocol.HashSize {
|
||||
api.Backend.LogError("blockchain.AddFile", "error: %v", "file length is not the same length as "+
|
||||
"the protocol hash size.")
|
||||
http.Error(w, "", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
@@ -153,6 +165,7 @@ func (api *WebapiInstance) apiBlockchainFileAdd(w http.ResponseWriter, r *http.R
|
||||
// Verify that the file exists in the warehouse. Folders are exempt from this check as they are only virtual.
|
||||
if !file.IsVirtualFolder() {
|
||||
if _, err := warehouse.ValidateHash(file.Hash); err != nil {
|
||||
api.Backend.LogError("blockchain.AddFile", "error: %v", err)
|
||||
http.Error(w, "", http.StatusBadRequest)
|
||||
return
|
||||
} else if _, fileSize, status, _ := api.Backend.UserWarehouse.FileExists(file.Hash); status != warehouse.StatusOK {
|
||||
@@ -179,22 +192,37 @@ func (api *WebapiInstance) apiBlockchainFileAdd(w http.ResponseWriter, r *http.R
|
||||
|
||||
newHeight, newVersion, status := api.Backend.UserBlockchain.AddFiles(filesAdd)
|
||||
|
||||
// Temporary log to check the output for warehouse API
|
||||
api.Backend.LogError("blockchain.AddFile", "output %v", apiBlockchainBlockStatus{Status: status, Height: newHeight, Version: newVersion})
|
||||
|
||||
EncodeJSON(api.Backend, w, r, apiBlockchainBlockStatus{Status: status, Height: newHeight, Version: newVersion})
|
||||
}
|
||||
|
||||
/*
|
||||
apiBlockchainFileList lists all files stored on the blockchain.
|
||||
|
||||
Request: GET /blockchain/file/list
|
||||
Request: GET /blockchain/file/list?fileFormat=<file format>
|
||||
Response: 200 with JSON structure apiBlockAddFiles
|
||||
*/
|
||||
func (api *WebapiInstance) apiBlockchainFileList(w http.ResponseWriter, r *http.Request) {
|
||||
files, status := api.Backend.UserBlockchain.ListFiles()
|
||||
|
||||
r.ParseForm()
|
||||
// filter based on file type
|
||||
fileType, err := strconv.Atoi(r.Form.Get("fileFormat"))
|
||||
|
||||
var result apiBlockAddFiles
|
||||
|
||||
for _, file := range files {
|
||||
result.Files = append(result.Files, blockRecordFileToAPI(file))
|
||||
ApiFile := blockRecordFileToAPI(file)
|
||||
if ApiFile.NodeID == nil {
|
||||
continue
|
||||
}
|
||||
if ApiFile.Format == uint16(fileType) {
|
||||
result.Files = append(result.Files, ApiFile)
|
||||
} else if err != nil {
|
||||
result.Files = append(result.Files, ApiFile)
|
||||
}
|
||||
}
|
||||
|
||||
result.Status = status
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
File Name: GeoIP.go
|
||||
File Username: GeoIP.go
|
||||
Copyright: 2021 Peernet Foundation s.r.o.
|
||||
Author: Peter Kleissner
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
File Name: HTTP Range.go
|
||||
File Username: HTTP Range.go
|
||||
Copyright: 2021 Peernet Foundation s.r.o.
|
||||
Author: Peter Kleissner
|
||||
*/
|
||||
|
||||
@@ -69,7 +69,11 @@ func (api *WebapiInstance) ExploreFileSharedByNodeThatSharedSimilarFile(fileType
|
||||
|
||||
// loop over results
|
||||
for n := range resultFiles {
|
||||
result.Files = append(result.Files, blockRecordFileToAPI(resultFiles[n]))
|
||||
ApiFile := blockRecordFileToAPI(resultFiles[n])
|
||||
if ApiFile.NodeID == nil {
|
||||
continue
|
||||
}
|
||||
result.Files = append(result.Files, ApiFile)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -86,8 +90,6 @@ func (api *WebapiInstance) GreedySearchMergeDirection(nodeID *[][]byte, fileType
|
||||
// get all NodeIDs
|
||||
peerList := api.Backend.PeerlistGet()
|
||||
|
||||
//var tags []blockchain.BlockRecordFileTag
|
||||
|
||||
// search with AllNodes which have a match of the NodeID.
|
||||
for _, peer := range peerList {
|
||||
if peer.BlockchainHeight == 0 {
|
||||
@@ -112,28 +114,18 @@ func (api *WebapiInstance) GreedySearchMergeDirection(nodeID *[][]byte, fileType
|
||||
file.Tags = append(file.Tags, blockchain.TagFromText(blockchain.TagSharedByGeoIP, sharedByGeoIP))
|
||||
}
|
||||
|
||||
// found a new file! append.
|
||||
filesFromPeer++
|
||||
//file.Username = Name
|
||||
|
||||
// if both the hashes match
|
||||
if bytes.Equal(file.Hash, hash) {
|
||||
// set the Tags needed for the filter parameter
|
||||
//tags = file.Tags
|
||||
|
||||
// found a new file! append.
|
||||
filesFromPeer++
|
||||
|
||||
*nodeID = append(*nodeID, file.NodeID)
|
||||
}
|
||||
|
||||
//for _, tag := range file.Tags {
|
||||
// // checks if any of tags from
|
||||
// // the NodeID provided matches
|
||||
// // Requires better search parameters
|
||||
// // So that it's more narrow
|
||||
// for i := range tags {
|
||||
// if tag.Text() == tags[i].Text() {
|
||||
// *nodeID = append(*nodeID, file.NodeID)
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
File Name: Profile.go
|
||||
File Username: Profile.go
|
||||
Copyright: 2021 Peernet Foundation s.r.o.
|
||||
Author: Peter Kleissner
|
||||
*/
|
||||
@@ -7,8 +7,10 @@ Author: Peter Kleissner
|
||||
package webapi
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/PeernetOfficial/core/blockchain"
|
||||
)
|
||||
@@ -31,15 +33,50 @@ type apiBlockRecordProfile struct {
|
||||
/*
|
||||
apiProfileList lists all users profile fields.
|
||||
|
||||
Request: GET /profile/list
|
||||
Request: GET /profile/list?node=[nodeid]
|
||||
Response: 200 with JSON structure apiProfileData
|
||||
*/
|
||||
func (api *WebapiInstance) apiProfileList(w http.ResponseWriter, r *http.Request) {
|
||||
fields, status := api.Backend.UserBlockchain.ProfileList()
|
||||
|
||||
NodeID, valid := DecodeBlake3Hash(r.URL.Query().Get("node"))
|
||||
|
||||
var fields []blockchain.BlockRecordProfile
|
||||
var status int
|
||||
|
||||
result := apiProfileData{Status: status}
|
||||
for n := range fields {
|
||||
result.Fields = append(result.Fields, blockRecordProfileToAPI(fields[n]))
|
||||
|
||||
if valid && !bytes.Equal(NodeID, api.Backend.SelfNodeID()) {
|
||||
//_, node, _ := api.Backend.FindNode(NodeID, 100)
|
||||
|
||||
_, peers, _ := api.Backend.FindNode(NodeID, time.Second*5)
|
||||
// First iteration of the entire blockchain to search for the profile
|
||||
// image and Username of the user
|
||||
|
||||
for blockN1 := peers.BlockchainHeight; blockN1 > 0; blockN1-- {
|
||||
blockDecoded, _, found, _ := api.Backend.ReadBlock(peers.PublicKey, peers.BlockchainVersion, blockN1)
|
||||
if !found {
|
||||
continue
|
||||
}
|
||||
|
||||
profile, _ := blockchain.DecodeBlockRecordProfile(blockDecoded.Block.RecordsRaw)
|
||||
// Adding profile image and Username to the output
|
||||
for raw, _ := range profile {
|
||||
|
||||
if profile[raw].Type == blockchain.ProfileName {
|
||||
result.Fields = append(result.Fields, blockRecordProfileToAPI(blockchain.BlockRecordProfile{Type: profile[raw].Type, Data: profile[raw].Data[:]}))
|
||||
}
|
||||
if profile[raw].Type == blockchain.ProfilePicture {
|
||||
result.Fields = append(result.Fields, blockRecordProfileToAPI(blockchain.BlockRecordProfile{Type: profile[raw].Type, Data: profile[raw].Data[:]}))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
fields, status = api.Backend.UserBlockchain.ProfileList()
|
||||
result.Status = status
|
||||
for n := range fields {
|
||||
result.Fields = append(result.Fields, blockRecordProfileToAPI(fields[n]))
|
||||
}
|
||||
}
|
||||
|
||||
EncodeJSON(api.Backend, w, r, result)
|
||||
@@ -48,12 +85,13 @@ func (api *WebapiInstance) apiProfileList(w http.ResponseWriter, r *http.Request
|
||||
/*
|
||||
apiProfileRead reads a specific users profile field. See core.ProfileX for recognized fields.
|
||||
|
||||
Request: GET /profile/read?field=[index]
|
||||
Request: GET /profile/read?field=[index]&node=[nodeid]
|
||||
Response: 200 with JSON structure apiProfileData
|
||||
*/
|
||||
func (api *WebapiInstance) apiProfileRead(w http.ResponseWriter, r *http.Request) {
|
||||
r.ParseForm()
|
||||
fieldN, err1 := strconv.Atoi(r.Form.Get("field"))
|
||||
fieldN, err1 := strconv.Atoi(r.URL.Query().Get("field"))
|
||||
NodeID, valid := DecodeBlake3Hash(r.URL.Query().Get("node"))
|
||||
|
||||
if err1 != nil || fieldN < 0 {
|
||||
http.Error(w, "", http.StatusBadRequest)
|
||||
@@ -61,10 +99,19 @@ func (api *WebapiInstance) apiProfileRead(w http.ResponseWriter, r *http.Request
|
||||
}
|
||||
|
||||
var result apiProfileData
|
||||
|
||||
var data []byte
|
||||
if data, result.Status = api.Backend.UserBlockchain.ProfileReadField(uint16(fieldN)); result.Status == blockchain.StatusOK {
|
||||
result.Fields = append(result.Fields, blockRecordProfileToAPI(blockchain.BlockRecordProfile{Type: uint16(fieldN), Data: data}))
|
||||
|
||||
if !valid {
|
||||
_, node, _ := api.Backend.FindNode(NodeID, 100)
|
||||
if data, result.Status = node.Backend.UserBlockchain.ProfileReadField(uint16(fieldN)); result.Status == blockchain.StatusOK {
|
||||
result.Fields = append(result.Fields, blockRecordProfileToAPI(blockchain.BlockRecordProfile{Type: uint16(fieldN), Data: data}))
|
||||
}
|
||||
} else {
|
||||
if api.Backend.NodelistLookup(NodeID) != nil {
|
||||
if data, result.Status = api.Backend.NodelistLookup(NodeID).Backend.UserBlockchain.ProfileReadField(uint16(fieldN)); result.Status == blockchain.StatusOK {
|
||||
result.Fields = append(result.Fields, blockRecordProfileToAPI(blockchain.BlockRecordProfile{Type: uint16(fieldN), Data: data}))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EncodeJSON(api.Backend, w, r, result)
|
||||
@@ -90,6 +137,8 @@ func (api *WebapiInstance) apiProfileWrite(w http.ResponseWriter, r *http.Reques
|
||||
|
||||
newHeight, newVersion, status := api.Backend.UserBlockchain.ProfileWrite(fields)
|
||||
|
||||
api.Backend.LogError("apiProfileWrite", "Height: %v, Version %v", newHeight, newVersion)
|
||||
|
||||
EncodeJSON(api.Backend, w, r, apiBlockchainBlockStatus{Status: status, Height: newHeight, Version: newVersion})
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
File Name: Search Dispatch.go
|
||||
File Username: Search Dispatch.go
|
||||
Copyright: 2021 Peernet Foundation s.r.o.
|
||||
Author: Peter Kleissner
|
||||
*/
|
||||
@@ -9,6 +9,7 @@ package webapi
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/PeernetOfficial/core/protocol"
|
||||
"time"
|
||||
|
||||
"github.com/PeernetOfficial/core/blockchain"
|
||||
@@ -43,6 +44,12 @@ func (job *SearchJob) localSearch(api *WebapiInstance, term string) {
|
||||
|
||||
resultLoop:
|
||||
for _, result := range results {
|
||||
|
||||
// check if the NodeID filter is provided
|
||||
if job.filtersStart.NodeID != nil && !(bytes.Equal(job.filtersStart.NodeID, protocol.PublicKey2NodeID(result.PublicKey))) {
|
||||
continue
|
||||
}
|
||||
|
||||
file, _, found, err := api.Backend.ReadFile(result.PublicKey, result.BlockchainVersion, result.BlockNumber, result.FileID)
|
||||
if err != nil || !found {
|
||||
continue
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
File Name: Search Job.go
|
||||
File Username: Search Job.go
|
||||
Copyright: 2021 Peernet Foundation s.r.o.
|
||||
Author: Peter Kleissner
|
||||
*/
|
||||
@@ -25,6 +25,7 @@ type SearchFilter struct {
|
||||
Sort int // Sort order. See SortX.
|
||||
SizeMin int // Min file size in bytes. -1 = not used.
|
||||
SizeMax int // Max file size in bytes. -1 = not used.
|
||||
NodeID []byte // Filter based on a NodeID provided
|
||||
}
|
||||
|
||||
// SearchJob is a collection of search jobs
|
||||
@@ -257,7 +258,7 @@ func (job *SearchJob) isFileFiltered(file *apiFile) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// SortFiles sorts a list of files. It returns a sorted list. 0 = no sorting, 1 = Relevance ASC, 2 = Relevance DESC, 3 = Date ASC, 4 = Date DESC, 5 = Name ASC, 6 = Name DESC
|
||||
// SortFiles sorts a list of files. It returns a sorted list. 0 = no sorting, 1 = Relevance ASC, 2 = Relevance DESC, 3 = Date ASC, 4 = Date DESC, 5 = Username ASC, 6 = Username DESC
|
||||
func SortFiles(files []*apiFile, Sort int) (sorted []*apiFile) {
|
||||
switch Sort {
|
||||
case SortRelevanceAsc:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
File Name: Search.go
|
||||
File Username: Search.go
|
||||
Copyright: 2021 Peernet Foundation s.r.o.
|
||||
Author: Peter Kleissner
|
||||
|
||||
@@ -156,6 +156,7 @@ func (api *WebapiInstance) apiSearchResult(w http.ResponseWriter, r *http.Reques
|
||||
sort, _ := strconv.Atoi(r.Form.Get("sort"))
|
||||
sizeMin, _ := strconv.Atoi(r.Form.Get("sizemin"))
|
||||
sizeMax, _ := strconv.Atoi(r.Form.Get("sizemax"))
|
||||
//nodeID, _ := DecodeBlake3Hash(r.Form.Get("node"))
|
||||
|
||||
filter := inputToSearchFilter(sort, fileType, fileFormat, dateFrom, dateTo, sizeMin, sizeMax)
|
||||
|
||||
@@ -353,23 +354,30 @@ func (api *WebapiInstance) apiSearchStatistic(w http.ResponseWriter, r *http.Req
|
||||
apiExplore returns recently shared files in Peernet. Results are returned in real-time. The file type is an optional filter. See TypeX.
|
||||
Special type -2 = Binary, Compressed, Container, Executable. This special type includes everything except Documents, Video, Audio, Ebooks, Picture, Text.
|
||||
|
||||
Request: GET /explore?limit=[max records]&type=[file type]&offset=[offset]
|
||||
Request: GET /explore?limit=[max records]&type=[file type]&offset=[offset]&node=[nodeID]
|
||||
Result: 200 with JSON structure SearchResult. Check the field status.
|
||||
*/
|
||||
func (api *WebapiInstance) apiExplore(w http.ResponseWriter, r *http.Request) {
|
||||
r.ParseForm()
|
||||
offset, _ := strconv.Atoi(r.Form.Get("offset"))
|
||||
limit, err := strconv.Atoi(r.Form.Get("limit"))
|
||||
offset, _ := strconv.Atoi(r.URL.Query().Get("offset"))
|
||||
limit, err := strconv.Atoi(r.URL.Query().Get("limit"))
|
||||
if err != nil {
|
||||
limit = 100
|
||||
}
|
||||
|
||||
fileType, err := strconv.Atoi(r.Form.Get("type"))
|
||||
fileType, err := strconv.Atoi(r.URL.Query().Get("type"))
|
||||
if err != nil {
|
||||
fileType = -1
|
||||
}
|
||||
|
||||
result := api.ExploreHelper(fileType, limit, offset, []byte{}, false)
|
||||
var result *SearchResult
|
||||
|
||||
NodeId, valid := DecodeBlake3Hash(r.URL.Query().Get("node"))
|
||||
if valid {
|
||||
result = api.ExploreHelper(fileType, limit, offset, NodeId, true)
|
||||
} else {
|
||||
result = api.ExploreHelper(fileType, limit, offset, []byte{}, false)
|
||||
}
|
||||
|
||||
EncodeJSON(api.Backend, w, r, result)
|
||||
}
|
||||
@@ -383,7 +391,11 @@ func (api *WebapiInstance) ExploreHelper(fileType int, limit, offset int, nodeID
|
||||
|
||||
// loop over results
|
||||
for n := range resultFiles {
|
||||
result.Files = append(result.Files, blockRecordFileToAPI(resultFiles[n]))
|
||||
ApiFile := blockRecordFileToAPI(resultFiles[n])
|
||||
if ApiFile.NodeID == nil {
|
||||
continue
|
||||
}
|
||||
result.Files = append(result.Files, ApiFile)
|
||||
}
|
||||
|
||||
if len(result.Files) == 0 {
|
||||
@@ -407,6 +419,7 @@ func (input *SearchRequest) Parse() (Timeout time.Duration) {
|
||||
|
||||
// ToSearchFilter converts the user input to a valid search filter
|
||||
func (input *SearchRequest) ToSearchFilter() (output SearchFilter) {
|
||||
//hash, _ := DecodeBlake3Hash(input.NodeID)
|
||||
return inputToSearchFilter(input.Sort, input.FileType, input.FileFormat, input.DateFrom, input.DateTo, input.SizeMin, input.SizeMax)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
File Name: Shared Recent.go
|
||||
File Username: Shared Recent.go
|
||||
Copyright: 2021 Peernet Foundation s.r.o.
|
||||
Author: Peter Kleissner
|
||||
*/
|
||||
@@ -18,7 +18,6 @@ func (api *WebapiInstance) queryRecentShared(backend *core.Backend, fileType int
|
||||
if limitPeer == 0 {
|
||||
limitPeer = 1
|
||||
}
|
||||
|
||||
// Assign peer list as an empty array
|
||||
var peerList []*core.PeerInfo
|
||||
|
||||
@@ -44,11 +43,31 @@ func (api *WebapiInstance) queryRecentShared(backend *core.Backend, fileType int
|
||||
}
|
||||
|
||||
var filesFromPeer uint64
|
||||
var Name string
|
||||
|
||||
ProfileNameFound := false
|
||||
|
||||
// First iteration of the entire blockchain to search for the profile
|
||||
// image and Username of the user
|
||||
for blockN1 := peer.BlockchainHeight - 1; blockN1 > 0; blockN1-- {
|
||||
blockDecoded, _, found, _ := backend.ReadBlock(peer.PublicKey, peer.BlockchainVersion, blockN1)
|
||||
if !found {
|
||||
continue
|
||||
}
|
||||
// Adding profile image and Username to the output
|
||||
for raw := range blockDecoded.Block.RecordsRaw {
|
||||
if blockDecoded.Block.RecordsRaw[raw].Type == blockchain.ProfileName && !ProfileNameFound {
|
||||
Name = string(blockDecoded.Block.RecordsRaw[raw].Data[:])
|
||||
ProfileNameFound = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// decode blocks from top down
|
||||
blockLoop:
|
||||
for blockN := peer.BlockchainHeight - 1; blockN > 0; blockN-- {
|
||||
blockDecoded, _, found, _ := backend.ReadBlock(peer.PublicKey, peer.BlockchainVersion, blockN)
|
||||
|
||||
if !found {
|
||||
continue
|
||||
}
|
||||
@@ -62,6 +81,8 @@ func (api *WebapiInstance) queryRecentShared(backend *core.Backend, fileType int
|
||||
file.Tags = append(file.Tags, blockchain.TagFromText(blockchain.TagSharedByGeoIP, sharedByGeoIP))
|
||||
}
|
||||
|
||||
file.Username = Name
|
||||
|
||||
// found a new file! append.
|
||||
if filesFromPeer < limitPeer {
|
||||
filesFromPeer++
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
File Name: Status.go
|
||||
File Username: Status.go
|
||||
Copyright: 2021 Peernet Foundation s.r.o.
|
||||
Author: Peter Kleissner
|
||||
*/
|
||||
|
||||
86
webapi/Upload.go
Normal file
86
webapi/Upload.go
Normal file
@@ -0,0 +1,86 @@
|
||||
package webapi
|
||||
|
||||
import (
|
||||
"github.com/google/uuid"
|
||||
"math"
|
||||
"net/http"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type UploadStatus struct {
|
||||
APIStatus int `json:"apistatus"` // Status of the API call. See DownloadResponseX.
|
||||
ID uuid.UUID `json:"id"` // Download ID. This can be used to query the latest status and take actions.
|
||||
sync.RWMutex // Mutext for changing the status
|
||||
|
||||
UploadStatus int `json:"uploadstatus"` // Status of the download. See DownloadX.
|
||||
Progress struct {
|
||||
TotalSize uint64 `json:"totalsize"` // Total size in bytes.
|
||||
UploadedSize uint64 `json:"uploadedsize"` // Count of bytes download so far.
|
||||
Percentage float64 `json:"percentage"` // Percentage downloaded. Rounded to 2 decimal points. Between 0.00 and 100.00.
|
||||
} `json:"progress"` // Progress of the download. Only valid for status >= DownloadWaitSwarm.
|
||||
}
|
||||
|
||||
func (api *WebapiInstance) uploadAdd(info *UploadStatus) {
|
||||
api.uploadsMutex.Lock()
|
||||
api.uploads[info.ID] = info
|
||||
api.uploadsMutex.Unlock()
|
||||
}
|
||||
|
||||
func (api *WebapiInstance) uploadDelete(id uuid.UUID) {
|
||||
api.uploadsMutex.Lock()
|
||||
delete(api.uploads, id)
|
||||
api.uploadsMutex.Unlock()
|
||||
}
|
||||
|
||||
func (api *WebapiInstance) uploadLookup(id uuid.UUID) (info *UploadStatus) {
|
||||
api.uploadsMutex.Lock()
|
||||
info = api.uploads[id]
|
||||
api.uploadsMutex.Unlock()
|
||||
return info
|
||||
}
|
||||
|
||||
// UploadID API to set a Status ID to track the upload
|
||||
func (api *WebapiInstance) apiUploadID(w http.ResponseWriter, r *http.Request) {
|
||||
var info UploadStatus
|
||||
info.ID = uuid.New()
|
||||
|
||||
// Create a upload ID for adding the upload
|
||||
// metadata later one
|
||||
api.uploadAdd(&info)
|
||||
|
||||
EncodeJSON(api.Backend, w, r, info)
|
||||
}
|
||||
|
||||
// Write is used to satisfy the io.Writer interface.
|
||||
// Instead of writing somewhere, it simply aggregates
|
||||
// the total bytes on each read
|
||||
func (uploadStatus *UploadStatus) Write(p []byte) (n int, err error) {
|
||||
n = len(p)
|
||||
uploadStatus.Progress.UploadedSize += uint64(n)
|
||||
uploadStatus.Progress.Percentage = math.Round(float64(uploadStatus.Progress.UploadedSize)/float64(uploadStatus.Progress.TotalSize)*100*100) / 100
|
||||
return
|
||||
}
|
||||
|
||||
// Get information about upload file status
|
||||
func (api *WebapiInstance) apiUploadInfo(w http.ResponseWriter, r *http.Request) {
|
||||
ID := r.URL.Query().Get("id")
|
||||
if ID == "" {
|
||||
api.Backend.LogError("upload.UploadInformation", "error: %v", "ID parameter not passed")
|
||||
http.Error(w, "", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
IDUUID, err := uuid.Parse(ID)
|
||||
if err != nil {
|
||||
api.Backend.LogError("upload.UploadInformation", "error: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
info := api.uploadLookup(IDUUID)
|
||||
if info == nil {
|
||||
EncodeJSON(api.Backend, w, r, UploadStatus{APIStatus: DownloadResponseIDNotFound})
|
||||
return
|
||||
}
|
||||
|
||||
EncodeJSON(api.Backend, w, r, info)
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
File Name: Warehouse.go
|
||||
File Username: Warehouse.go
|
||||
Copyright: 2021 Peernet Foundation s.r.o.
|
||||
Author: Peter Kleissner
|
||||
*/
|
||||
@@ -7,6 +7,7 @@ Author: Peter Kleissner
|
||||
package webapi
|
||||
|
||||
import (
|
||||
"github.com/google/uuid"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
@@ -20,18 +21,63 @@ type WarehouseResult struct {
|
||||
}
|
||||
|
||||
/*
|
||||
apiWarehouseCreateFile creates a file in the warehouse.
|
||||
ApiWarehouseCreateFile creates a file in the warehouse.
|
||||
|
||||
Request: POST /warehouse/create with raw data to create as new file
|
||||
Response: 200 with JSON structure WarehouseResult
|
||||
*/
|
||||
func (api *WebapiInstance) apiWarehouseCreateFile(w http.ResponseWriter, r *http.Request) {
|
||||
hash, status, err := api.Backend.UserWarehouse.CreateFile(r.Body, 0)
|
||||
func (api *WebapiInstance) ApiWarehouseCreateFile(w http.ResponseWriter, r *http.Request) {
|
||||
// changing parameter to take ID as a parameter for upload and file itself
|
||||
ID := r.FormValue("id")
|
||||
file, handler, err := r.FormFile("File")
|
||||
if err != nil {
|
||||
api.Backend.LogError("warehouse.CreateFile", "error: %v", err)
|
||||
EncodeJSON(api.Backend, w, r, errorResponse{function: "warehouse.CreateFile", error: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
var hash []byte
|
||||
var status int
|
||||
|
||||
// checks if there is a new upload and then
|
||||
if ID != "" {
|
||||
IDUUID, err := uuid.Parse(ID)
|
||||
if err != nil {
|
||||
api.Backend.LogError("warehouse.CreateFile", "error: %v", err)
|
||||
EncodeJSON(api.Backend, w, r, errorResponse{function: "warehouse.CreateFile", error: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
info := api.uploadLookup(IDUUID)
|
||||
if info == nil {
|
||||
var newInfo UploadStatus
|
||||
newInfo.ID = IDUUID
|
||||
newInfo.Progress.TotalSize = uint64(handler.Size)
|
||||
api.Backend.LogError("warehouse.CreateFile", "%v", newInfo)
|
||||
api.uploadAdd(&newInfo)
|
||||
hash, status, err = api.Backend.UserWarehouse.CreateFile(file, uint64(handler.Size), &newInfo)
|
||||
} else {
|
||||
info.Progress.TotalSize = uint64(handler.Size)
|
||||
api.Backend.LogError("warehouse.CreateFile", "%v", info)
|
||||
hash, status, err = api.Backend.UserWarehouse.CreateFile(file, uint64(handler.Size), info)
|
||||
}
|
||||
|
||||
api.Backend.LogError("warehouse.CreateFile", "outside Create file: %v", info)
|
||||
|
||||
} else {
|
||||
// File := r.
|
||||
hash, status, err = api.Backend.UserWarehouse.CreateFile(file, uint64(handler.Size), nil)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
api.Backend.LogError("warehouse.CreateFile", "status %d error: %v", status, err)
|
||||
EncodeJSON(api.Backend, w, r, errorResponse{function: "warehouse.CreateFile", error: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// Temporary log to check the output for warehouse API
|
||||
api.Backend.LogError("warehouse.CreateFile", "output %v", WarehouseResult{Status: status, Hash: hash})
|
||||
|
||||
EncodeJSON(api.Backend, w, r, WarehouseResult{Status: status, Hash: hash})
|
||||
}
|
||||
|
||||
|
||||
@@ -75,6 +75,11 @@ These are the functions provided by the API:
|
||||
|
||||
/merge/directory List all recent files shared by peers based
|
||||
on the similar file shared
|
||||
/warehouse/create/uploadID Generates a UUID to track upload status
|
||||
/warehouse/create/track/uploadID Tracks upload status when a upload is
|
||||
ongoing to the warehaouse (Triggers after
|
||||
the route "/warehouse/create" is called).
|
||||
|
||||
```
|
||||
|
||||
# API Documentation
|
||||
@@ -277,29 +282,30 @@ The file type is an indication what type of content the file's data is:
|
||||
|
||||
The file format is a more granular indicator about the content of a file:
|
||||
|
||||
| Type | Constant | Info |
|
||||
| ---- | ---------------- | --------------------------------------------------- |
|
||||
| 0 | FormatBinary | Binary/unspecified |
|
||||
| 1 | FormatPDF | PDF document |
|
||||
| 2 | FormatWord | Word document |
|
||||
| 3 | FormatExcel | Excel |
|
||||
| 4 | FormatPowerpoint | Powerpoint |
|
||||
| 5 | FormatPicture | Pictures (including GIF, excluding icons) |
|
||||
| 6 | FormatAudio | Audio files |
|
||||
| 7 | FormatVideo | Video files |
|
||||
| Type | Constant | Info |
|
||||
|------| ---------------- |----------------------------------------------------|
|
||||
| 0 | FormatBinary | Binary/unspecified |
|
||||
| 1 | FormatPDF | PDF document |
|
||||
| 2 | FormatWord | Word document |
|
||||
| 3 | FormatExcel | Excel |
|
||||
| 4 | FormatPowerpoint | Powerpoint |
|
||||
| 5 | FormatPicture | Pictures (including GIF, excluding icons) |
|
||||
| 6 | FormatAudio | Audio files |
|
||||
| 7 | FormatVideo | Video files |
|
||||
| 8 | FormatContainer | Compressed files including ZIP, RAR, TAR and others |
|
||||
| 9 | FormatHTML | HTML file |
|
||||
| 10 | FormatText | Text file |
|
||||
| 11 | FormatEbook | Ebook file |
|
||||
| 12 | FormatCompressed | Compressed file |
|
||||
| 13 | FormatDatabase | Database file |
|
||||
| 14 | FormatEmail | Single email |
|
||||
| 15 | FormatCSV | CSV file |
|
||||
| 16 | FormatFolder | Virtual folder |
|
||||
| 17 | FormatExecutable | Executable file |
|
||||
| 18 | FormatInstaller | Installer |
|
||||
| 19 | FormatAPK | APK |
|
||||
| 20 | FormatISO | ISO |
|
||||
| 9 | FormatHTML | HTML file |
|
||||
| 10 | FormatText | Text file |
|
||||
| 11 | FormatEbook | Ebook file |
|
||||
| 12 | FormatCompressed | Compressed file |
|
||||
| 13 | FormatDatabase | Database file |
|
||||
| 14 | FormatEmail | Single email |
|
||||
| 15 | FormatCSV | CSV file |
|
||||
| 16 | FormatFolder | Virtual folder |
|
||||
| 17 | FormatExecutable | Executable file |
|
||||
| 18 | FormatInstaller | Installer |
|
||||
| 19 | FormatAPK | APK |
|
||||
| 20 | FormatISO | ISO |
|
||||
| 21 | FormatPeernetSearch | File type to store peernet search history |
|
||||
|
||||
### Add File
|
||||
|
||||
@@ -496,7 +502,7 @@ Below is the list of well known profile information. Clients may define addition
|
||||
This lists all profile fields.
|
||||
|
||||
```
|
||||
Request: GET /profile/list
|
||||
Request: GET /profile/list&node=[node id<optional>]
|
||||
Response: 200 with JSON structure apiProfileData
|
||||
```
|
||||
|
||||
@@ -538,7 +544,7 @@ Example response:
|
||||
This reads a specific profile field. See ProfileX for recognized fields.
|
||||
|
||||
```
|
||||
Request: GET /profile/read?field=[index]
|
||||
Request: GET /profile/read?field=[index]&node=[node id<optional>]
|
||||
Response: 200 with JSON structure apiProfileData
|
||||
```
|
||||
|
||||
@@ -617,7 +623,7 @@ Filters and sort order may be applied when starting the search at `/search`, or
|
||||
These are the available sort options:
|
||||
|
||||
| Sort | Constant | Info |
|
||||
| ---- | --------------------- | ----------------------------------------------------------------------------------- |
|
||||
|------|-----------------------|-------------------------------------------------------------------------------------|
|
||||
| 0 | SortNone | No sorting. Results are returned as they come in. |
|
||||
| 1 | SortRelevanceAsc | Least relevant results first. |
|
||||
| 2 | SortRelevanceDec | Most relevant results first. |
|
||||
@@ -630,6 +636,7 @@ These are the available sort options:
|
||||
| 9 | SortSharedByCountAsc | Shared by count ascending. Files that are shared by the least count of peers first. |
|
||||
| 10 | SortSharedByCountDesc | Shared by count descending. Files that are shared by the most count of peers first. |
|
||||
|
||||
|
||||
The following filters are supported:
|
||||
|
||||
* Filter by date from and to. Both dates are required. The inclusion check for the 'from date' is >= and 'to date' <.
|
||||
@@ -658,6 +665,7 @@ type SearchRequest struct {
|
||||
FileFormat int `json:"fileformat"` // File format such as PDF, Word, Ebook, etc. See core.FormatX. -1 = not used.
|
||||
SizeMin int `json:"sizemin"` // Min file size in bytes. -1 = not used.
|
||||
SizeMax int `json:"sizemax"` // Max file size in bytes. -1 = not used.
|
||||
NodeID string `json:"node"` // Filter based on the NodeID provided
|
||||
}
|
||||
|
||||
type SearchRequestResponse struct {
|
||||
@@ -1028,7 +1036,8 @@ type WarehouseResult struct {
|
||||
Example POST request to `http://127.0.0.1:112/warehouse/create`:
|
||||
|
||||
```
|
||||
Test file.
|
||||
--form 'id="<uuid>"' \
|
||||
--form 'File=@"<file path>"'
|
||||
```
|
||||
|
||||
Example response:
|
||||
|
||||
Reference in New Issue
Block a user