save peers RIVM-31

This commit is contained in:
Mihail Slobodyanuk 2022-12-16 22:58:12 +02:00
parent d68d3db702
commit 0b54277ece
7 changed files with 164 additions and 165 deletions

View file

@ -1,14 +1,12 @@
package main package main
import ( import (
"bytes"
"context" "context"
"crypto/ed25519" "crypto/ed25519"
"encoding/hex" "encoding/hex"
"encoding/json" "encoding/json"
"flag" "flag"
"fmt" "fmt"
"io"
"net" "net"
"os" "os"
"os/signal" "os/signal"
@ -17,13 +15,10 @@ import (
"sync" "sync"
"syscall" "syscall"
"golang.org/x/text/encoding/unicode"
"github.com/gologme/log" "github.com/gologme/log"
gsyslog "github.com/hashicorp/go-syslog" gsyslog "github.com/hashicorp/go-syslog"
"github.com/hjson/hjson-go" "github.com/hjson/hjson-go"
"github.com/kardianos/minwinsvc" "github.com/kardianos/minwinsvc"
"github.com/mitchellh/mapstructure"
//"github.com/RiV-chain/RiV-mesh/src/address" //"github.com/RiV-chain/RiV-mesh/src/address"
"github.com/RiV-chain/RiV-mesh/src/admin" "github.com/RiV-chain/RiV-mesh/src/admin"
@ -44,77 +39,6 @@ type node struct {
admin *admin.AdminSocket admin *admin.AdminSocket
} }
func readConfig(log *log.Logger, useconf bool, useconffile string, normaliseconf bool) *config.NodeConfig {
// Use a configuration file. If -useconf, the configuration will be read
// from stdin. If -useconffile, the configuration will be read from the
// filesystem.
var conf []byte
var err error
if useconffile != "" {
// Read the file from the filesystem
conf, err = os.ReadFile(useconffile)
} else {
// Read the file from stdin.
conf, err = io.ReadAll(os.Stdin)
}
if err != nil {
panic(err)
}
// If there's a byte order mark - which Windows 10 is now incredibly fond of
// throwing everywhere when it's converting things into UTF-16 for the hell
// of it - remove it and decode back down into UTF-8. This is necessary
// because hjson doesn't know what to do with UTF-16 and will panic
if bytes.Equal(conf[0:2], []byte{0xFF, 0xFE}) ||
bytes.Equal(conf[0:2], []byte{0xFE, 0xFF}) {
utf := unicode.UTF16(unicode.BigEndian, unicode.UseBOM)
decoder := utf.NewDecoder()
conf, err = decoder.Bytes(conf)
if err != nil {
panic(err)
}
}
// Generate a new configuration - this gives us a set of sane defaults -
// then parse the configuration we loaded above on top of it. The effect
// of this is that any configuration item that is missing from the provided
// configuration will use a sane default.
cfg := defaults.GenerateConfig()
var dat map[string]interface{}
if err := hjson.Unmarshal(conf, &dat); err != nil {
panic(err)
}
// Sanitise the config
confJson, err := json.Marshal(dat)
if err != nil {
panic(err)
}
if err := json.Unmarshal(confJson, &cfg); err != nil {
panic(err)
}
// Overlay our newly mapped configuration onto the autoconf node config that
// we generated above.
if err = mapstructure.Decode(dat, &cfg); err != nil {
panic(err)
}
return cfg
}
// Generates a new configuration and returns it in HJSON format. This is used
// with -genconf.
func doGenconf(isjson bool) string {
cfg := defaults.GenerateConfig()
var bs []byte
var err error
if isjson {
bs, err = json.MarshalIndent(cfg, "", " ")
} else {
bs, err = hjson.Marshal(cfg)
}
if err != nil {
panic(err)
}
return string(bs)
}
func setLogLevel(loglevel string, logger *log.Logger) { func setLogLevel(loglevel string, logger *log.Logger) {
levels := [...]string{"error", "warn", "info", "debug", "trace"} levels := [...]string{"error", "warn", "info", "debug", "trace"}
loglevel = strings.ToLower(loglevel) loglevel = strings.ToLower(loglevel)
@ -229,7 +153,10 @@ func run(args yggArgs, ctx context.Context) {
cfg = defaults.GenerateConfig() cfg = defaults.GenerateConfig()
case args.useconffile != "" || args.useconf: case args.useconffile != "" || args.useconf:
// Read the configuration from either stdin or from the filesystem // Read the configuration from either stdin or from the filesystem
cfg = readConfig(logger, args.useconf, args.useconffile, args.normaliseconf) cfg, err = defaults.ReadConfig(args.useconffile)
if err != nil {
panic("Configuration file load error: " + err.Error())
}
// If the -normaliseconf option was specified then remarshal the above // If the -normaliseconf option was specified then remarshal the above
// configuration and print it back to stdout. This lets the user update // configuration and print it back to stdout. This lets the user update
// their configuration file with newly mapped names (like above) or to // their configuration file with newly mapped names (like above) or to
@ -249,7 +176,7 @@ func run(args yggArgs, ctx context.Context) {
} }
case args.genconf: case args.genconf:
// Generate a new configuration and print it to stdout. // Generate a new configuration and print it to stdout.
fmt.Println(doGenconf(args.confjson)) fmt.Println(defaults.Genconf(args.confjson))
return return
default: default:
// No flags were provided, therefore print the list of flags to stdout. // No flags were provided, therefore print the list of flags to stdout.
@ -391,7 +318,7 @@ func run(args yggArgs, ctx context.Context) {
logger.Infof("Your IPv6 address is %s", address.String()) logger.Infof("Your IPv6 address is %s", address.String())
logger.Infof("Your IPv6 subnet is %s", subnet.String()) logger.Infof("Your IPv6 subnet is %s", subnet.String())
// Start HTTP server // Start HTTP server
n.admin.StartHttpServer(cfg) n.admin.StartHttpServer(args.useconffile, cfg)
// Block until we are told to shut down. // Block until we are told to shut down.
<-ctx.Done() <-ctx.Done()

File diff suppressed because one or more lines are too long

View file

@ -68,8 +68,13 @@ img {
display: block; display: block;
} }
html, body { html, body {
height: 100vh; height: 100vh;
}
body {
max-width: 690px;
margin: auto;
} }
td.fi { td.fi {

View file

@ -4,6 +4,7 @@ var $$ = clazz => document.getElementsByClassName(clazz)
function setPingValue(peer, value) { function setPingValue(peer, value) {
var cellText; var cellText;
var peerCell = $(peer); var peerCell = $(peer);
if (!peerCell) return;
var peerTable = $("peer_list"); var peerTable = $("peer_list");
if (value === "-1") { if (value === "-1") {
var peerAddress = $("label_" + peer); var peerAddress = $("label_" + peer);
@ -148,7 +149,7 @@ function showWindow(text) {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
'Riv-Save-Config': 'true', 'Riv-Save-Config': 'true',
}, },
body: JSON.stringify({"peers": peer_list}), body: JSON.stringify(peer_list),
}) })
.catch((error) => { .catch((error) => {
console.error('Error:', error); console.error('Error:', error);
@ -315,6 +316,7 @@ ui.updateSelfInfo = () =>
ui.getSelfInfo() ui.getSelfInfo()
.then((info) => { .then((info) => {
$("ipv6").innerText = info.address; $("ipv6").innerText = info.address;
$("subnet").innerText = info.subnet;
$("pub_key").innerText = info.key; $("pub_key").innerText = info.key;
$("priv_key").innerText = info.private_key; $("priv_key").innerText = info.private_key;
$("ipv6").innerText = info.address; $("ipv6").innerText = info.address;

View file

@ -1,14 +1,11 @@
package main package main
import ( import (
"bytes"
"log" "log"
"os" "os"
"github.com/RiV-chain/RiV-mesh/src/defaults" "github.com/RiV-chain/RiV-mesh/src/defaults"
"github.com/hjson/hjson-go"
"github.com/webview/webview" "github.com/webview/webview"
"golang.org/x/text/encoding/unicode"
"github.com/docopt/docopt-go" "github.com/docopt/docopt-go"
) )
@ -46,35 +43,10 @@ func main() {
w.SetSize(690, 920, webview.HintFixed) w.SetSize(690, 920, webview.HintFixed)
if confui.IndexHtml == "" { if confui.IndexHtml == "" {
confui.IndexHtml = getEndpoint() confui.IndexHtml = defaults.GetHttpEndpoint("http://localhost:19019")
}
if confui.IndexHtml == "" {
confui.IndexHtml = "http://localhost:19019"
} }
log.Printf("Opening: %v", confui.IndexHtml) log.Printf("Opening: %v", confui.IndexHtml)
w.Navigate(confui.IndexHtml) w.Navigate(confui.IndexHtml)
w.Run() w.Run()
} }
func getEndpoint() string {
if config, err := os.ReadFile(defaults.GetDefaults().DefaultConfigFile); err == nil {
if bytes.Equal(config[0:2], []byte{0xFF, 0xFE}) ||
bytes.Equal(config[0:2], []byte{0xFE, 0xFF}) {
utf := unicode.UTF16(unicode.BigEndian, unicode.UseBOM)
decoder := utf.NewDecoder()
config, err = decoder.Bytes(config)
if err != nil {
return ""
}
}
var dat map[string]interface{}
if err := hjson.Unmarshal(config, &dat); err != nil {
return ""
}
if ep, ok := dat["HttpAddress"].(string); ok && (ep != "none" && ep != "") {
return ep
}
}
return ""
}

View file

@ -19,6 +19,7 @@ import (
"github.com/RiV-chain/RiV-mesh/src/config" "github.com/RiV-chain/RiV-mesh/src/config"
"github.com/RiV-chain/RiV-mesh/src/core" "github.com/RiV-chain/RiV-mesh/src/core"
"github.com/RiV-chain/RiV-mesh/src/defaults"
) )
// TODO: Add authentication // TODO: Add authentication
@ -246,7 +247,7 @@ func (a *AdminSocket) SetupAdminHandlers() {
} }
// Start runs http server // Start runs http server
func (a *AdminSocket) StartHttpServer(nc *config.NodeConfig) { func (a *AdminSocket) StartHttpServer(configFn string, nc *config.NodeConfig) {
if nc.HttpAddress != "none" && nc.HttpAddress != "" && nc.WwwRoot != "none" && nc.WwwRoot != "" { if nc.HttpAddress != "none" && nc.HttpAddress != "" && nc.WwwRoot != "none" && nc.WwwRoot != "" {
u, err := url.Parse(nc.HttpAddress) u, err := url.Parse(nc.HttpAddress)
if err != nil { if err != nil {
@ -275,6 +276,46 @@ func (a *AdminSocket) StartHttpServer(nc *config.NodeConfig) {
} }
}) })
http.HandleFunc("/api/peers", func(w http.ResponseWriter, r *http.Request) { http.HandleFunc("/api/peers", func(w http.ResponseWriter, r *http.Request) {
var handleDelete = func() error {
err := a.core.RemovePeers()
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
return err
}
var handlePost = func() error {
var peers []string
err := json.NewDecoder(r.Body).Decode(&peers)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return err
}
for _, peer := range peers {
if err := a.core.AddPeer(peer, ""); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return err
}
}
if len(configFn) > 0 {
saveHeaders := r.Header["Riv-Save-Config"]
if len(saveHeaders) > 0 && saveHeaders[0] == "true" {
cfg, err := defaults.ReadConfig(configFn)
if err == nil {
cfg.Peers = peers
err := defaults.WriteConfig(configFn, cfg)
if err != nil {
a.log.Errorln("Config file read error:", err)
}
} else {
a.log.Errorln("Config file read error:", err)
}
}
}
return nil
}
switch r.Method { switch r.Method {
case "GET": case "GET":
w.Header().Add("Content-Type", "application/json") w.Header().Add("Content-Type", "application/json")
@ -290,61 +331,17 @@ func (a *AdminSocket) StartHttpServer(nc *config.NodeConfig) {
} }
fmt.Fprint(w, string(b[:])) fmt.Fprint(w, string(b[:]))
case "POST": case "POST":
req := &AddPeersRequest{} handlePost()
res := &AddPeersResponse{}
err := json.NewDecoder(r.Body).Decode(&req)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
if err := a.addPeersHandler(req, res); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
b, err := json.Marshal(res)
if err != nil {
http.Error(w, err.Error(), 503)
}
w.Header().Add("Content-Type", "application/json")
fmt.Fprint(w, string(b[:]))
case "PUT": case "PUT":
err := a.core.RemovePeers() if handleDelete() == nil {
if err != nil { if handlePost() == nil {
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, "No content", http.StatusNoContent)
}
} }
req := &AddPeersRequest{}
res := &AddPeersResponse{}
err = json.NewDecoder(r.Body).Decode(&req)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
if err := a.addPeersHandler(req, res); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
b, err := json.Marshal(res)
if err != nil {
http.Error(w, err.Error(), 503)
}
w.Header().Add("Content-Type", "application/json")
fmt.Fprint(w, string(b[:]))
//TODO save peers
// saveHeaders := r.Header["Riv-Save-Config"]
// if len(saveHeaders) > 0 && saveHeaders[0] == "true" {
// nc.Peers =
// }
case "DELETE": case "DELETE":
err := a.core.RemovePeers() if handleDelete() == nil {
if err != nil { http.Error(w, "No content", http.StatusNoContent)
http.Error(w, err.Error(), http.StatusInternalServerError)
} }
http.Error(w, "No content", http.StatusNoContent)
default: default:
http.Error(w, "Method Not Allowed", 405) http.Error(w, "Method Not Allowed", 405)
} }

View file

@ -1,6 +1,16 @@
package defaults package defaults
import "github.com/RiV-chain/RiV-mesh/src/config" import (
"bytes"
"encoding/json"
"io"
"os"
"github.com/RiV-chain/RiV-mesh/src/config"
"github.com/hjson/hjson-go"
"github.com/mitchellh/mapstructure"
"golang.org/x/text/encoding/unicode"
)
type MulticastInterfaceConfig = config.MulticastInterfaceConfig type MulticastInterfaceConfig = config.MulticastInterfaceConfig
type NetworkDomainConfig = config.NetworkDomainConfig type NetworkDomainConfig = config.NetworkDomainConfig
@ -77,3 +87,89 @@ func GenerateConfig() *config.NodeConfig {
return cfg return cfg
} }
func Genconf(isjson bool) string {
cfg := GenerateConfig()
var bs []byte
if isjson {
bs, _ = json.MarshalIndent(cfg, "", " ")
} else {
bs, _ = hjson.Marshal(cfg)
}
return string(bs)
}
func ReadConfig(useconffile string) (*config.NodeConfig, error) {
// Use a configuration file. If -useconf, the configuration will be read
// from stdin. If -useconffile, the configuration will be read from the
// filesystem.
var conf []byte
var err error
if useconffile != "" {
// Read the file from the filesystem
conf, err = os.ReadFile(useconffile)
} else {
// Read the file from stdin.
conf, err = io.ReadAll(os.Stdin)
}
if err != nil {
return nil, err
}
// If there's a byte order mark - which Windows 10 is now incredibly fond of
// throwing everywhere when it's converting things into UTF-16 for the hell
// of it - remove it and decode back down into UTF-8. This is necessary
// because hjson doesn't know what to do with UTF-16 and will panic
if bytes.Equal(conf[0:2], []byte{0xFF, 0xFE}) ||
bytes.Equal(conf[0:2], []byte{0xFE, 0xFF}) {
utf := unicode.UTF16(unicode.BigEndian, unicode.UseBOM)
decoder := utf.NewDecoder()
conf, err = decoder.Bytes(conf)
if err != nil {
return nil, err
}
}
// Generate a new configuration - this gives us a set of sane defaults -
// then parse the configuration we loaded above on top of it. The effect
// of this is that any configuration item that is missing from the provided
// configuration will use a sane default.
cfg := GenerateConfig()
var dat map[string]interface{}
if err := hjson.Unmarshal(conf, &dat); err != nil {
return nil, err
}
// Sanitise the config
confJson, err := json.Marshal(dat)
if err != nil {
return nil, err
}
if err := json.Unmarshal(confJson, &cfg); err != nil {
return nil, err
}
// Overlay our newly mapped configuration onto the autoconf node config that
// we generated above.
if err = mapstructure.Decode(dat, &cfg); err != nil {
return nil, err
}
return cfg, nil
}
func WriteConfig(confFn string, cfg *config.NodeConfig) error {
bs, err := hjson.Marshal(cfg)
if err != nil {
return err
}
err = os.WriteFile(confFn, bs, 0644)
if err != nil {
return err
}
return nil
}
func GetHttpEndpoint(defaultEndpoint string) string {
if config, err := ReadConfig(GetDefaults().DefaultConfigFile); err == nil {
if ep := config.HttpAddress; ep != "none" && ep != "" {
return ep
}
}
return defaultEndpoint
}