mirror of
https://github.com/PeernetOfficial/Cmd.git
synced 2026-07-20 04:17:50 +01:00
Start with API development. First endpoints are /status and /peer/self. More in development.
This commit is contained in:
166
API.go
Normal file
166
API.go
Normal file
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
File Name: API.go
|
||||
Copyright: 2021 Peernet Foundation s.r.o.
|
||||
Author: Peter Kleissner
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/PeernetOfficial/core"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
func startAPI() {
|
||||
if len(config.APIListen) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// by default allow all requests
|
||||
wsUpgrader.CheckOrigin = func(r *http.Request) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
router := mux.NewRouter()
|
||||
|
||||
router.HandleFunc("/test", apiTest).Methods("GET")
|
||||
router.HandleFunc("/status", apiStatus).Methods("GET")
|
||||
router.HandleFunc("/peer/self", apiPeerSelf).Methods("GET")
|
||||
router.HandleFunc("/console", apiConsole).Methods("GET")
|
||||
|
||||
for _, listen := range config.APIListen {
|
||||
go startWebServer(listen, config.APIUseSSL, config.APICertificateFile, config.APICertificateKey, router, "API", parseDuration(config.APITimeoutRead), parseDuration(config.APITimeoutWrite))
|
||||
}
|
||||
}
|
||||
|
||||
// startWebServer starts a web-server with given parameters and logs the status. If may block forever and only returns if there is an error.
|
||||
func startWebServer(WebListen string, UseSSL bool, CertificateFile, CertificateKey string, Handler http.Handler, Info string, ReadTimeout, WriteTimeout time.Duration) {
|
||||
tlsConfig := &tls.Config{MinVersion: tls.VersionTLS12} // for security reasons disable TLS 1.0/1.1
|
||||
|
||||
server := &http.Server{
|
||||
Addr: WebListen,
|
||||
Handler: Handler,
|
||||
ReadTimeout: ReadTimeout, // ReadTimeout is the maximum duration for reading the entire request, including the body.
|
||||
WriteTimeout: WriteTimeout, // WriteTimeout is the maximum duration before timing out writes of the response. This includes processing time and is therefore the max time any HTTP function may take.
|
||||
//IdleTimeout: IdleTimeout, // IdleTimeout is the maximum amount of time to wait for the next request when keep-alives are enabled.
|
||||
TLSConfig: tlsConfig,
|
||||
}
|
||||
|
||||
if UseSSL {
|
||||
// HTTPS
|
||||
if err := server.ListenAndServeTLS(CertificateFile, CertificateKey); err != nil {
|
||||
log.Printf("Error listening on '%s': %v\n", WebListen, err)
|
||||
}
|
||||
} else {
|
||||
// HTTP
|
||||
if err := server.ListenAndServe(); err != nil {
|
||||
log.Printf("Error listening on '%s': %v\n", WebListen, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// parseDuration is the same as time.ParseDuration without returning an error. Valid units are ms, s, m, h. For example "10s".
|
||||
func parseDuration(input string) (result time.Duration) {
|
||||
result, _ = time.ParseDuration(input)
|
||||
return
|
||||
}
|
||||
|
||||
func apiEncodeJSON(w http.ResponseWriter, r *http.Request, data interface{}) (err error) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
err = json.NewEncoder(w).Encode(data)
|
||||
if err != nil {
|
||||
log.Printf("Error writing data for route '%s': %v\n", r.URL.Path, err)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func apiTest(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte("ok"))
|
||||
}
|
||||
|
||||
type apiResponseStatus struct {
|
||||
Status int `json:"status"` // Status code: 0 = Ok.
|
||||
IsConnected bool `json:"isconnected"` // Whether connected to Peernet.
|
||||
CountPeerList int `json:"countpeerlist"` // Count of peers in the peer list. Note that this contains peers that are considered inactive, but have not yet been removed from the list.
|
||||
}
|
||||
|
||||
/* apiStatus returns the current connectivity status to the network
|
||||
Request: GET /status
|
||||
Result: 200 with JSON structure Status
|
||||
*/
|
||||
func apiStatus(w http.ResponseWriter, r *http.Request) {
|
||||
status := apiResponseStatus{Status: 0, CountPeerList: core.PeerlistCount()}
|
||||
|
||||
// Connected: If at leat 2 peers.
|
||||
// This metric needs to be improved in the future, as root peers never disconnect.
|
||||
// Instead, the core should keep a count of "active peers".
|
||||
status.IsConnected = status.CountPeerList >= 2
|
||||
|
||||
apiEncodeJSON(w, r, status)
|
||||
}
|
||||
|
||||
type apiResponsePeerSelf struct {
|
||||
PeerID string `json:"peerid"` // Peer ID. This is derived from the public in compressed form.
|
||||
NodeID string `json:"nodeid"` // Node ID. This is the blake3 hash of the peer ID and used in the DHT.
|
||||
}
|
||||
|
||||
/* apiPeerSelf provides information about the self peer details
|
||||
Request: GET /peer/self
|
||||
Result: 200 with JSON structure apiResponsePeerSelf
|
||||
*/
|
||||
func apiPeerSelf(w http.ResponseWriter, r *http.Request) {
|
||||
response := apiResponsePeerSelf{}
|
||||
response.NodeID = hex.EncodeToString(core.SelfNodeID())
|
||||
|
||||
_, publicKey := core.ExportPrivateKey()
|
||||
response.PeerID = hex.EncodeToString(publicKey.SerializeCompressed())
|
||||
|
||||
apiEncodeJSON(w, r, response)
|
||||
}
|
||||
|
||||
var wsUpgrader = websocket.Upgrader{} // use default options
|
||||
|
||||
/* apiConsole provides a websocket to send/receive internal commands
|
||||
Request: GET /console
|
||||
Result: 200 with JSON structure apiResponsePeerSelf
|
||||
*/
|
||||
func apiConsole(w http.ResponseWriter, r *http.Request) {
|
||||
c, err := wsUpgrader.Upgrade(w, r, nil)
|
||||
if err != nil {
|
||||
// May happen if request is simple HTTP request.
|
||||
return
|
||||
}
|
||||
|
||||
// start reader/writer to forward
|
||||
|
||||
defer c.Close()
|
||||
for {
|
||||
_, message, err := c.ReadMessage()
|
||||
if err != nil {
|
||||
//fmt.Println("read:", err)
|
||||
break
|
||||
}
|
||||
//c.NextWriter(websocket.BinaryMessage)
|
||||
|
||||
//c.NextReader()
|
||||
//userCommands()
|
||||
fmt.Printf("recv: %s", message)
|
||||
//err = c.WriteMessage(mt, message)
|
||||
//if err != nil {
|
||||
// //fmt.Println("write:", err)
|
||||
// break
|
||||
//}
|
||||
}
|
||||
}
|
||||
91
API.md
Normal file
91
API.md
Normal file
@@ -0,0 +1,91 @@
|
||||
# API
|
||||
|
||||
The API is intended to be used by a local full standalone client that provides a frontend for the end-user. The API allows to use Peernet effectively; it provides functions to share files, search for content and download files.
|
||||
|
||||
Note: This API code is likely to be moved into the core soon.
|
||||
|
||||
## Use considerations
|
||||
|
||||
It is not intended to be directly used in a legacy web browser, and shall not be exposed to the internet. It shall only run on a loopback IP such as `127.0.0.1` or `::1`. Special HTTP headers (including the Access-Control headers) are intentionally not set.
|
||||
|
||||
The API is currently unauthenticated and intentionally provides direct access to the users blockchain.
|
||||
|
||||
### Notes
|
||||
|
||||
The API is still in development and endpoints are subject to change. The API should be currently only used for debugging and early phase development purposes.
|
||||
|
||||
## Configuartion
|
||||
|
||||
The configuration file (default `Config.yaml`) contains settings for the API.
|
||||
|
||||
## Overview
|
||||
|
||||
These are the functions provided by the API:
|
||||
|
||||
```
|
||||
/status Provides current connectivity status to the network
|
||||
/peer/self Provides information about the self peer details
|
||||
/blockchain/self/header Header of the self peers blockchain
|
||||
/blockchain/self/read Read the self peers blockchain
|
||||
/blockchain/self/append Add a record to the blockchain
|
||||
/share/list List all files and directories that are shared
|
||||
/share/file Share a file via the peers blockchain
|
||||
|
||||
/search Search Peernet for a file based on keywords or hash
|
||||
/download/start Download a file based on a hash
|
||||
/download/status Get the status of a download
|
||||
|
||||
/status/ws Starts a websocket to receive updates on operations immediately (push instead of pull)
|
||||
/console Console provides a websocket to send/receive internal commands
|
||||
```
|
||||
|
||||
The `/share` functions are providing high-level functionality to work with files; the `/blockchain` functions provide low-level functionality.
|
||||
|
||||
## Status
|
||||
|
||||
This function informs about the current connection status of the client to the network. Additional fields will be added in the future.
|
||||
|
||||
```
|
||||
Request: GET /status
|
||||
|
||||
Response: 200 with JSON structure apiResponseStatus
|
||||
```
|
||||
|
||||
```go
|
||||
type apiResponseStatus struct {
|
||||
Status int `json:"status"` // Status code: 0 = Ok.
|
||||
IsConnected bool `json:"isconnected"` // Whether connected to Peernet.
|
||||
CountPeerList int `json:"countpeerlist"` // Count of peers in the peer list. Note that this contains peers that are considered inactive, but have not yet been removed from the list.
|
||||
}
|
||||
```
|
||||
|
||||
Example response data: Todo
|
||||
|
||||
## Self Information
|
||||
|
||||
This function returns information about the current peer.
|
||||
|
||||
```
|
||||
Request: GET /peer/self
|
||||
|
||||
Response: 200 with JSON structure apiResponsePeerSelf
|
||||
```
|
||||
|
||||
The peer and node IDs are returned as hex encoded strings.
|
||||
|
||||
```go
|
||||
type apiResponsePeerSelf struct {
|
||||
PeerID string `json:"peerid"` // Peer ID. This is derived from the public in compressed form.
|
||||
NodeID string `json:"nodeid"` // Node ID. This is the blake3 hash of the peer ID and used in the DHT.
|
||||
}
|
||||
```
|
||||
|
||||
Example response data: Todo
|
||||
|
||||
## Console
|
||||
|
||||
The `/console` web-socket allows to execute internal commands. This should be only used for debugging purposes by the end-user. The same input and output as raw text as via the command-line is provided through this endpoint.
|
||||
|
||||
```
|
||||
Request: ws://127.0.0.1:112/console
|
||||
```
|
||||
144
Command Line.go
144
Command Line.go
@@ -11,8 +11,8 @@ import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -82,33 +82,33 @@ func getUserOptionHash(reader *bufio.Reader) (hash []byte, valid bool) {
|
||||
return hash, true
|
||||
}
|
||||
|
||||
func showHelp() {
|
||||
fmt.Print("Please enter a command:\n")
|
||||
fmt.Print("help Show this help\n" +
|
||||
"net list Lists all network adapters and their IPs\n" +
|
||||
"status Get current status\n" +
|
||||
"chat Send text to all peers\n" +
|
||||
"peer list List current peers\n" +
|
||||
"debug key create Create Public-Private Key pair\n" +
|
||||
"debug key self List current Public-Private Key pair\n" +
|
||||
"debug connect Attempts to connect to the target peer\n" +
|
||||
"debug watch searches Watch all outgoing DHT searches\n" +
|
||||
"debug watch incoming Watch all incoming information requests\n" +
|
||||
"debug watch Watch packets and info requests for hash\n" +
|
||||
"hash Create blake3 hash of input\n" +
|
||||
"warehouse get Get data from local warehouse by hash\n" +
|
||||
"warehouse store Store data into local warehouse\n" +
|
||||
"dht get Get data via DHT by hash\n" +
|
||||
"dht store Store data into DHT\n" +
|
||||
"log error Set error log output\n" +
|
||||
func showHelp(output io.Writer) {
|
||||
fmt.Fprint(output, "Please enter a command:\n"+
|
||||
"help Show this help\n"+
|
||||
"net list Lists all network adapters and their IPs\n"+
|
||||
"status Get current status\n"+
|
||||
"chat Send text to all peers\n"+
|
||||
"peer list List current peers\n"+
|
||||
"debug key create Create Public-Private Key pair\n"+
|
||||
"debug key self List current Public-Private Key pair\n"+
|
||||
"debug connect Attempts to connect to the target peer\n"+
|
||||
"debug watch searches Watch all outgoing DHT searches\n"+
|
||||
"debug watch incoming Watch all incoming information requests\n"+
|
||||
"debug watch Watch packets and info requests for hash\n"+
|
||||
"hash Create blake3 hash of input\n"+
|
||||
"warehouse get Get data from local warehouse by hash\n"+
|
||||
"warehouse store Store data into local warehouse\n"+
|
||||
"dht get Get data via DHT by hash\n"+
|
||||
"dht store Store data into DHT\n"+
|
||||
"log error Set error log output\n"+
|
||||
"\n")
|
||||
}
|
||||
|
||||
func userCommands() {
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
func userCommands(input io.Reader, output io.Writer) {
|
||||
reader := bufio.NewReader(input)
|
||||
|
||||
fmt.Print(appName + " " + core.Version + "\n------------------------------\n")
|
||||
showHelp()
|
||||
fmt.Fprint(output, appName+" "+core.Version+"\n------------------------------\n")
|
||||
showHelp(output)
|
||||
|
||||
for {
|
||||
command, valid := getUserOptionString(reader)
|
||||
@@ -120,25 +120,25 @@ func userCommands() {
|
||||
|
||||
switch command {
|
||||
case "help", "?":
|
||||
showHelp()
|
||||
showHelp(output)
|
||||
|
||||
case "net list":
|
||||
fmt.Print(NetworkListOutput())
|
||||
fmt.Fprint(output, NetworkListOutput())
|
||||
|
||||
case "debug key create":
|
||||
privateKey, publicKey, err := core.Secp256k1NewPrivateKey()
|
||||
if err != nil {
|
||||
fmt.Printf("Error: %s\n", err.Error())
|
||||
fmt.Fprintf(output, "Error: %s\n", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("Private Key: %s\n", hex.EncodeToString(privateKey.Serialize()))
|
||||
fmt.Printf("Public Key: %s\n", hex.EncodeToString(publicKey.SerializeCompressed()))
|
||||
fmt.Fprintf(output, "Private Key: %s\n", hex.EncodeToString(privateKey.Serialize()))
|
||||
fmt.Fprintf(output, "Public Key: %s\n", hex.EncodeToString(publicKey.SerializeCompressed()))
|
||||
|
||||
case "debug key self":
|
||||
privateKey, publicKey := core.ExportPrivateKey()
|
||||
fmt.Printf("Private Key: %s\n", hex.EncodeToString(privateKey.Serialize()))
|
||||
fmt.Printf("Public Key: %s\n", hex.EncodeToString(publicKey.SerializeCompressed()))
|
||||
fmt.Fprintf(output, "Private Key: %s\n", hex.EncodeToString(privateKey.Serialize()))
|
||||
fmt.Fprintf(output, "Public Key: %s\n", hex.EncodeToString(publicKey.SerializeCompressed()))
|
||||
|
||||
case "peer list":
|
||||
for _, peer := range GetPeerlistSorted() {
|
||||
@@ -151,7 +151,7 @@ func userCommands() {
|
||||
}
|
||||
userAgent := strings.ToValidUTF8(peer.UserAgent, "?")
|
||||
|
||||
fmt.Printf("* Peer ID %s%s\n Node ID %s\n User Agent: %s\n\n%s\n Packets sent: %d\n Packets received: %d\n\n", hex.EncodeToString(peer.PublicKey.SerializeCompressed()), info, hex.EncodeToString(peer.NodeID), userAgent, textPeerConnections(peer), peer.StatsPacketSent, peer.StatsPacketReceived)
|
||||
fmt.Fprintf(output, "* Peer ID %s%s\n Node ID %s\n User Agent: %s\n\n%s\n Packets sent: %d\n Packets received: %d\n\n", hex.EncodeToString(peer.PublicKey.SerializeCompressed()), info, hex.EncodeToString(peer.NodeID), userAgent, textPeerConnections(peer), peer.StatsPacketSent, peer.StatsPacketReceived)
|
||||
}
|
||||
|
||||
case "chat all", "chat":
|
||||
@@ -162,7 +162,7 @@ func userCommands() {
|
||||
case "status":
|
||||
_, publicKey := core.ExportPrivateKey()
|
||||
nodeID := core.SelfNodeID()
|
||||
fmt.Printf("----------------\nPublic Key: %s\nNode ID: %s\n\n", hex.EncodeToString(publicKey.SerializeCompressed()), hex.EncodeToString(nodeID))
|
||||
fmt.Fprintf(output, "----------------\nPublic Key: %s\nNode ID: %s\n\n", hex.EncodeToString(publicKey.SerializeCompressed()), hex.EncodeToString(nodeID))
|
||||
|
||||
features := ""
|
||||
featureSupport := core.FeatureSupport()
|
||||
@@ -176,9 +176,9 @@ func userCommands() {
|
||||
features += "IPv6"
|
||||
}
|
||||
|
||||
fmt.Printf("User Agent: %s\nFeatures: %s\n\n", core.UserAgent, features)
|
||||
fmt.Fprintf(output, "User Agent: %s\nFeatures: %s\n\n", core.UserAgent, features)
|
||||
|
||||
fmt.Printf("Listen Address Multicast IP out External Address\n")
|
||||
fmt.Fprintf(output, "Listen Address Multicast IP out External Address\n")
|
||||
|
||||
for _, network := range core.GetNetworks(4) {
|
||||
address, _, broadcastIPv4, ipExternal, externalPort := network.GetListen()
|
||||
@@ -206,7 +206,7 @@ func userCommands() {
|
||||
externalAddress = net.JoinHostPort(externalIPA, externalPortA)
|
||||
}
|
||||
|
||||
fmt.Printf("%-46s %-32s %s\n", address.String(), broadcastIPsA, externalAddress)
|
||||
fmt.Fprintf(output, "%-46s %-32s %s\n", address.String(), broadcastIPsA, externalAddress)
|
||||
}
|
||||
for _, network := range core.GetNetworks(6) {
|
||||
address, multicastIP, _, _, externalPort := network.GetListen()
|
||||
@@ -216,10 +216,10 @@ func userCommands() {
|
||||
externalPortA = strconv.Itoa(int(externalPort))
|
||||
}
|
||||
|
||||
fmt.Printf("%-46s %-31s %s\n", address.String(), multicastIP.String(), externalPortA)
|
||||
fmt.Fprintf(output, "%-46s %-31s %s\n", address.String(), multicastIP.String(), externalPortA)
|
||||
}
|
||||
|
||||
fmt.Printf("\nPeer ID Sent Received IP Flags RTT \n")
|
||||
fmt.Fprintf(output, "\nPeer ID Sent Received IP Flags RTT \n")
|
||||
for _, peer := range GetPeerlistSorted() {
|
||||
addressA := "N/A"
|
||||
rttA := "N/A"
|
||||
@@ -236,75 +236,75 @@ func userCommands() {
|
||||
if peer.IsBehindNAT() {
|
||||
flagsA += "N"
|
||||
}
|
||||
fmt.Printf("%-66s %-8d %-8d %-35s %-6s %-6s\n", hex.EncodeToString(peer.PublicKey.SerializeCompressed()), peer.StatsPacketSent, peer.StatsPacketReceived, addressA, flagsA, rttA)
|
||||
fmt.Fprintf(output, "%-66s %-8d %-8d %-35s %-6s %-6s\n", hex.EncodeToString(peer.PublicKey.SerializeCompressed()), peer.StatsPacketSent, peer.StatsPacketReceived, addressA, flagsA, rttA)
|
||||
}
|
||||
|
||||
fmt.Printf("\n")
|
||||
fmt.Fprintf(output, "\n")
|
||||
|
||||
case "hash":
|
||||
if text, valid := getUserOptionString(reader); valid {
|
||||
hash := core.Data2Hash([]byte(text))
|
||||
fmt.Printf("blake3 hash: %s\n", hex.EncodeToString(hash))
|
||||
fmt.Fprintf(output, "blake3 hash: %s\n", hex.EncodeToString(hash))
|
||||
}
|
||||
|
||||
case "warehouse get":
|
||||
if hash, valid := getUserOptionHash(reader); valid {
|
||||
data, found := core.GetDataLocal(hash)
|
||||
if !found {
|
||||
fmt.Printf("Not found.\n")
|
||||
fmt.Fprintf(output, "Not found.\n")
|
||||
} else {
|
||||
fmt.Printf("Data hex: %s\n", hex.EncodeToString(data))
|
||||
fmt.Printf("Data string: %s\n", string(data))
|
||||
fmt.Fprintf(output, "Data hex: %s\n", hex.EncodeToString(data))
|
||||
fmt.Fprintf(output, "Data string: %s\n", string(data))
|
||||
}
|
||||
} else {
|
||||
fmt.Printf("Invalid hash. Hex-encoded blake3 hash as input is required.\n")
|
||||
fmt.Fprintf(output, "Invalid hash. Hex-encoded blake3 hash as input is required.\n")
|
||||
}
|
||||
|
||||
case "warehouse store":
|
||||
if text, valid := getUserOptionString(reader); valid {
|
||||
if err := core.StoreDataLocal([]byte(text)); err != nil {
|
||||
fmt.Printf("Error storing data: %s\n", err.Error())
|
||||
fmt.Fprintf(output, "Error storing data: %s\n", err.Error())
|
||||
break
|
||||
}
|
||||
fmt.Printf("Stored via hash: %s\n", hex.EncodeToString(core.Data2Hash([]byte(text))))
|
||||
fmt.Fprintf(output, "Stored via hash: %s\n", hex.EncodeToString(core.Data2Hash([]byte(text))))
|
||||
}
|
||||
|
||||
case "dht store":
|
||||
if text, valid := getUserOptionString(reader); valid {
|
||||
if err := core.StoreDataDHT([]byte(text), 5); err != nil {
|
||||
fmt.Printf("Error storing data: %s\n", err.Error())
|
||||
fmt.Fprintf(output, "Error storing data: %s\n", err.Error())
|
||||
break
|
||||
}
|
||||
fmt.Printf("Stored via hash: %s\n", hex.EncodeToString(core.Data2Hash([]byte(text))))
|
||||
fmt.Fprintf(output, "Stored via hash: %s\n", hex.EncodeToString(core.Data2Hash([]byte(text))))
|
||||
}
|
||||
|
||||
case "dht get":
|
||||
if hash, valid := getUserOptionHash(reader); valid {
|
||||
data, sender, found := core.GetDataDHT(hash)
|
||||
if !found {
|
||||
fmt.Printf("Not found.\n")
|
||||
fmt.Fprintf(output, "Not found.\n")
|
||||
} else {
|
||||
fmt.Printf("\nSender: %s\n", hex.EncodeToString(sender))
|
||||
fmt.Printf("Data hex: %s\n", hex.EncodeToString(data))
|
||||
fmt.Printf("Data string: %s\n", string(data))
|
||||
fmt.Fprintf(output, "\nSender: %s\n", hex.EncodeToString(sender))
|
||||
fmt.Fprintf(output, "Data hex: %s\n", hex.EncodeToString(data))
|
||||
fmt.Fprintf(output, "Data string: %s\n", string(data))
|
||||
}
|
||||
} else {
|
||||
fmt.Printf("Invalid hash. Hex-encoded blake3 hash as input is required.\n")
|
||||
fmt.Fprintf(output, "Invalid hash. Hex-encoded blake3 hash as input is required.\n")
|
||||
}
|
||||
|
||||
case "log error":
|
||||
fmt.Printf("Please choose the target output of error messages:\n0 = Log file (default)\n1 = Command line\n2 = Log file + command line\n3 = None\n")
|
||||
fmt.Fprintf(output, "Please choose the target output of error messages:\n0 = Log file (default)\n1 = Command line\n2 = Log file + command line\n3 = None\n")
|
||||
if number, valid := getUserOptionInt(reader); !valid || number < 0 || number > 3 {
|
||||
fmt.Printf("Invalid option.\n")
|
||||
fmt.Fprintf(output, "Invalid option.\n")
|
||||
} else {
|
||||
config.ErrorOutput = number
|
||||
}
|
||||
|
||||
case "debug connect":
|
||||
fmt.Printf("Please specify the target peer to connect to via DHT lookup, either by peer ID or node ID:\n")
|
||||
fmt.Fprintf(output, "Please specify the target peer to connect to via DHT lookup, either by peer ID or node ID:\n")
|
||||
text, valid := getUserOptionString(reader)
|
||||
if !valid || (len(text) != 66 && len(text) != 64) {
|
||||
fmt.Printf("Invalid peer ID or node ID. It must be hex-encoded and 66 (peer ID) or 64 characters (node ID) long.\n")
|
||||
fmt.Fprintf(output, "Invalid peer ID or node ID. It must be hex-encoded and 66 (peer ID) or 64 characters (node ID) long.\n")
|
||||
break
|
||||
}
|
||||
|
||||
@@ -316,13 +316,13 @@ func userCommands() {
|
||||
// Assume peer ID was supplied.
|
||||
publicKeyB, err := hex.DecodeString(text)
|
||||
if err != nil || len(publicKeyB) != 33 {
|
||||
fmt.Printf("Invalid peer ID encoding.\n")
|
||||
fmt.Fprintf(output, "Invalid peer ID encoding.\n")
|
||||
break
|
||||
}
|
||||
|
||||
publicKey, err := btcec.ParsePubKey(publicKeyB, btcec.S256())
|
||||
if err != nil {
|
||||
fmt.Printf("Invalid peer ID (public key decoding failed).\n")
|
||||
fmt.Fprintf(output, "Invalid peer ID (public key decoding failed).\n")
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -330,58 +330,58 @@ func userCommands() {
|
||||
} else {
|
||||
// Node ID was supplied.
|
||||
if nodeID, err = hex.DecodeString(text); err != nil || len(nodeID) != 256/8 {
|
||||
fmt.Printf("Invalid node ID encoding.\n")
|
||||
fmt.Fprintf(output, "Invalid node ID encoding.\n")
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// is self?
|
||||
if bytes.Equal(nodeID, core.SelfNodeID()) {
|
||||
fmt.Printf("Target node is self.\n")
|
||||
fmt.Fprintf(output, "Target node is self.\n")
|
||||
break
|
||||
}
|
||||
|
||||
debugCmdConnect(nodeID)
|
||||
|
||||
case "debug watch searches":
|
||||
fmt.Printf("Enable (1) or disable (0) watching of all outgoing DHT searches? (current setting: %t)\n", enableMonitorAll)
|
||||
fmt.Fprintf(output, "Enable (1) or disable (0) watching of all outgoing DHT searches? (current setting: %t)\n", enableMonitorAll)
|
||||
if number, valid := getUserOptionInt(reader); !valid || number < 0 || number > 1 {
|
||||
fmt.Printf("Invalid option.\n")
|
||||
fmt.Fprintf(output, "Invalid option.\n")
|
||||
} else {
|
||||
enableMonitorAll = number == 1
|
||||
}
|
||||
|
||||
case "debug watch incoming":
|
||||
fmt.Printf("Enable (1) or disable (0) watching of all incoming information requests? (current setting: %t)\n", enableWatchIncomingAll)
|
||||
fmt.Fprintf(output, "Enable (1) or disable (0) watching of all incoming information requests? (current setting: %t)\n", enableWatchIncomingAll)
|
||||
if number, valid := getUserOptionInt(reader); !valid || number < 0 || number > 1 {
|
||||
fmt.Printf("Invalid option.\n")
|
||||
fmt.Fprintf(output, "Invalid option.\n")
|
||||
} else {
|
||||
enableWatchIncomingAll = number == 1
|
||||
}
|
||||
|
||||
case "debug bucket refresh":
|
||||
fmt.Printf("Disable (1) or enable (0) bucket refresh. This can be useful to disable bucket refresh when debugging outgoing DHT searches. (current setting: %t)\n", dht.DisableBucketRefresh)
|
||||
fmt.Fprintf(output, "Disable (1) or enable (0) bucket refresh. This can be useful to disable bucket refresh when debugging outgoing DHT searches. (current setting: %t)\n", dht.DisableBucketRefresh)
|
||||
if number, valid := getUserOptionInt(reader); !valid || number < 0 || number > 1 {
|
||||
fmt.Printf("Invalid option.\n")
|
||||
fmt.Fprintf(output, "Invalid option.\n")
|
||||
} else {
|
||||
dht.DisableBucketRefresh = number == 1
|
||||
}
|
||||
|
||||
case "debug watch":
|
||||
fmt.Printf("Enter hash of data or node ID to watch. This monitors info requests and packets. Enter same hash again to remove from list.\n")
|
||||
fmt.Fprintf(output, "Enter hash of data or node ID to watch. This monitors info requests and packets. Enter same hash again to remove from list.\n")
|
||||
text, _ := getUserOptionString(reader)
|
||||
var hash []byte
|
||||
var err error
|
||||
if hash, err = hex.DecodeString(text); err != nil || len(hash) != 256/8 {
|
||||
fmt.Printf("Invalid hash. Hex-encoded 64 character hash expected.\n")
|
||||
fmt.Fprintf(output, "Invalid hash. Hex-encoded 64 character hash expected.\n")
|
||||
break
|
||||
}
|
||||
|
||||
added := hashMonitorControl(hash, 2)
|
||||
if added {
|
||||
fmt.Printf("The hash was added to the monitoring list.\n")
|
||||
fmt.Fprintf(output, "The hash was added to the monitoring list.\n")
|
||||
} else {
|
||||
fmt.Printf("The hash was removed from the monitoring list.\n")
|
||||
fmt.Fprintf(output, "The hash was removed from the monitoring list.\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
12
Main.go
12
Main.go
@@ -19,6 +19,14 @@ const appName = "Peernet Cmd"
|
||||
var config struct {
|
||||
// Log settings
|
||||
ErrorOutput int `yaml:"ErrorOutput"` // 0 = Log file (default), 1 = Command line, 2 = Log file + command line, 3 = None
|
||||
|
||||
// API settings
|
||||
APIListen []string `yaml:"APIListen"` // WebListen is in format IP:Port and declares where the web-interface should listen on. IP can also be ommitted to listen on any.
|
||||
APIUseSSL bool `yaml:"APIUseSSL"` // Enables SSL.
|
||||
APICertificateFile string `yaml:"APICertificateFile"` // This is the certificate received from the CA. This can also include the intermediate certificate from the CA.
|
||||
APICertificateKey string `yaml:"APICertificateKey"` // This is the private key.
|
||||
APITimeoutRead string `yaml:"HTTPTimeoutRead"` // The maximum duration for reading the entire request, including the body.
|
||||
APITimeoutWrite string `yaml:"HTTPTimeoutWrite"` // The maximum duration before timing out writes of the response. This includes processing time and is therefore the max time any HTTP function may take.
|
||||
}
|
||||
|
||||
func init() {
|
||||
@@ -58,7 +66,9 @@ func init() {
|
||||
}
|
||||
|
||||
func main() {
|
||||
startAPI()
|
||||
|
||||
core.Connect()
|
||||
|
||||
userCommands()
|
||||
userCommands(os.Stdin, os.Stdout)
|
||||
}
|
||||
|
||||
2
go.mod
2
go.mod
@@ -5,4 +5,6 @@ go 1.16
|
||||
require (
|
||||
github.com/PeernetOfficial/core v0.0.0-20210731152914-2937154c8d7b
|
||||
github.com/btcsuite/btcd v0.22.0-beta.0.20210625194946-86a17263b0ff
|
||||
github.com/gorilla/mux v1.8.1-0.20200912192056-d07530f46e1e
|
||||
github.com/gorilla/websocket v1.4.3-0.20210424162022-e8629af678b7
|
||||
)
|
||||
|
||||
4
go.sum
4
go.sum
@@ -22,6 +22,10 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
|
||||
github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/gorilla/mux v1.8.1-0.20200912192056-d07530f46e1e h1:8+CIGbDW29nl9niCoMrpF8+ACFz3rLRpIjuqnx4rNKg=
|
||||
github.com/gorilla/mux v1.8.1-0.20200912192056-d07530f46e1e/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||
github.com/gorilla/websocket v1.4.3-0.20210424162022-e8629af678b7 h1:L89uC9ATI61/V2eNgZYtQHyjjyjEplemB+aky4HdyzQ=
|
||||
github.com/gorilla/websocket v1.4.3-0.20210424162022-e8629af678b7/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
|
||||
Reference in New Issue
Block a user