From fc09dff2b19b2d40092d92594ea5420eb78d98dc Mon Sep 17 00:00:00 2001 From: Akilan Date: Tue, 31 Jan 2023 22:01:02 +0000 Subject: [PATCH] changes to explore to take parameter id in /explore --- webapi/Download_test.go | 13 ++ webapi/Search.go | 505 ++++++++++++++++++++-------------------- webapi/Shared Recent.go | 127 +++++----- 3 files changed, 335 insertions(+), 310 deletions(-) create mode 100644 webapi/Download_test.go diff --git a/webapi/Download_test.go b/webapi/Download_test.go new file mode 100644 index 0000000..8437585 --- /dev/null +++ b/webapi/Download_test.go @@ -0,0 +1,13 @@ +package webapi + +import ( + "fmt" + "testing" +) + +// Test function +func TestDecodeBlake3Hash(t *testing.T) { + hash, bool := DecodeBlake3Hash("") + fmt.Println(hash) + fmt.Println(bool) +} diff --git a/webapi/Search.go b/webapi/Search.go index 1b7c7b5..99ba139 100644 --- a/webapi/Search.go +++ b/webapi/Search.go @@ -16,61 +16,61 @@ Author: Peter Kleissner package webapi import ( - "net/http" - "strconv" - "time" + "net/http" + "strconv" + "time" - "github.com/google/uuid" + "github.com/google/uuid" ) // SearchRequest is the information from the end-user for the search. Filters and sort order may be applied when starting the search, or at runtime when getting the results. type SearchRequest struct { - Term string `json:"term"` // Search term. - Timeout int `json:"timeout"` // Timeout in seconds. 0 means default. This is the entire time the search may take. Found results are still available after this timeout. - MaxResults int `json:"maxresults"` // Total number of max results. 0 means default. - DateFrom string `json:"datefrom"` // Date from, both from/to are required if set. Format "2006-01-02 15:04:05". - DateTo string `json:"dateto"` // Date to, both from/to are required if set. Format "2006-01-02 15:04:05". - Sort int `json:"sort"` // See SortX. - TerminateID []uuid.UUID `json:"terminate"` // Optional: Previous search IDs to terminate. This is if the user makes a new search from the same tab. Same as first calling /search/terminate. - FileType int `json:"filetype"` // File type such as binary, text document etc. See core.TypeX. -1 = not used. - 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. + Term string `json:"term"` // Search term. + Timeout int `json:"timeout"` // Timeout in seconds. 0 means default. This is the entire time the search may take. Found results are still available after this timeout. + MaxResults int `json:"maxresults"` // Total number of max results. 0 means default. + DateFrom string `json:"datefrom"` // Date from, both from/to are required if set. Format "2006-01-02 15:04:05". + DateTo string `json:"dateto"` // Date to, both from/to are required if set. Format "2006-01-02 15:04:05". + Sort int `json:"sort"` // See SortX. + TerminateID []uuid.UUID `json:"terminate"` // Optional: Previous search IDs to terminate. This is if the user makes a new search from the same tab. Same as first calling /search/terminate. + FileType int `json:"filetype"` // File type such as binary, text document etc. See core.TypeX. -1 = not used. + 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. } // Sort orders const ( - SortNone = 0 // No sorting. Results are returned as they come in. - SortRelevanceAsc = 1 // Least relevant results first. - SortRelevanceDec = 2 // Most relevant results first. - SortDateAsc = 3 // Oldest first. - SortDateDesc = 4 // Newest first. - SortNameAsc = 5 // File name ascending. The folder name is not used for sorting. - SortNameDesc = 6 // File name descending. The folder name is not used for sorting. - SortSizeAsc = 7 // File size ascending. Smallest files first. - SortSizeDesc = 8 // File size descending. Largest files first. - SortSharedByCountAsc = 9 // Shared by count ascending. Files that are shared by the least count of peers first. - SortSharedByCountDesc = 10 // Shared by count descending. Files that are shared by the most count of peers first. + SortNone = 0 // No sorting. Results are returned as they come in. + SortRelevanceAsc = 1 // Least relevant results first. + SortRelevanceDec = 2 // Most relevant results first. + SortDateAsc = 3 // Oldest first. + SortDateDesc = 4 // Newest first. + SortNameAsc = 5 // File name ascending. The folder name is not used for sorting. + SortNameDesc = 6 // File name descending. The folder name is not used for sorting. + SortSizeAsc = 7 // File size ascending. Smallest files first. + SortSizeDesc = 8 // File size descending. Largest files first. + SortSharedByCountAsc = 9 // Shared by count ascending. Files that are shared by the least count of peers first. + SortSharedByCountDesc = 10 // Shared by count descending. Files that are shared by the most count of peers first. ) // SearchRequestResponse is the result to the initial search request type SearchRequestResponse struct { - ID uuid.UUID `json:"id"` // ID of the search job. This is used to get the results. - Status int `json:"status"` // Status of the search: 0 = Success (ID valid), 1 = Invalid Term, 2 = Error Max Concurrent Searches + ID uuid.UUID `json:"id"` // ID of the search job. This is used to get the results. + Status int `json:"status"` // Status of the search: 0 = Success (ID valid), 1 = Invalid Term, 2 = Error Max Concurrent Searches } // SearchResult contains the search results. type SearchResult struct { - Status int `json:"status"` // Status: 0 = Success with results, 1 = No more results available, 2 = Search ID not found, 3 = No results yet available keep trying - Files []apiFile `json:"files"` // List of files found - Statistic interface{} `json:"statistic"` // Statistics of all results (independent from applied filters), if requested. Only set if files are returned (= if statistics changed). See SearchStatisticData. + Status int `json:"status"` // Status: 0 = Success with results, 1 = No more results available, 2 = Search ID not found, 3 = No results yet available keep trying + Files []apiFile `json:"files"` // List of files found + Statistic interface{} `json:"statistic"` // Statistics of all results (independent from applied filters), if requested. Only set if files are returned (= if statistics changed). See SearchStatisticData. } // SearchStatistic contains statistics on search results. Statistics are always calculated over all results, regardless of any applied runtime filters. type SearchStatistic struct { - SearchStatisticData - Status int `json:"status"` // Status: 0 = Success - IsTerminated bool `json:"terminated"` // Whether the search is terminated, meaning that statistics won't change + SearchStatisticData + Status int `json:"status"` // Status: 0 = Success + IsTerminated bool `json:"terminated"` // Whether the search is terminated, meaning that statistics won't change } const apiDateFormat = "2006-01-02 15:04:05" @@ -84,29 +84,29 @@ Result: 200 on success with JSON SearchRequestResponse */ func (api *WebapiInstance) apiSearch(w http.ResponseWriter, r *http.Request) { - var input SearchRequest - if err := DecodeJSON(w, r, &input); err != nil { - return - } + var input SearchRequest + if err := DecodeJSON(w, r, &input); err != nil { + return + } - if input.Timeout <= 0 { - input.Timeout = 20 - } - if input.MaxResults <= 0 { - input.MaxResults = 200 - } + if input.Timeout <= 0 { + input.Timeout = 20 + } + if input.MaxResults <= 0 { + input.MaxResults = 200 + } - // Terminate previous searches, if their IDs were supplied. This allows terminating the old search immediately without making a separate /search/terminate request. - for _, terminate := range input.TerminateID { - if job := api.JobLookup(terminate); job != nil { - job.Terminate() - api.RemoveJob(job) - } - } + // Terminate previous searches, if their IDs were supplied. This allows terminating the old search immediately without making a separate /search/terminate request. + for _, terminate := range input.TerminateID { + if job := api.JobLookup(terminate); job != nil { + job.Terminate() + api.RemoveJob(job) + } + } - job := api.dispatchSearch(input) + job := api.dispatchSearch(input) - EncodeJSON(api.backend, w, r, SearchRequestResponse{Status: 0, ID: job.id}) + EncodeJSON(api.backend, w, r, SearchRequestResponse{Status: 0, ID: job.id}) } /* @@ -126,82 +126,82 @@ Optional parameters: Result: 200 with JSON structure SearchResult. Check the field status. */ func (api *WebapiInstance) apiSearchResult(w http.ResponseWriter, r *http.Request) { - r.ParseForm() - jobID, err := uuid.Parse(r.Form.Get("id")) - if err != nil { - http.Error(w, "", http.StatusBadRequest) - return - } - limit, err := strconv.Atoi(r.Form.Get("limit")) - if err != nil { - limit = 100 - } - offset, errOffset := strconv.Atoi(r.Form.Get("offset")) + r.ParseForm() + jobID, err := uuid.Parse(r.Form.Get("id")) + if err != nil { + http.Error(w, "", http.StatusBadRequest) + return + } + limit, err := strconv.Atoi(r.Form.Get("limit")) + if err != nil { + limit = 100 + } + offset, errOffset := strconv.Atoi(r.Form.Get("offset")) - // find the job ID - job := api.JobLookup(jobID) - if job == nil { - EncodeJSON(api.backend, w, r, SearchResult{Status: 2}) - return - } + // find the job ID + job := api.JobLookup(jobID) + if job == nil { + EncodeJSON(api.backend, w, r, SearchResult{Status: 2}) + return + } - // filters and sort parameter - if filterReset, _ := strconv.ParseBool(r.Form.Get("reset")); filterReset { - fileType, _ := strconv.Atoi(r.Form.Get("filetype")) - fileFormat, _ := strconv.Atoi(r.Form.Get("fileformat")) - dateFrom := r.Form.Get("from") - dateTo := r.Form.Get("to") - sort, _ := strconv.Atoi(r.Form.Get("sort")) - sizeMin, _ := strconv.Atoi(r.Form.Get("sizemin")) - sizeMax, _ := strconv.Atoi(r.Form.Get("sizemax")) + // filters and sort parameter + if filterReset, _ := strconv.ParseBool(r.Form.Get("reset")); filterReset { + fileType, _ := strconv.Atoi(r.Form.Get("filetype")) + fileFormat, _ := strconv.Atoi(r.Form.Get("fileformat")) + dateFrom := r.Form.Get("from") + dateTo := r.Form.Get("to") + sort, _ := strconv.Atoi(r.Form.Get("sort")) + sizeMin, _ := strconv.Atoi(r.Form.Get("sizemin")) + sizeMax, _ := strconv.Atoi(r.Form.Get("sizemax")) - filter := inputToSearchFilter(sort, fileType, fileFormat, dateFrom, dateTo, sizeMin, sizeMax) + filter := inputToSearchFilter(sort, fileType, fileFormat, dateFrom, dateTo, sizeMin, sizeMax) - job.RuntimeFilter(filter) - } + job.RuntimeFilter(filter) + } - // query all results - var resultFiles []*apiFile - if errOffset == nil { - resultFiles = job.ReturnResult(offset, limit) - } else { - resultFiles = job.ReturnNext(limit) - } + // query all results + var resultFiles []*apiFile + if errOffset == nil { + resultFiles = job.ReturnResult(offset, limit) + } else { + resultFiles = job.ReturnNext(limit) + } - var result SearchResult - result.Files = []apiFile{} + var result SearchResult + result.Files = []apiFile{} - // loop over results - for n := range resultFiles { - result.Files = append(result.Files, *resultFiles[n]) - } + // loop over results + for n := range resultFiles { + result.Files = append(result.Files, *resultFiles[n]) + } - // set the status - if len(result.Files) > 0 { - if job.IsSearchResults() { - result.Status = 0 // 0 = Success with results - } else { - result.Status = 1 // No more results to expect - } - } else { - switch job.Status { - case SearchStatusLive: - result.Status = 3 // No results yet available keep trying + // set the status + if len(result.Files) > 0 { + if job.IsSearchResults() { + result.Status = 0 // 0 = Success with results + } else { + result.Status = 1 // No more results to expect + } + } else { + switch job.Status { + case SearchStatusLive: + result.Status = 3 // No results yet available keep trying - case SearchStatusTerminated: - result.Status = 1 // No more results to expect + case SearchStatusTerminated: + result.Status = 1 // No more results to expect - default: // SearchStatusNoIndex, SearchStatusNotStarted - result.Status = 1 // No more results to expect - } - } + default: // SearchStatusNoIndex, SearchStatusNotStarted + result.Status = 1 // No more results to expect + } + } - // embedded statistics? - if returnStats, _ := strconv.ParseBool(r.Form.Get("stats")); returnStats { - result.Statistic = job.Statistics() - } + // embedded statistics? + if returnStats, _ := strconv.ParseBool(r.Form.Get("stats")); returnStats { + result.Statistic = job.Statistics() + } - EncodeJSON(api.backend, w, r, result) + EncodeJSON(api.backend, w, r, result) } /* @@ -212,80 +212,80 @@ Result: If successful, upgrades to a websocket and sends JSON structure Sear Limit is optional. Not used if ommitted or 0. */ func (api *WebapiInstance) apiSearchResultStream(w http.ResponseWriter, r *http.Request) { - r.ParseForm() - jobID, err := uuid.Parse(r.Form.Get("id")) - if err != nil { - http.Error(w, "", http.StatusBadRequest) - return - } - limit, err := strconv.Atoi(r.Form.Get("limit")) - useLimit := err == nil + r.ParseForm() + jobID, err := uuid.Parse(r.Form.Get("id")) + if err != nil { + http.Error(w, "", http.StatusBadRequest) + return + } + limit, err := strconv.Atoi(r.Form.Get("limit")) + useLimit := err == nil - // look up the job - job := api.JobLookup(jobID) - if job == nil { - EncodeJSON(api.backend, w, r, SearchResult{Status: 2}) - return - } + // look up the job + job := api.JobLookup(jobID) + if job == nil { + EncodeJSON(api.backend, w, r, SearchResult{Status: 2}) + return + } - // upgrade to websocket - conn, err := WSUpgrader.Upgrade(w, r, nil) - if err != nil { - // gorilla will automatically respond with "400 Bad Request", no other response is therefore necessary - return - } + // upgrade to websocket + conn, err := WSUpgrader.Upgrade(w, r, nil) + if err != nil { + // gorilla will automatically respond with "400 Bad Request", no other response is therefore necessary + return + } - defer conn.Close() + defer conn.Close() - // loop to get new results and send out via the web socket. - // Only exit if limit is reached if used, otherwise only if there are no result or the connection breaks. - for { - // query all results - var resultFiles []*apiFile + // loop to get new results and send out via the web socket. + // Only exit if limit is reached if used, otherwise only if there are no result or the connection breaks. + for { + // query all results + var resultFiles []*apiFile - queryCount := 1 - if useLimit { - queryCount = limit - } - resultFiles = job.ReturnNext(queryCount) + queryCount := 1 + if useLimit { + queryCount = limit + } + resultFiles = job.ReturnNext(queryCount) - if useLimit { - limit -= len(resultFiles) - } + if useLimit { + limit -= len(resultFiles) + } - // loop over results - var result SearchResult - result.Files = []apiFile{} + // loop over results + var result SearchResult + result.Files = []apiFile{} - for n := range resultFiles { - result.Files = append(result.Files, *resultFiles[n]) - } + for n := range resultFiles { + result.Files = append(result.Files, *resultFiles[n]) + } - if !job.IsSearchResults() { - result.Status = 1 // No more results to expect + if !job.IsSearchResults() { + result.Status = 1 // No more results to expect - if len(result.Files) == 0 { - conn.WriteJSON(result) // final message - return - } - } + if len(result.Files) == 0 { + conn.WriteJSON(result) // final message + return + } + } - // if no results, stall - if len(result.Files) == 0 { - time.Sleep(time.Millisecond * 100) - continue - } + // if no results, stall + if len(result.Files) == 0 { + time.Sleep(time.Millisecond * 100) + continue + } - // send out the results via the websocket - if err := conn.WriteJSON(result); err != nil { - return - } + // send out the results via the websocket + if err := conn.WriteJSON(result); err != nil { + return + } - // Check whether to continue. If the limit is used break once all done. - if (useLimit && limit <= 0) || result.Status == 1 { - break - } - } + // Check whether to continue. If the limit is used break once all done. + if (useLimit && limit <= 0) || result.Status == 1 { + break + } + } } /* @@ -298,25 +298,25 @@ Response: 204 Empty */ func (api *WebapiInstance) apiSearchTerminate(w http.ResponseWriter, r *http.Request) { - r.ParseForm() - jobID, err := uuid.Parse(r.Form.Get("id")) - if err != nil { - http.Error(w, "", http.StatusBadRequest) - return - } + r.ParseForm() + jobID, err := uuid.Parse(r.Form.Get("id")) + if err != nil { + http.Error(w, "", http.StatusBadRequest) + return + } - // look up the job - job := api.JobLookup(jobID) - if job == nil { - http.Error(w, "", http.StatusNotFound) - return - } + // look up the job + job := api.JobLookup(jobID) + if job == nil { + http.Error(w, "", http.StatusNotFound) + return + } - // terminate and remove it from the list - job.Terminate() - api.RemoveJob(job) + // terminate and remove it from the list + job.Terminate() + api.RemoveJob(job) - w.WriteHeader(http.StatusNoContent) + w.WriteHeader(http.StatusNoContent) } /* @@ -326,92 +326,95 @@ Request: GET /search/result?id=[UUID] Result: 200 with JSON structure SearchStatistic. Check the field status (0 = Success, 2 = ID not found). */ func (api *WebapiInstance) apiSearchStatistic(w http.ResponseWriter, r *http.Request) { - r.ParseForm() - jobID, err := uuid.Parse(r.Form.Get("id")) - if err != nil { - http.Error(w, "", http.StatusBadRequest) - return - } + r.ParseForm() + jobID, err := uuid.Parse(r.Form.Get("id")) + if err != nil { + http.Error(w, "", http.StatusBadRequest) + return + } - // find the job ID - job := api.JobLookup(jobID) - if job == nil { - EncodeJSON(api.backend, w, r, SearchStatistic{Status: 2}) - return - } + // find the job ID + job := api.JobLookup(jobID) + if job == nil { + EncodeJSON(api.backend, w, r, SearchStatistic{Status: 2}) + return + } - stats := job.Statistics() + stats := job.Statistics() - EncodeJSON(api.backend, w, r, SearchStatistic{SearchStatisticData: stats, Status: 0, IsTerminated: job.IsTerminated()}) + EncodeJSON(api.backend, w, r, SearchStatistic{SearchStatisticData: stats, Status: 0, IsTerminated: job.IsTerminated()}) } /* 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]&nodeid=[node id] 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")) - if err != nil { - limit = 100 - } - fileType, err := strconv.Atoi(r.Form.Get("type")) - if err != nil { - fileType = -1 - } + r.ParseForm() + offset, _ := strconv.Atoi(r.Form.Get("offset")) + limit, err := strconv.Atoi(r.Form.Get("limit")) + if err != nil { + limit = 100 + } + // ID fields for results for a specific node ID. + nodeid, _ := DecodeBlake3Hash(r.Form.Get("nodeid")) - resultFiles := api.queryRecentShared(api.backend, fileType, uint64(limit*20/100), uint64(offset), uint64(limit)) + fileType, err := strconv.Atoi(r.Form.Get("type")) + if err != nil { + fileType = -1 + } - var result SearchResult - result.Files = []apiFile{} + resultFiles := api.queryRecentShared(api.backend, fileType, uint64(limit*20/100), uint64(offset), uint64(limit), nodeid) - // loop over results - for n := range resultFiles { - result.Files = append(result.Files, blockRecordFileToAPI(resultFiles[n])) - } + var result SearchResult + result.Files = []apiFile{} - if len(result.Files) == 0 { - result.Status = 3 // No results yet available keep trying - } + // loop over results + for n := range resultFiles { + result.Files = append(result.Files, blockRecordFileToAPI(resultFiles[n])) + } - result.Status = 1 // No more results to expect + if len(result.Files) == 0 { + result.Status = 3 // No results yet available keep trying + } - EncodeJSON(api.backend, w, r, result) + result.Status = 1 // No more results to expect + + EncodeJSON(api.backend, w, r, result) } func (input *SearchRequest) Parse() (Timeout time.Duration) { - if input.Timeout == 0 { - Timeout = time.Second * 20 // default timeout: 20 seconds - } else { - Timeout = time.Duration(input.Timeout) * time.Second - } + if input.Timeout == 0 { + Timeout = time.Second * 20 // default timeout: 20 seconds + } else { + Timeout = time.Duration(input.Timeout) * time.Second + } - return + return } // ToSearchFilter converts the user input to a valid search filter func (input *SearchRequest) ToSearchFilter() (output SearchFilter) { - return inputToSearchFilter(input.Sort, input.FileType, input.FileFormat, input.DateFrom, input.DateTo, input.SizeMin, input.SizeMax) + return inputToSearchFilter(input.Sort, input.FileType, input.FileFormat, input.DateFrom, input.DateTo, input.SizeMin, input.SizeMax) } func inputToSearchFilter(Sort, FileType, FileFormat int, DateFrom, DateTo string, SizeMin, SizeMax int) (output SearchFilter) { - output.Sort = Sort - output.FileType = FileType - output.FileFormat = FileFormat - output.SizeMin = SizeMin - output.SizeMax = SizeMax + output.Sort = Sort + output.FileType = FileType + output.FileFormat = FileFormat + output.SizeMin = SizeMin + output.SizeMax = SizeMax - dateFrom, errFrom := time.Parse(apiDateFormat, DateFrom) - dateTo, errTo := time.Parse(apiDateFormat, DateTo) - if errFrom == nil && errTo == nil && dateFrom.Before(dateTo) { - output.DateFrom = dateFrom - output.DateTo = dateTo - output.IsDates = true - } + dateFrom, errFrom := time.Parse(apiDateFormat, DateFrom) + dateTo, errTo := time.Parse(apiDateFormat, DateTo) + if errFrom == nil && errTo == nil && dateFrom.Before(dateTo) { + output.DateFrom = dateFrom + output.DateTo = dateTo + output.IsDates = true + } - return + return } diff --git a/webapi/Shared Recent.go b/webapi/Shared Recent.go index 73cb9f7..6fbdc55 100644 --- a/webapi/Shared Recent.go +++ b/webapi/Shared Recent.go @@ -6,84 +6,93 @@ Author: Peter Kleissner package webapi import ( - "fmt" + "fmt" + "time" - "github.com/PeernetOfficial/core" - "github.com/PeernetOfficial/core/blockchain" + "github.com/PeernetOfficial/core" + "github.com/PeernetOfficial/core/blockchain" ) // queryRecentShared returns recently shared files on the network from random peers until the limit is reached. -func (api *WebapiInstance) queryRecentShared(backend *core.Backend, fileType int, limitPeer, offsetTotal, limitTotal uint64) (files []blockchain.BlockRecordFile) { - if limitPeer == 0 { - limitPeer = 1 - } +func (api *WebapiInstance) queryRecentShared(backend *core.Backend, fileType int, limitPeer, offsetTotal, limitTotal uint64, nodeID []byte) (files []blockchain.BlockRecordFile) { + if limitPeer == 0 { + limitPeer = 1 + } - // Use the peer list to know about active peers. Random order! - peerList := api.backend.PeerlistGet() + // Assign peer list as an empty array + var peerList []*core.PeerInfo - // Files from peers exceeding the limit. It is used if from all peers the total limit is not reached. - var filesSeconday []blockchain.BlockRecordFile + // check if the NodeID is provided or not + if len(nodeID) == 0 { + // Use the peer list to know about active peers. Random order! + peerList = api.backend.PeerlistGet() + } else { + _, peerList[0], _ = api.backend.FindNode(nodeID, time.Second*5) + } - for _, peer := range peerList { - if peer.BlockchainHeight == 0 { - continue - } + // Files from peers exceeding the limit. It is used if from all peers the total limit is not reached. + var filesSeconday []blockchain.BlockRecordFile - var filesFromPeer uint64 + for _, peer := range peerList { + if peer.BlockchainHeight == 0 { + continue + } - // 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 - } + var filesFromPeer uint64 - for _, record := range blockDecoded.RecordsDecoded { - if file, ok := record.(blockchain.BlockRecordFile); ok && isFileTypeMatchBlock(&file, fileType) { - // add the tags 'Shared By Count' and 'Shared By GeoIP' - file.Tags = append(file.Tags, blockchain.TagFromNumber(blockchain.TagSharedByCount, 1)) - if latitude, longitude, valid := api.Peer2GeoIP(peer); valid { - sharedByGeoIP := fmt.Sprintf("%.4f", latitude) + "," + fmt.Sprintf("%.4f", longitude) - file.Tags = append(file.Tags, blockchain.TagFromText(blockchain.TagSharedByGeoIP, sharedByGeoIP)) - } + // 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 + } - // found a new file! append. - if filesFromPeer < limitPeer { - filesFromPeer++ + for _, record := range blockDecoded.RecordsDecoded { + if file, ok := record.(blockchain.BlockRecordFile); ok && isFileTypeMatchBlock(&file, fileType) { + // add the tags 'Shared By Count' and 'Shared By GeoIP' + file.Tags = append(file.Tags, blockchain.TagFromNumber(blockchain.TagSharedByCount, 1)) + if latitude, longitude, valid := api.Peer2GeoIP(peer); valid { + sharedByGeoIP := fmt.Sprintf("%.4f", latitude) + "," + fmt.Sprintf("%.4f", longitude) + file.Tags = append(file.Tags, blockchain.TagFromText(blockchain.TagSharedByGeoIP, sharedByGeoIP)) + } - if offsetTotal > 0 { - offsetTotal-- - continue - } + // found a new file! append. + if filesFromPeer < limitPeer { + filesFromPeer++ - files = append(files, file) + if offsetTotal > 0 { + offsetTotal-- + continue + } - if uint64(len(files)) >= limitTotal { - return - } - } else if uint64(len(filesSeconday)) < limitTotal-uint64(len(files)) { - filesSeconday = append(filesSeconday, file) - } else { - break blockLoop - } - } - } - } - } + files = append(files, file) - files = append(files, filesSeconday...) + if uint64(len(files)) >= limitTotal { + return + } + } else if uint64(len(filesSeconday)) < limitTotal-uint64(len(files)) { + filesSeconday = append(filesSeconday, file) + } else { + break blockLoop + } + } + } + } + } - return + files = append(files, filesSeconday...) + + return } // isFileTypeMatchBlock checks if the file type matches. -1 = accept any. -2 = core.TypeBinary, core.TypeCompressed, core.TypeContainer, core.TypeExecutable. func isFileTypeMatchBlock(file *blockchain.BlockRecordFile, fileType int) bool { - if fileType == -1 { - return true - } else if fileType == -2 { - return file.Type == core.TypeBinary || file.Type == core.TypeCompressed || file.Type == core.TypeContainer || file.Type == core.TypeExecutable - } + if fileType == -1 { + return true + } else if fileType == -2 { + return file.Type == core.TypeBinary || file.Type == core.TypeCompressed || file.Type == core.TypeContainer || file.Type == core.TypeExecutable + } - return file.Type == uint8(fileType) + return file.Type == uint8(fileType) }