Skip to content
Snippets Groups Projects
Commit eddad532 authored by Luca Moser's avatar Luca Moser
Browse files

adjusts analysis webinterface to serve files from disk or packr

parent 0100b23c
No related branches found
No related tags found
No related merge requests found
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
"serverAddress": "ressims.iota.cafe:188" "serverAddress": "ressims.iota.cafe:188"
}, },
"server": { "server": {
"port": 0 "port": 9191
}, },
"httpServer": { "httpServer": {
"bindAddress": "0.0.0.0:80" "bindAddress": "0.0.0.0:80"
......
...@@ -7,7 +7,7 @@ require ( ...@@ -7,7 +7,7 @@ require (
github.com/dgrijalva/jwt-go v3.2.0+incompatible github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/dgryski/go-farm v0.0.0-20191112170834-c2139c5d712b // indirect github.com/dgryski/go-farm v0.0.0-20191112170834-c2139c5d712b // indirect
github.com/gdamore/tcell v1.3.0 github.com/gdamore/tcell v1.3.0
github.com/gobuffalo/packr/v2 v2.7.1 // indirect github.com/gobuffalo/packr/v2 v2.7.1
github.com/golang/protobuf v1.3.2 github.com/golang/protobuf v1.3.2
github.com/googollee/go-engine.io v1.4.3-0.20190924125625-798118fc0dd2 github.com/googollee/go-engine.io v1.4.3-0.20190924125625-798118fc0dd2
github.com/googollee/go-socket.io v1.4.3-0.20191204093753-683f8725b6d0 github.com/googollee/go-socket.io v1.4.3-0.20191204093753-683f8725b6d0
......
// +build !skippackr
// Code generated by github.com/gobuffalo/packr/v2. DO NOT EDIT.
// You can use the "packr clean" command to clean up this,
// and any other packr generated files.
package httpserver
import _ "github.com/iotaledger/goshimmer/plugins/analysis/webinterface/httpserver/packrd"
package httpserver
import (
"fmt"
"net/http"
)
func index(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, `<head>
<style>
html {
font-family: monospace, monospace;
line-height: 1.15;
-webkit-text-size-adjust: 100%%;
}
body {
text-align: center;
margin: 0;
overflow: hidden;
}
#nfo{
position:absolute;
right: 0;
padding:10px;
}
#knownPeers{
position:relative;
margin:0;
font-size:14px;
font-weight: bold;
text-align:right;
color:#aaa;
}
#avgNeighbors{
position:relative;
margin:0;
font-size:13px;
text-align:right;
color:grey;
}
#graphc {
position: absolute;
top: 0px;
right: 0px;
margin:0;
right: 0px;
}
#nodeId{
position:relative;
margin:0;
padding:5px 0;
font-size:13px;
font-weight: bold;
text-align:right;
color:#aaa;
}
#nodestat{
position:relative;
margin:0;
font-size:12px;
text-align:right;
color:grey;
}
#in, #out{
margin:0;
padding: 3px 0;
}
</style>
<script src="https://unpkg.com/3d-force-graph"></script>
<!--<script src="../../dist/3d-force-graph.js"></script>-->
</head>
<body>
<div id="graphc"></div>
<div id="nfo">
<p id="knownPeers"></p>
<p id="avgNeighbors"></p>
<div id="nodeId"></div>
<div id="nodestat">
<p id="in"></p>
<p id="out"></p>
</div>
</div>
<script>
var socket = new WebSocket(((window.location.protocol === "https:") ? "wss://" : "ws://") + window.location.host + "/datastream");
socket.onopen = function () {
setInterval(function() {
socket.send("_");
}, 1000);
};
socket.onmessage = function (e) {
document.getElementById("knownPeers").innerHTML = "Known peers: " + data.nodes.length;
document.getElementById("avgNeighbors").innerHTML = "Average neighbors: " + parseFloat(((data.links.length * 2) / data.nodes.length).toFixed(2));
switch (e.data[0]) {
case "_":
// do nothing - its just a ping
break;
case "A":
addNode(e.data.substr(1));
console.log("Add node:",e.data.substr(1));
break;
case "a":
removeNode(e.data.substr(1));
console.log("Remove node:", e.data.substr(1));
break;
case "C":
connectNodes(e.data.substr(1, 64), e.data.substr(65, 64));
console.log("Connect nodes:",e.data.substr(1, 64), " - ", e.data.substr(65, 64));
break;
case "c":
disconnectNodes(e.data.substr(1, 64), e.data.substr(65, 64));
console.log("Disconnect nodes:",e.data.substr(1, 64), " - ", e.data.substr(65, 64));
break;
case "O":
setNodeOnline(e.data.substr(1));
console.log("setNodeOnline:",e.data.substr(1));
break;
case "o":
setNodeOffline(e.data.substr(1));
console.log("setNodeOffline:",e.data.substr(1));
break;
}
};
var nodesById = {};
const data = {
nodes: [],
links: []
};
var existingLinks = {};
let highlightNodes = [];
let highlightInbound = [];
let highlightOutbound = [];
let highlightLinks = [];
let highlightLink = null;
const elem = document.getElementById("graphc");
//.onNodeHover(node => elem.style.cursor = node ? 'pointer' : null)
const Graph = ForceGraph3D()(elem)
.graphData(data)
.enableNodeDrag(false)
.onNodeClick(showNodeStat)
.nodeColor(node => {
if (highlightNodes.indexOf(node) != -1) {
return 'rgb(255,0,0,1)'
}
if (highlightInbound.indexOf(node) != -1) {
return 'rgba(0,255,100,0.6)'
}
if (highlightOutbound.indexOf(node) != -1) {
return 'rgba(0,100,255,0.6)'
}
else {
return 'rgba(0,255,255,0.6)'
}
})
.linkWidth(link => highlightLinks.indexOf(link) === -1 ? 1 : 3)
.linkDirectionalParticles(link => highlightLinks.indexOf(link) === -1 ? 0 : 3)
.linkDirectionalParticleWidth(3)
.onNodeHover(node => {
// no state change
if ((!node && !highlightNodes.length) || (highlightNodes.length === 1 && highlightNodes[0] === node)) return;
highlightNodes = node ? [node] : [];
highlightLinks = [];
highlightInbound = [];
highlightOutbound = [];
clearNodeStat();
if (node != null) {
highlightLinks = data.links.filter(l => (l.target.id == node.id) || (l.source.id == node.id));
highlightLinks.forEach(function(link){
if (link.target.id == node.id) {
highlightInbound.push(link.source)
}
else {
highlightOutbound.push(link.target)
}
});
showNodeStat(node);
}
updateHighlight();
})
.onLinkHover(link => {
// no state change
if ((!link && !highlightLinks.length) || (highlightLinks.length === 1 && highlightLinks[0] === link)) return;
highlightLinks = [link];
highlightNodes = link ? [link.source, link.target] : [];
updateHighlight();
});
var updateRequired = true;
setInterval(function() {
if (updateRequired) {
Graph.graphData(data);
updateRequired = false;
}
}, 500)
function updateHighlight() {
// trigger update of highlighted objects in scene
Graph
.nodeColor(Graph.nodeColor())
.linkWidth(Graph.linkWidth())
.linkDirectionalParticles(Graph.linkDirectionalParticles());
}
updateGraph = function() {
updateRequired = true;
};
function addNode(nodeId, displayImmediately) {
node = {id : nodeId, online: false};
if (!(node.id in nodesById)) {
data.nodes = [...data.nodes, node];
nodesById[node.id] = node;
nodesById[nodeId].online = true;
updateGraph();
}
}
function removeNode(nodeId) {
data.links = data.links.filter(l => l.source.id !== nodeId && l.target.id !== nodeId);
data.nodes = data.nodes.filter(currentNode => currentNode.id != nodeId)
delete nodesById[nodeId];
updateGraph();
}
function setNodeOnline(nodeId) {
if (nodeId in nodesById) {
nodesById[nodeId].online = true;
}
updateGraph();
}
function setNodeOffline(nodeId) {
if (nodeId in nodesById) {
nodesById[nodeId].online = false;
updateGraph();
}
}
function connectNodes(sourceNodeId, targetNodeId) {
if(existingLinks[sourceNodeId + targetNodeId] == undefined) {
if ((sourceNodeId in nodesById) && (targetNodeId in nodesById)) {
//nodesById[sourceNodeId].online = true;
//nodesById[targetNodeId].online = true;
existingLinks[sourceNodeId + targetNodeId] = true
data.links = [...data.links, { source: sourceNodeId, target: targetNodeId }];
}
updateGraph();
}
}
function disconnectNodes(sourceNodeId, targetNodeId) {
data.links = data.links.filter(l => !(l.source.id == sourceNodeId && l.target.id == targetNodeId));
delete existingLinks[sourceNodeId + targetNodeId];
updateGraph();
}
function clearNodeStat() {
document.getElementById("nodeId").innerHTML = ""
document.getElementById("in").innerHTML = ""
document.getElementById("out").innerHTML = ""
}
function showNodeStat(node) {
document.getElementById("nodeId").innerHTML = "ID: " + node.id.substr(0, 16);
var incoming = data.links.filter(l => (l.target.id == node.id));
document.getElementById("in").innerHTML = "INBOUND (accepted): " + incoming.length + "<br>";
incoming.forEach(function(link){
document.getElementById("in").innerHTML += link.source.id.substr(0, 16) + " &rarr; NODE <br>";
});
var outgoing = data.links.filter(l => (l.source.id == node.id));
document.getElementById("out").innerHTML = "OUTBOUND (chosen): " + outgoing.length + "<br>";
outgoing.forEach(function(link){
document.getElementById("out").innerHTML += "NODE &rarr; " + link.target.id.substr(0, 16) + "<br>";
});
}
</script>
</body>`)
}
This diff is collapsed.
...@@ -6,8 +6,10 @@ import ( ...@@ -6,8 +6,10 @@ import (
const ( const (
CFG_BIND_ADDRESS = "analysis.httpServer.bindAddress" CFG_BIND_ADDRESS = "analysis.httpServer.bindAddress"
CFG_DEV = "analysis.httpServer.dev"
) )
func init() { func init() {
flag.String(CFG_BIND_ADDRESS, "0.0.0.0:80", "the bind address for the web API") flag.String(CFG_BIND_ADDRESS, "0.0.0.0:80", "the bind address for the web API")
flag.Bool(CFG_DEV, false, "whether the analysis server visualizer is running dev mode")
} }
...@@ -5,34 +5,41 @@ import ( ...@@ -5,34 +5,41 @@ import (
"net/http" "net/http"
"time" "time"
"github.com/gobuffalo/packr/v2"
"github.com/iotaledger/goshimmer/packages/parameter" "github.com/iotaledger/goshimmer/packages/parameter"
"github.com/iotaledger/goshimmer/packages/shutdown" "github.com/iotaledger/goshimmer/packages/shutdown"
"github.com/iotaledger/hive.go/daemon" "github.com/iotaledger/hive.go/daemon"
"github.com/iotaledger/hive.go/logger" "github.com/iotaledger/hive.go/logger"
"github.com/labstack/echo"
"golang.org/x/net/context" "golang.org/x/net/context"
"golang.org/x/net/websocket" "golang.org/x/net/websocket"
) )
var ( var (
log *logger.Logger log *logger.Logger
httpServer *http.Server engine *echo.Echo
router *http.ServeMux
) )
const name = "Analysis HTTP Server" const name = "Analysis HTTP Server"
var assetsBox = packr.New("Assets", "./static")
func Configure() { func Configure() {
log = logger.NewLogger(name) log = logger.NewLogger(name)
router = http.NewServeMux() engine = echo.New()
engine.HideBanner = true
httpServer = &http.Server{ // we only need this special flag, because we always keep a packed box in the same directory
Addr: parameter.NodeConfig.GetString(CFG_BIND_ADDRESS), if parameter.NodeConfig.GetBool(CFG_DEV) {
Handler: router, engine.Static("/static", "./plugins/analysis/webinterface/httpserver/static")
engine.File("/", "./plugins/analysis/webinterface/httpserver/static/index.html")
} else {
engine.GET("/static/*", echo.WrapHandler(http.StripPrefix("/static", http.FileServer(assetsBox))))
engine.GET("/", index)
} }
router.Handle("/datastream", websocket.Handler(dataStream)) engine.GET("/datastream", echo.WrapHandler(websocket.Handler(dataStream)))
router.HandleFunc("/", index)
} }
func Run() { func Run() {
...@@ -44,9 +51,10 @@ func Run() { ...@@ -44,9 +51,10 @@ func Run() {
func start(shutdownSignal <-chan struct{}) { func start(shutdownSignal <-chan struct{}) {
stopped := make(chan struct{}) stopped := make(chan struct{})
bindAddr := parameter.NodeConfig.GetString(CFG_BIND_ADDRESS)
go func() { go func() {
log.Infof("Started %s: http://%s", name, httpServer.Addr) log.Infof("Started %s: http://%s", name, bindAddr)
if err := httpServer.ListenAndServe(); err != nil { if err := engine.Start(bindAddr); err != nil {
if !errors.Is(err, http.ErrServerClosed) { if !errors.Is(err, http.ErrServerClosed) {
log.Errorf("Error serving: %s", err) log.Errorf("Error serving: %s", err)
} }
...@@ -63,8 +71,16 @@ func start(shutdownSignal <-chan struct{}) { ...@@ -63,8 +71,16 @@ func start(shutdownSignal <-chan struct{}) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second) ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel() defer cancel()
if err := httpServer.Shutdown(ctx); err != nil { if err := engine.Shutdown(ctx); err != nil {
log.Errorf("Error stopping: %s", err) log.Errorf("Error stopping: %s", err)
} }
log.Info("Stopping %s ... done", name) log.Info("Stopping %s ... done", name)
} }
func index(e echo.Context) error {
indexHTML, err := assetsBox.Find("index.html")
if err != nil {
return err
}
return e.HTMLBlob(http.StatusOK, indexHTML)
}
...@@ -3,15 +3,15 @@ ...@@ -3,15 +3,15 @@
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<title>GoShimmer - Network Visualizer</title> <title>GoShimmer - Network Visualizer</title>
<link rel="stylesheet" type="text/css" href="./css/normalize.css"> <link rel="stylesheet" type="text/css" href="/static/css/normalize.css">
<script type="text/javascript" src="./js/vivagraph-0.12.0.min.js"></script> <script type="text/javascript" src="/static/js/vivagraph-0.12.0.min.js"></script>
<script type="text/javascript" src="./js/main.js"></script> <script type="text/javascript" src="/static/js/main.js"></script>
</head> </head>
<body style="background: #202126;"> <body style="background: #202126;">
<div id="logo"> <div id="logo">
<p id="title">GOSHIMMER<br>NETWORK</p> <p id="title">GOSHIMMER<br>NETWORK</p>
<img class="logo" src="./img/gos.png" alt="GoShimmer"> <img class="logo" src="/static/img/gos.png" alt="GoShimmer">
</div> </div>
<div id="graphc"></div> <div id="graphc"></div>
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment