mirror of
https://github.com/PeernetOfficial/core.git
synced 2026-07-17 10:57:49 +01:00
253 lines
7.7 KiB
Go
253 lines
7.7 KiB
Go
/*
|
|
File Name: Search.go
|
|
Copyright: 2021 Peernet Foundation s.r.o.
|
|
Author: Peter Kleissner
|
|
|
|
/search Submit a search request
|
|
/search/result Return search results
|
|
/search/terminate Terminate a search
|
|
/search/result/ws Websocket to return search results as stream (future)
|
|
/search/statistic Statistics about the results (future)
|
|
|
|
/explore List recently shared files
|
|
|
|
*/
|
|
|
|
package webapi
|
|
|
|
import (
|
|
"net/http"
|
|
"strconv"
|
|
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
// SearchRequest is the information from the end-user for the search.
|
|
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.
|
|
DateTo string `json:"dateto"` // Date to, both from/to are required if set.
|
|
Sort int `json:"sort"` // Sort order: 0 = No sorting, 1 = Relevance ASC, 2 = Relevance DESC (this should be default), 3 = Date ASC, 4 = Date DESC
|
|
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.
|
|
TypeFilter int `json:"typefilter"` // 0 = No filters used, 1 = Use file type filter, 2 = Use file format filter.
|
|
FileType int `json:"filetype"` // File type such as binary, text document etc. See core.TypeX.
|
|
FileFormat int `json:"fileformat"` // File format such as PDF, Word, Ebook, etc. See core.FormatX.
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
// 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 []apiBlockRecordFile `json:"files"` // List of files found
|
|
}
|
|
|
|
/*
|
|
apiSearch submits a search request
|
|
Request: POST /search with JSON SearchRequest
|
|
Result: 200 on success with JSON SearchRequestResponse
|
|
400 on invalid JSON
|
|
*/
|
|
func apiSearch(w http.ResponseWriter, r *http.Request) {
|
|
|
|
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
|
|
}
|
|
|
|
// 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 := JobLookup(terminate); job != nil {
|
|
job.Terminate()
|
|
job.Remove()
|
|
}
|
|
}
|
|
|
|
job := dispatchSearch(input)
|
|
|
|
EncodeJSON(w, r, SearchRequestResponse{Status: 0, ID: job.id})
|
|
}
|
|
|
|
/*
|
|
apiSearchResult returns results. The default limit is 100.
|
|
Request: GET /search/result?id=[UUID]&limit=[max records]
|
|
Result: 200 with JSON structure SearchResult. Check the field status.
|
|
*/
|
|
func 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
|
|
}
|
|
|
|
// find the job ID
|
|
job := JobLookup(jobID)
|
|
if job == nil {
|
|
EncodeJSON(w, r, SearchResult{Status: 2})
|
|
return
|
|
}
|
|
|
|
// query all results
|
|
resultFiles := job.ReturnNext(limit)
|
|
|
|
var result SearchResult
|
|
result.Files = []apiBlockRecordFile{}
|
|
|
|
// loop over results
|
|
for n := range resultFiles {
|
|
result.Files = append(result.Files, blockRecordFileToAPI(*resultFiles[n]))
|
|
}
|
|
|
|
if len(result.Files) == 0 {
|
|
result.Status = 3 // No results yet available keep trying
|
|
}
|
|
|
|
result.Status = 1 // No more results to expect
|
|
|
|
EncodeJSON(w, r, result)
|
|
}
|
|
|
|
/*
|
|
apiSearchResultStream runs a websocket to return results
|
|
Request: GET /search/result/ws?id=[UUID]&limit=[optional max records]
|
|
Result: If successful, upgrades to a web-socket and sends JSON structure SearchResult messages
|
|
Limit is optional. Not used if ommitted or 0.
|
|
*/ /*
|
|
func 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
|
|
|
|
// look up the job
|
|
job := JobLookup(jobID)
|
|
if job == nil {
|
|
EncodeJSON(w, r, SearchResult{Status: 2})
|
|
return
|
|
}
|
|
|
|
// upgrade to web-socket
|
|
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
|
|
}
|
|
|
|
// 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 result SearchResult
|
|
result.Files = []apiBlockRecordFile{}
|
|
|
|
// loop over the results
|
|
|
|
// if no results, stall
|
|
if len(result.Files) == 0 {
|
|
time.Sleep(time.Millisecond * 100)
|
|
continue
|
|
}
|
|
|
|
// send out the results via the web-socket
|
|
if err := conn.WriteJSON(result); err != nil {
|
|
conn.Close()
|
|
return
|
|
}
|
|
|
|
// Check whether to continue. If the limit is used break once all done.
|
|
if (useLimit && limit <= 0) || result.Status == 1 {
|
|
break
|
|
}
|
|
}
|
|
}*/
|
|
|
|
/*
|
|
apiSearchTerminate terminates a search
|
|
Request: GET /search/terminate?id=[UUID]
|
|
Response: 204 Empty
|
|
400 Invalid input
|
|
404 ID not found
|
|
*/
|
|
func 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
|
|
}
|
|
|
|
// look up the job
|
|
job := JobLookup(jobID)
|
|
if job == nil {
|
|
http.Error(w, "", http.StatusNotFound)
|
|
return
|
|
}
|
|
|
|
// terminate and remove it from the list
|
|
job.Terminate()
|
|
job.Remove()
|
|
|
|
w.WriteHeader(http.StatusNoContent)
|
|
}
|
|
|
|
/*
|
|
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]
|
|
Result: 200 with JSON structure SearchResult. Check the field status.
|
|
*/
|
|
func apiExplore(w http.ResponseWriter, r *http.Request) {
|
|
r.ParseForm()
|
|
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
|
|
}
|
|
|
|
resultFiles := queryRecentShared(fileType, limit)
|
|
|
|
var result SearchResult
|
|
result.Files = []apiBlockRecordFile{}
|
|
|
|
// loop over results
|
|
for n := range resultFiles {
|
|
result.Files = append(result.Files, blockRecordFileToAPI(*resultFiles[n]))
|
|
}
|
|
|
|
if len(result.Files) == 0 {
|
|
result.Status = 3 // No results yet available keep trying
|
|
}
|
|
|
|
result.Status = 1 // No more results to expect
|
|
|
|
EncodeJSON(w, r, result)
|
|
}
|