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 @@
"serverAddress": "ressims.iota.cafe:188"
},
"server": {
"port": 0
"port": 9191
},
"httpServer": {
"bindAddress": "0.0.0.0:80"
......
......@@ -7,7 +7,7 @@ require (
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/dgryski/go-farm v0.0.0-20191112170834-c2139c5d712b // indirect
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/googollee/go-engine.io v1.4.3-0.20190924125625-798118fc0dd2
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 (
const (
CFG_BIND_ADDRESS = "analysis.httpServer.bindAddress"
CFG_DEV = "analysis.httpServer.dev"
)
func init() {
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 (
"net/http"
"time"
"github.com/gobuffalo/packr/v2"
"github.com/iotaledger/goshimmer/packages/parameter"
"github.com/iotaledger/goshimmer/packages/shutdown"
"github.com/iotaledger/hive.go/daemon"
"github.com/iotaledger/hive.go/logger"
"github.com/labstack/echo"
"golang.org/x/net/context"
"golang.org/x/net/websocket"
)
var (
log *logger.Logger
httpServer *http.Server
router *http.ServeMux
log *logger.Logger
engine *echo.Echo
)
const name = "Analysis HTTP Server"
var assetsBox = packr.New("Assets", "./static")
func Configure() {
log = logger.NewLogger(name)
router = http.NewServeMux()
engine = echo.New()
engine.HideBanner = true
httpServer = &http.Server{
Addr: parameter.NodeConfig.GetString(CFG_BIND_ADDRESS),
Handler: router,
// we only need this special flag, because we always keep a packed box in the same directory
if parameter.NodeConfig.GetBool(CFG_DEV) {
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))
router.HandleFunc("/", index)
engine.GET("/datastream", echo.WrapHandler(websocket.Handler(dataStream)))
}
func Run() {
......@@ -44,9 +51,10 @@ func Run() {
func start(shutdownSignal <-chan struct{}) {
stopped := make(chan struct{})
bindAddr := parameter.NodeConfig.GetString(CFG_BIND_ADDRESS)
go func() {
log.Infof("Started %s: http://%s", name, httpServer.Addr)
if err := httpServer.ListenAndServe(); err != nil {
log.Infof("Started %s: http://%s", name, bindAddr)
if err := engine.Start(bindAddr); err != nil {
if !errors.Is(err, http.ErrServerClosed) {
log.Errorf("Error serving: %s", err)
}
......@@ -63,8 +71,16 @@ func start(shutdownSignal <-chan struct{}) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
if err := httpServer.Shutdown(ctx); err != nil {
if err := engine.Shutdown(ctx); err != nil {
log.Errorf("Error stopping: %s", err)
}
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 @@
<head>
<meta charset="utf-8" />
<title>GoShimmer - Network Visualizer</title>
<link rel="stylesheet" type="text/css" href="./css/normalize.css">
<script type="text/javascript" src="./js/vivagraph-0.12.0.min.js"></script>
<script type="text/javascript" src="./js/main.js"></script>
<link rel="stylesheet" type="text/css" href="/static/css/normalize.css">
<script type="text/javascript" src="/static/js/vivagraph-0.12.0.min.js"></script>
<script type="text/javascript" src="/static/js/main.js"></script>
</head>
<body style="background: #202126;">
<div id="logo">
<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 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