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:
Akilan Selvacoumar
2023-06-28 00:38:17 +01:00
committed by GitHub
parent 97c3001596
commit ae5f1d2fd7
91 changed files with 483 additions and 196 deletions

View File

@@ -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")

View File

@@ -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 {

View File

@@ -1,5 +1,5 @@
/*
File Name: Download Transfer.go
File Username: Download Transfer.go
Copyright: 2021 Peernet Foundation s.r.o.
Author: Peter Kleissner

View File

@@ -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)
}

View File

@@ -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
}

View File

@@ -1,5 +1,5 @@
/*
File Name: File IO.go
File Username: File IO.go
Copyright: 2021 Peernet Foundation s.r.o.
Author: Peter Kleissner
*/

View File

@@ -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

View File

@@ -1,5 +1,5 @@
/*
File Name: GeoIP.go
File Username: GeoIP.go
Copyright: 2021 Peernet Foundation s.r.o.
Author: Peter Kleissner

View File

@@ -1,5 +1,5 @@
/*
File Name: HTTP Range.go
File Username: HTTP Range.go
Copyright: 2021 Peernet Foundation s.r.o.
Author: Peter Kleissner
*/

View File

@@ -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
// }
// }
//}
}
}

View File

@@ -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})
}

View File

@@ -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

View File

@@ -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:

View File

@@ -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)
}

View File

@@ -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++

View File

@@ -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
View 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)
}

View File

@@ -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})
}

View File

@@ -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: