From 6d6a25b1d9957aaf88c64b1f24e9543c20d63ab5 Mon Sep 17 00:00:00 2001 From: Kleissner Date: Mon, 9 Aug 2021 10:19:05 +0200 Subject: [PATCH] Start with API development. First endpoints are /status and /peer/self. More in development. --- API.go | 166 ++++++++++++++++++++++++++++++++++++++++++++++++ API.md | 91 ++++++++++++++++++++++++++ Command Line.go | 144 ++++++++++++++++++++--------------------- Main.go | 12 +++- go.mod | 2 + go.sum | 4 ++ 6 files changed, 346 insertions(+), 73 deletions(-) create mode 100644 API.go create mode 100644 API.md diff --git a/API.go b/API.go new file mode 100644 index 0000000..59f76c2 --- /dev/null +++ b/API.go @@ -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 + //} + } +} diff --git a/API.md b/API.md new file mode 100644 index 0000000..f9903d8 --- /dev/null +++ b/API.md @@ -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 +``` diff --git a/Command Line.go b/Command Line.go index efe8a47..01a52ec 100644 --- a/Command Line.go +++ b/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") } } } diff --git a/Main.go b/Main.go index 33f2486..c637d1f 100644 --- a/Main.go +++ b/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) } diff --git a/go.mod b/go.mod index cdb95e1..8c9b26d 100644 --- a/go.mod +++ b/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 ) diff --git a/go.sum b/go.sum index 8e0fcbf..b6eb75e 100644 --- a/go.sum +++ b/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=