diff --git a/contrib/deb/generate-gui.sh b/contrib/deb/generate-gui.sh
index a262dbfd..ae90afd0 100755
--- a/contrib/deb/generate-gui.sh
+++ b/contrib/deb/generate-gui.sh
@@ -57,7 +57,7 @@ cat > /tmp/$PKGNAME/usr/share/applications/riv.desktop << EOF
Name=RiV mesh
GenericName=Mesh network
Comment=RiV-mesh is an early-stage implementation of a fully end-to-end encrypted IPv6 network
-Exec=sh -c "/usr/bin/mesh-ui http://localhost:19019"
+Exec=sh -c "/usr/bin/mesh-ui"
Terminal=false
Type=Application
Icon=riv
diff --git a/contrib/macos/create-pkg-gui.sh b/contrib/macos/create-pkg-gui.sh
index 10be6cef..c135ce24 100644
--- a/contrib/macos/create-pkg-gui.sh
+++ b/contrib/macos/create-pkg-gui.sh
@@ -54,7 +54,7 @@ cp contrib/macos/mesh.plist pkgbuild/root/Library/LaunchDaemons
cat > pkgbuild/root/Applications/RiV-mesh.app/Contents/MacOS/open-mesh-ui << EOF
#!/usr/bin/env bash
-exec /Applications/RiV-mesh.app/Contents/MacOS/mesh-ui http://localhost:19019 1>/tmp/mesh-ui.stdout.log 2>/tmp/mesh-ui.stderr.log
+exec /Applications/RiV-mesh.app/Contents/MacOS/mesh-ui 1>/tmp/mesh-ui.stdout.log 2>/tmp/mesh-ui.stderr.log
EOF
# Create the postinstall script
diff --git a/contrib/msi/build-msi-gui.sh b/contrib/msi/build-msi-gui.sh
index 66b9825d..b892e22f 100644
--- a/contrib/msi/build-msi-gui.sh
+++ b/contrib/msi/build-msi-gui.sh
@@ -272,7 +272,6 @@ cat > wix.xml << EOF
@@ -291,7 +290,6 @@ cat > wix.xml << EOF
Description="RiV-mesh is IoT E2E encrypted network"
Directory="DesktopFolder"
Target="[MeshInstallFolder]mesh-ui.exe"
- Arguments="http://localhost:19019"
WorkingDirectory="MeshInstallFolder"/>
wix.xml << EOF
Key="Software\Microsoft\Windows\CurrentVersion\Run"
Name="RiV-mesh client"
Type="string"
- Value='"[MeshInstallFolder]mesh-ui.exe" "http://localhost:19019"' />
+ Value='"[MeshInstallFolder]mesh-ui.exe"' />
ASSISTANCE_START_VIA_REGISTRY
diff --git a/contrib/ui/mesh-ui/ui/assets/mesh-ui.js b/contrib/ui/mesh-ui/ui/assets/mesh-ui.js
index cae90647..f0936cdb 100644
--- a/contrib/ui/mesh-ui/ui/assets/mesh-ui.js
+++ b/contrib/ui/mesh-ui/ui/assets/mesh-ui.js
@@ -120,13 +120,13 @@ function showWindow(text) {
button_info_close.onclick = function () {
message.value = "";
info.classList.add("is-hidden");
- //$("peer_list").remove();
+ $("peer_list").remove();
};
var button_window_close = $("window_close");
button_window_close.onclick = function () {
message.value = "";
info.classList.add("is-hidden");
- //$("peer_list").remove();
+ $("peer_list").remove();
};
var button_window_save = $("window_save");
button_window_save.onclick = function () {
@@ -142,8 +142,18 @@ function showWindow(text) {
peer_list.push(peerURL);
}
}
- savePeers(JSON.stringify(peer_list));
- //$("peer_list").remove();
+ fetch('api/peers', {
+ method: 'PUT',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'Riv-Save-Config': 'true',
+ },
+ body: JSON.stringify({"peers": peer_list}),
+ })
+ .catch((error) => {
+ console.error('Error:', error);
+ });
+ $("peer_list").remove();
};
}
@@ -249,32 +259,44 @@ ui.showAllPeers = () =>
.then((peerList) => {
var peers = add_table(peerList);
//start peers test
- ping(JSON.stringify(peers));
+ fetch('api/ping', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify(peers)
+ })
+ .catch((error) => {
+ console.error('Error:', error);
+ });
}).catch((error) => {
console.error(error);
});
ui.getConnectedPeers = () =>
- fetch('api/getpeers')
+ fetch('api/peers')
.then((response) => response.json())
+ui.updateConnectedPeersHandler = (cont) => {
+ $("peers").innerText = "";
+ const regexStrip = /%[^\]]*/gm;
+ const regexMulticast = /:\/\/\[fe80::/;
+ cont.peers.forEach(peer => {
+ let row = $("peers").appendChild(document.createElement('div'));
+ let flag = row.appendChild(document.createElement("span"));
+ if(peer["remote"].match(regexMulticast))
+ flag.className = "fa fa-thin fa-share-nodes peer-connected-fl";
+ else
+ flag.className = "fi fi-" + ui.lookupCountryCodeByAddress(peer["remote"]) + " peer-connected-fl";
+ row.append(peer["remote"].replace(regexStrip, ""));
+ });
+}
+
ui.updateConnectedPeers = () =>
ui.getConnectedPeers()
- .then((cont) => {
- $("peers").innerText = "";
- const regexStrip = /%[^\]]*/gm;
- const regexMulticast = /:\/\/\[fe80::/;
- cont.peers.forEach(peer => {
- let row = $("peers").appendChild(document.createElement('div'));
- let flag = row.appendChild(document.createElement("span"));
- if(peer["remote"].match(regexMulticast))
- flag.className = "fa fa-thin fa-share-nodes peer-connected-fl";
- else
- flag.className = "fi fi-" + ui.lookupCountryCodeByAddress(peer["remote"]) + " peer-connected-fl";
- row.append(peer["remote"].replace(regexStrip, ""));
- });
- }).catch((error) => {
+ .then(ui.updateConnectedPeersHandler)
+ .catch((error) => {
$("peers").innerText = error.message;
});
@@ -286,7 +308,7 @@ ui.lookupCountryCodeByAddress = (address) => {
}
ui.getSelfInfo = () =>
- fetch('api/getself')
+ fetch('api/self')
.then((response) => response.json())
ui.updateSelfInfo = () =>
@@ -301,6 +323,7 @@ ui.updateSelfInfo = () =>
$("ipv6").innerText = error.message;
});
+ui.sse = new EventSource('/api/sse');
function main() {
@@ -315,7 +338,17 @@ function main() {
setInterval(ui.updateConnectedPeers, 5000);
ui.updateSelfInfo();
- setInterval(ui.updateSelfInfo, 5000);
+ //setInterval(ui.updateSelfInfo, 5000);
+
+ ui.sse.addEventListener("ping", (e) => {
+ let data = JSON.parse(e.data);
+ setPingValue(data.peer, data.value);
+ })
+
+ ui.sse.addEventListener("peers", (e) => {
+ ui.updateConnectedPeersHandler(JSON.parse(e.data));
+ })
+
});
}
diff --git a/contrib/ui/mesh-ui/webview.go b/contrib/ui/mesh-ui/webview.go
index d83e8da9..cdb19afb 100644
--- a/contrib/ui/mesh-ui/webview.go
+++ b/contrib/ui/mesh-ui/webview.go
@@ -2,25 +2,14 @@ package main
import (
"bytes"
- "encoding/json"
- "errors"
- "fmt"
- "io/ioutil"
"log"
- "net"
- "net/url"
"os"
- "path/filepath"
- "strconv"
- "strings"
- "time"
+ "github.com/RiV-chain/RiV-mesh/src/defaults"
"github.com/hjson/hjson-go"
"github.com/webview/webview"
"golang.org/x/text/encoding/unicode"
- "github.com/RiV-chain/RiV-mesh/src/admin"
- "github.com/RiV-chain/RiV-mesh/src/defaults"
"github.com/docopt/docopt-go"
)
@@ -32,7 +21,7 @@ Usage:
mesh-ui -v | --version
Options:
- Index file name [default: index.html].
+ Index file name [default: http://localhost:19019].
-c --console Show debug console window.
-h --help Show this screen.
-v --version Show version.`
@@ -55,224 +44,20 @@ func main() {
defer w.Destroy()
w.SetTitle("RiV-mesh")
w.SetSize(690, 920, webview.HintFixed)
- /*1. Create ~/.riv-mesh folder if not existing
- *2. Create ~/.riv-mesh/mesh.conf if not existing
- *3. If the file exists read Peers.
- *3.1 Invoke add peers for each record
- */
- mesh_folder := ".riv-mesh"
- mesh_conf := "mesh.conf"
- user_home := get_user_home_path()
- mesh_settings_folder := filepath.Join(user_home, mesh_folder)
- err := os.MkdirAll(mesh_settings_folder, os.ModePerm)
- if err != nil {
- fmt.Printf("Unable to create folder: %v", err)
- }
- mesh_settings_path := filepath.Join(user_home, mesh_folder, mesh_conf)
- if _, err := os.Stat(mesh_settings_path); os.IsNotExist(err) {
- err := ioutil.WriteFile(mesh_settings_path, []byte(""), 0750)
- if err != nil {
- fmt.Printf("Unable to write file: %v", err)
- }
- } else {
- //read peers from mesh.conf
- conf, _ := ioutil.ReadFile(mesh_settings_path)
- var dat map[string]interface{}
- if err := hjson.Unmarshal(conf, &dat); err != nil {
- fmt.Printf("Unable to parse mesh.conf file: %v", err)
- } else {
- if dat["Peers"] != nil {
- peers := dat["Peers"].([]interface{})
- remove_peers()
- for _, u := range peers {
- log.Printf("Unmarshaled: %v", u.(string))
- add_peers(u.(string))
- }
- } else {
- fmt.Printf("Warning: Peers array not loaded from mesh.conf file")
- }
- }
- }
if confui.IndexHtml == "" {
- confui.IndexHtml = "index.html"
+ confui.IndexHtml = getEndpoint()
}
- //Check is it URL already
- indexUrl, err := url.ParseRequestURI(confui.IndexHtml)
- if err != nil || len(indexUrl.Scheme) < 2 { // handling no scheme at all and windows c:\ as scheme detection
- confui.IndexHtml, err = filepath.Abs(confui.IndexHtml)
- if err != nil {
- panic(errors.New("Index file not found: " + err.Error()))
- }
- if stat, err := os.Stat(confui.IndexHtml); err != nil {
- panic(errors.New(fmt.Sprintf("Index file %v not found or permissians denied: %v", confui.IndexHtml, err.Error())))
- } else if stat.IsDir() {
- panic(errors.New(fmt.Sprintf("Index file %v not found", confui.IndexHtml)))
- }
- path_prefix := ""
- if indexUrl != nil && len(indexUrl.Scheme) == 1 {
- path_prefix = "/"
- }
- indexUrl, err = url.ParseRequestURI("file://" + path_prefix + strings.ReplaceAll(confui.IndexHtml, "\\", "/"))
- if err != nil {
- panic(errors.New("Index file URL parse error: " + err.Error()))
- }
+ if confui.IndexHtml == "" {
+ confui.IndexHtml = "http://localhost:19019"
}
- w.Bind("savePeers", func(peer_list string) {
- //log.Println("peers saved ", peer_list)
- var peers []string
- _ = json.Unmarshal([]byte(peer_list), &peers)
- log.Printf("Unmarshaled: %v", peers)
- remove_peers()
- for _, u := range peers {
- log.Printf("Unmarshaled: %v", u)
- add_peers(u)
- }
- //add peers to ~/mesh.conf
- dat := make(map[string]interface{})
- dat["Peers"] = peers
- bs, _ := hjson.Marshal(dat)
- e := ioutil.WriteFile(mesh_settings_path, bs, 0750)
- if e != nil {
- fmt.Printf("Unable to write file: %v", e)
- }
- })
- w.Bind("ping", func(peer_list string) {
- go ping(w, peer_list)
- })
- log.Printf("Opening: %v", indexUrl)
- w.Navigate(indexUrl.String())
+ log.Printf("Opening: %v", confui.IndexHtml)
+ w.Navigate(confui.IndexHtml)
w.Run()
}
-func ping(w webview.WebView, peer_list string) {
- var peers []string
- _ = json.Unmarshal([]byte(peer_list), &peers)
- log.Printf("Unmarshaled: %v", peers)
- for _, u := range peers {
- log.Printf("Unmarshaled: %v", u)
- ping_time := check(u)
- log.Printf("ping: %d", ping_time)
- setPingValue(w, u, strconv.FormatInt(ping_time, 10))
- }
-}
-
-func check(peer string) int64 {
- u, e := url.Parse(peer)
- if e != nil {
- return -1
- }
- t := time.Now()
- _, err := net.DialTimeout("tcp", u.Host, 5*time.Second)
- if err != nil {
- return -1
- }
- d := time.Since(t)
- return d.Milliseconds()
-}
-
-func add_peers(uri string) {
- _, err := run_command_with_arg("addpeers", "uri="+uri)
- if err != nil {
- log.Println("Error in the add_peers() call:", err)
- }
-}
-
-func remove_peers() {
- run_command("removepeers")
-}
-
-func setPingValue(p webview.WebView, peer string, value string) {
- p.Dispatch(func() {
- p.Eval("setPingValue('" + peer + "','" + value + "');")
- })
-}
-
-func run_command(command string) []byte {
- data, err := run_command_with_arg(command, "")
- if err != nil {
- log.Println("Error in the "+command+" call:", err)
- }
- return data
-}
-
-func run_command_with_arg(command string, arg string) ([]byte, error) {
- logbuffer := &bytes.Buffer{}
- logger := log.New(logbuffer, "", log.Flags())
-
- wrapErr := func(err error) error {
- logger.Println("Error:", err)
- return errors.New(fmt.Sprintln(logbuffer))
- }
-
- endpoint := getEndpoint(logger)
-
- var conn net.Conn
- u, err := url.Parse(endpoint)
- d := net.Dialer{Timeout: 5000 * time.Millisecond}
- if err == nil {
- switch strings.ToLower(u.Scheme) {
- case "unix":
- logger.Println("Connecting to UNIX socket", endpoint[7:])
- conn, err = d.Dial("unix", endpoint[7:])
- case "tcp":
- logger.Println("Connecting to TCP socket", u.Host)
- conn, err = d.Dial("tcp", u.Host)
- default:
- logger.Println("Unknown protocol or malformed address - check your endpoint")
- err = errors.New("protocol not supported")
- }
- } else {
- logger.Println("Connecting to TCP socket", u.Host)
- conn, err = d.Dial("tcp", endpoint)
- }
- if err != nil {
- return nil, wrapErr(err)
- }
-
- logger.Println("Connected")
- defer conn.Close()
-
- decoder := json.NewDecoder(conn)
- encoder := json.NewEncoder(conn)
- send := &admin.AdminSocketRequest{}
- recv := &admin.AdminSocketResponse{}
- send.Name = command
- args := map[string]string{}
- switch {
- case len(arg) > 0:
- tokens := strings.SplitN(arg, "=", 2)
- args[tokens[0]] = tokens[1]
- default:
- }
-
- if send.Arguments, err = json.Marshal(args); err != nil {
- return nil, err
- }
- if err := encoder.Encode(&send); err != nil {
- return nil, err
- }
- logger.Printf("Request sent")
- //js, _ := json.Marshal(send)
- //fmt.Println("sent:", string(js))
- if err := decoder.Decode(&recv); err != nil {
- return nil, wrapErr(err)
- }
- if recv.Status == "error" {
- if err := recv.Error; err != "" {
- return nil, wrapErr(errors.New("Admin socket returned an error:" + err))
- } else {
- return nil, wrapErr(errors.New("Admin socket returned an error but didn't specify any error text"))
- }
- }
- if json, err := json.MarshalIndent(recv.Response, "", " "); err == nil {
- return json, nil
- }
- return nil, wrapErr(err)
-}
-
-func getEndpoint(logger *log.Logger) string {
+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}) {
@@ -280,25 +65,16 @@ func getEndpoint(logger *log.Logger) string {
decoder := utf.NewDecoder()
config, err = decoder.Bytes(config)
if err != nil {
- return defaults.GetDefaults().DefaultAdminListen
+ return ""
}
}
var dat map[string]interface{}
if err := hjson.Unmarshal(config, &dat); err != nil {
- return defaults.GetDefaults().DefaultAdminListen
+ return ""
}
- if ep, ok := dat["AdminListen"].(string); ok && (ep != "none" && ep != "") {
- logger.Println("Found platform default config file", defaults.GetDefaults().DefaultConfigFile)
- logger.Println("Using endpoint", ep, "from AdminListen")
+ if ep, ok := dat["HttpAddress"].(string); ok && (ep != "none" && ep != "") {
return ep
- } else {
- logger.Println("Configuration file doesn't contain appropriate AdminListen option")
- logger.Println("Falling back to platform default", defaults.GetDefaults().DefaultAdminListen)
- return defaults.GetDefaults().DefaultAdminListen
}
- } else {
- logger.Println("Can't open config file from default location", defaults.GetDefaults().DefaultConfigFile)
- logger.Println("Falling back to platform default", defaults.GetDefaults().DefaultAdminListen)
- return defaults.GetDefaults().DefaultAdminListen
}
+ return ""
}
diff --git a/contrib/ui/mesh-ui/webview_other.go b/contrib/ui/mesh-ui/webview_other.go
index e4365ad5..313ad87f 100644
--- a/contrib/ui/mesh-ui/webview_other.go
+++ b/contrib/ui/mesh-ui/webview_other.go
@@ -3,16 +3,5 @@
package main
-import "os"
-
-func get_user_home_path() string {
- path, exists := os.LookupEnv("HOME")
- if exists {
- return path
- } else {
- return ""
- }
-}
-
func Console(show bool) {
}
diff --git a/contrib/ui/mesh-ui/webview_windows.go b/contrib/ui/mesh-ui/webview_windows.go
index 872f3b63..556afedd 100755
--- a/contrib/ui/mesh-ui/webview_windows.go
+++ b/contrib/ui/mesh-ui/webview_windows.go
@@ -4,19 +4,9 @@
package main
import (
- "os"
"syscall"
)
-func get_user_home_path() string {
- path, exists := os.LookupEnv("USERPROFILE")
- if exists {
- return path
- } else {
- return ""
- }
-}
-
func Console(show bool) {
var getWin = syscall.NewLazyDLL("kernel32.dll").NewProc("GetConsoleWindow")
var showWin = syscall.NewLazyDLL("user32.dll").NewProc("ShowWindow")
diff --git a/contrib/ui/nas-asustor/www/assets/properties.js b/contrib/ui/nas-asustor/www/assets/properties.js
index d7af016c..95919118 100644
--- a/contrib/ui/nas-asustor/www/assets/properties.js
+++ b/contrib/ui/nas-asustor/www/assets/properties.js
@@ -13,7 +13,7 @@ var ed = {
var d = new Date();
d.setTime(d.getTime() + (10 * 60 * 1000));
document.cookie = "access_key=" + btoa( "user=" + encodeURIComponent($('#nasInputUser').val()) + ";pwd=" + encodeURIComponent($('#nasInputPassword').val()))+ "; expires=" + d.toUTCString() + "; path=/";
- $.ajax({url: "api/getself"}).done(function () {
+ $.ajax({url: "api/self"}).done(function () {
window.location.reload();
}).fail(function () {
ed.nasLogoutCall();
diff --git a/src/admin/admin.go b/src/admin/admin.go
index a7241b72..bcc20978 100644
--- a/src/admin/admin.go
+++ b/src/admin/admin.go
@@ -9,6 +9,7 @@ import (
"net/url"
"os"
"sort"
+ "strconv"
"archive/zip"
"strings"
@@ -22,13 +23,20 @@ import (
// TODO: Add authentication
+type ServerEvent struct {
+ event string
+ data string
+}
+
type AdminSocket struct {
- core *core.Core
- log core.Logger
- listener net.Listener
- handlers map[string]handler
- done chan struct{}
- config struct {
+ core *core.Core
+ log core.Logger
+ listener net.Listener
+ handlers map[string]handler
+ done chan struct{}
+ serverEvents chan ServerEvent
+ serverEventNextId int
+ config struct {
listenaddr ListenAddress
}
}
@@ -103,6 +111,7 @@ func New(c *core.Core, log core.Logger, opts ...SetupOption) (*AdminSocket, erro
return res, nil
})
a.done = make(chan struct{})
+ a.serverEvents = make(chan ServerEvent)
go a.listen()
return a, a.core.SetAdmin(a)
}
@@ -247,33 +256,139 @@ func (a *AdminSocket) StartHttpServer(nc *config.NodeConfig) {
http.HandleFunc("/api", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Following methods are allowed: getself, getpeers. litening"+u.Host)
})
- http.HandleFunc("/api/getself", func(w http.ResponseWriter, r *http.Request) {
- w.Header().Add("Content-Type", "application/json")
- req := &GetSelfRequest{}
- res := &GetSelfResponse{}
- if err := a.getSelfHandler(req, res); err != nil {
- http.Error(w, err.Error(), 503)
+ http.HandleFunc("/api/self", func(w http.ResponseWriter, r *http.Request) {
+ switch r.Method {
+ case "GET":
+ w.Header().Add("Content-Type", "application/json")
+ req := &GetSelfRequest{}
+ res := &GetSelfResponse{}
+ if err := a.getSelfHandler(req, res); err != nil {
+ http.Error(w, err.Error(), 503)
+ }
+ b, err := json.Marshal(res)
+ if err != nil {
+ http.Error(w, err.Error(), 503)
+ }
+ fmt.Fprint(w, string(b[:]))
+ default:
+ http.Error(w, "Method Not Allowed", 405)
}
- b, err := json.Marshal(res)
- if err != nil {
- http.Error(w, err.Error(), 503)
- }
- fmt.Fprint(w, string(b[:]))
})
- http.HandleFunc("/api/getpeers", func(w http.ResponseWriter, r *http.Request) {
- w.Header().Add("Content-Type", "application/json")
- req := &GetPeersRequest{}
- res := &GetPeersResponse{}
+ http.HandleFunc("/api/peers", func(w http.ResponseWriter, r *http.Request) {
+ switch r.Method {
+ case "GET":
+ w.Header().Add("Content-Type", "application/json")
+ req := &GetPeersRequest{}
+ res := &GetPeersResponse{}
- if err := a.getPeersHandler(req, res); err != nil {
- http.Error(w, err.Error(), 503)
+ if err := a.getPeersHandler(req, res); err != nil {
+ http.Error(w, err.Error(), 503)
+ }
+ b, err := json.Marshal(res)
+ if err != nil {
+ http.Error(w, err.Error(), 503)
+ }
+ fmt.Fprint(w, string(b[:]))
+ case "POST":
+ 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[:]))
+ case "PUT":
+ err := a.core.RemovePeers()
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ }
+
+ 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":
+ err := a.core.RemovePeers()
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ }
+ http.Error(w, "No content", http.StatusNoContent)
+ default:
+ http.Error(w, "Method Not Allowed", 405)
}
- b, err := json.Marshal(res)
- if err != nil {
- http.Error(w, err.Error(), 503)
- }
- fmt.Fprint(w, string(b[:]))
})
+ http.HandleFunc("/api/ping", func(w http.ResponseWriter, r *http.Request) {
+ switch r.Method {
+ case "POST":
+ peer_list := []string{}
+
+ err := json.NewDecoder(r.Body).Decode(&peer_list)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusBadRequest)
+ return
+ }
+
+ go a.ping(peer_list)
+ http.Error(w, "Accepted", http.StatusAccepted)
+ default:
+ http.Error(w, "Method Not Allowed", 405)
+ }
+ })
+
+ http.HandleFunc("/api/sse", func(w http.ResponseWriter, r *http.Request) {
+ switch r.Method {
+ case "GET":
+ w.Header().Add("Content-Type", "text/event-stream")
+ Loop:
+ for {
+ select {
+ case v := <-a.serverEvents:
+ fmt.Fprintln(w, "id:", a.serverEventNextId)
+ fmt.Fprintln(w, "event:", v.event)
+ fmt.Fprintln(w, "data:", v.data)
+ fmt.Fprintln(w) //end of event
+ a.serverEventNextId += 1
+ default:
+ break Loop
+ }
+ }
+ default:
+ http.Error(w, "Method Not Allowed", 405)
+ }
+ })
+
var docFs = ""
pakReader, err := zip.OpenReader(nc.WwwRoot)
if err == nil {
@@ -300,6 +415,29 @@ func (a *AdminSocket) StartHttpServer(nc *config.NodeConfig) {
}
}
+func (a *AdminSocket) ping(peers []string) {
+ for _, u := range peers {
+ go func(u string) {
+ data, _ := json.Marshal(map[string]string{"peer": u, "value": strconv.FormatInt(check(u), 10)})
+ a.serverEvents <- ServerEvent{event: "ping", data: string(data)}
+ }(u)
+ }
+}
+
+func check(peer string) int64 {
+ u, e := url.Parse(peer)
+ if e != nil {
+ return -1
+ }
+ t := time.Now()
+ _, err := net.DialTimeout("tcp", u.Host, 5*time.Second)
+ if err != nil {
+ return -1
+ }
+ d := time.Since(t)
+ return d.Milliseconds()
+}
+
// IsStarted returns true if the module has been started.
func (a *AdminSocket) IsStarted() bool {
select {