Browse Source

Embed static and simple UI

Ettore Di Giacinto 3 years ago
parent
commit
9e4fe87222
10 changed files with 313 additions and 17 deletions
  1. 1 0
      README.md
  2. 44 0
      api/api.go
  3. 124 0
      api/public/blockchain.html
  4. 5 0
      api/public/css/style.css
  5. 131 0
      api/public/index.html
  6. 1 0
      api/public/js/dt.js
  7. 2 14
      cmd/api.go
  8. 1 1
      go.mod
  9. 1 0
      pkg/blockchain/data.go
  10. 3 2
      pkg/edgevpn/edgevpn.go

+ 1 - 0
README.md

@@ -39,6 +39,7 @@ IFACE=edgevpn0 ADDRESS=10.1.0.13/24 ./edgevpn
 
 
 ## Architecture
 ## Architecture
 
 
+- Simple (KISS) interface to display network data from the blockchain
 - p2p encryption between peers with libp2p
 - p2p encryption between peers with libp2p
 - randezvous points dynamically generated from OTP keys
 - randezvous points dynamically generated from OTP keys
 - extra AES symmetric encryption on top. In case randezvous point is compromised
 - extra AES symmetric encryption on top. In case randezvous point is compromised

+ 44 - 0
api/api.go

@@ -0,0 +1,44 @@
+package api
+
+import (
+	"embed"
+	"io/fs"
+	"net/http"
+
+	"github.com/labstack/echo/v4"
+	"github.com/mudler/edgevpn/pkg/blockchain"
+)
+
+//go:embed public
+var embededFiles embed.FS
+
+func getFileSystem() http.FileSystem {
+	fsys, err := fs.Sub(embededFiles, "public")
+	if err != nil {
+		panic(err)
+	}
+
+	return http.FS(fsys)
+}
+
+func API(l string, ledger *blockchain.Ledger) error {
+	ec := echo.New()
+	assetHandler := http.FileServer(getFileSystem())
+
+	ec.GET("/api/data", func(c echo.Context) error {
+		list := []blockchain.Data{}
+		for _, v := range ledger.CurrentData() {
+			list = append(list, v)
+		}
+		return c.JSON(http.StatusOK, list)
+	})
+
+	ec.GET("/*", echo.WrapHandler(http.StripPrefix("/", assetHandler)))
+
+	ec.GET("/api/blockchain", func(c echo.Context) error {
+		//		c.SetHandler()
+		return c.JSON(http.StatusOK, ledger.BlockChain())
+	})
+
+	return ec.Start(l)
+}

+ 124 - 0
api/public/blockchain.html

@@ -0,0 +1,124 @@
+<html>
+<head>
+  <link rel="preconnect" href="https://fonts.googleapis.com">
+  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
+  <link href="https://fonts.googleapis.com/css2?family=Duru+Sans&family=Hammersmith+One&family=Lato&family=Nunito&family=Nunito+Sans:wght@400;600&family=Zen+Kurenaido&display=swap" rel="stylesheet"> 
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.9.1/css/bulma.css" />
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.1/css/all.min.css" integrity="sha512-+4zCK9k+qNFUR5X+cKL9EIR+ZOhtIloNl9GIKS57V1MyNsYpYcUrUeQc9vNfzsWfV28IaLL3i96P9sdNyeRssA==" crossorigin="anonymous" />
+
+<!-- datatables -->
+<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script> 
+
+<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.10.22/css/jquery.dataTables.css">
+
+<script type="text/javascript" charset="utf8" src="https://cdn.datatables.net/1.10.22/js/jquery.dataTables.js"></script>
+<script src="/js/dt.js"></script> 
+<link rel="stylesheet" type="text/css" href="/css/style.css">
+
+<title>EdgeVPN - Blockchain index</title>
+</head>
+
+
+<body>
+  <nav class="navbar is-dark is-spaced has-shadow" role="navigation" aria-label="main navigation">
+    <div class="navbar-brand">
+      <a class="navbar-item" href="/">
+      <!--  <img src="/images/logo.png" width="112" height="28"> -->
+      <i class="fas fa-ship"></i>
+      </a>
+  
+      <a role="button" class="navbar-burger" aria-label="menu" aria-expanded="false" data-target="navbarBasicExample">
+        <span aria-hidden="true"></span>
+        <span aria-hidden="true"></span>
+        <span aria-hidden="true"></span>
+      </a>
+    </div>
+    <div class="navbar-menu">
+      <div class="navbar-start">
+        <a class="navbar-item" href="/index.html">
+          <i class="fas fa-desktop"></i>&nbsp;
+          Machines
+        </a>
+        <a class="navbar-item" href="/blockchain.html">
+          <i class="fas fa-dice-d20"></i>&nbsp;
+          Blockchain
+        </a>
+      </div>
+      <div class="navbar-end">
+        <div class="navbar-item">
+          <div class="buttons">
+            <a class="button is-link" href="https://github.com/mudler/edgevpn" target=_blank>
+             <strong><i class="fab fa-github-alt"></i> GitHub</strong>
+            </a>
+            <a class="button is-light" href="https://github.com/mudler/edgevpn/issues/new" target=_blank>
+             <i class="fas fa-bug"></i> Report issue
+            </a>
+          </div>
+        </div>
+      </div>
+    </div>
+  </nav>
+
+    <section class="hero">
+      <div class="hero-body">
+        <div class="container">
+          <h1 class="title">
+            <i class="fas fa-dice-d20"></i> Blockchain index
+          </h1>
+          <h2 class="subtitle">
+          </h2>
+        </div>
+      </div>
+    </section>
+    <section class="section">
+        <div class="container">
+
+        </div>
+    </section>
+
+
+    <div class="container">
+        <table  data-toggle="table"
+          data-search="true"
+          data-show-columns="true"
+          id="table"  >
+          <thead>
+        
+          <tr>
+              <th ><abbr title="Index">Index</abbr></th>
+              <th  >Timestamp</th>
+              <th ><abbr title="Hash">Hash</abbr></th>
+              <th><abbr title="PrevHash">PrevHash</abbr></th>
+
+            </tr>
+          </thead>    
+        </table>
+      </div>
+        <script type="text/javascript">
+            $(document).ready(function() {
+                var table = $('#table').DataTable( {
+                    "processing": true,
+                    "ajax": {
+                        "url": "/api/blockchain",
+                        "type": "GET",
+                        "dataSrc": '',
+                    },
+                    'language':{ 
+                      "loadingRecords": "&nbsp;",
+                      "processing": "Loading..."
+                    },
+                    "columns": [
+                        { "data": "Index" },
+                        { "data": "Timestamp" },
+                        { "data": "Hash" },
+                        { "data": "PrevHash" },
+                    ],
+                } );
+
+                setInterval( function () {
+                    table.ajax.reload();
+                }, 30000 );
+            } );
+        </script>  
+    </body>
+</html>

+ 5 - 0
api/public/css/style.css

@@ -0,0 +1,5 @@
+body {
+    font-family: 'Hammersmith One', sans-serif;
+}
+   
+table.dataTable.nowrap th,table.dataTable.nowrap td{white-space:nowrap}div.dataTables_wrapper div.dataTables_length select{width:75px;display:inline-block}div.dataTables_wrapper div.dataTables_filter{text-align:right}div.dataTables_wrapper div.dataTables_filter label{font-weight:400;white-space:nowrap;text-align:left}div.dataTables_wrapper div.dataTables_filter input{margin-left:.5em;display:inline-block;width:auto}div.dataTables_wrapper div.dataTables_paginate{white-space:nowrap;float:right}@media screen and (max-width: 768px){div.dataTables_paginate{white-space:nowrap;float:none!important;display:flex;justify-content:space-around}}table.dataTable thead>tr>th.sorting_asc,table.dataTable thead>tr>th.sorting_desc,table.dataTable thead>tr>th.sorting,table.dataTable thead>tr>td.sorting_asc,table.dataTable thead>tr>td.sorting_desc,table.dataTable thead>tr>td.sorting{padding-right:30px}table.dataTable thead .sorting,table.dataTable thead .sorting_asc,table.dataTable thead .sorting_desc,table.dataTable thead .sorting_asc_disabled,table.dataTable thead .sorting_desc_disabled{cursor:pointer;position:relative}table.dataTable thead .sorting:after,table.dataTable thead .sorting_asc:after,table.dataTable thead .sorting_desc:after,table.dataTable thead .sorting_asc_disabled:after,table.dataTable thead .sorting_desc_disabled:after{position:absolute;bottom:4px;right:4px;display:block;font-family:"Font Awesome\ 5 Free";opacity:.5}table.dataTable thead .sorting:after{opacity:.2;content:"\f0dc"}table.dataTable thead .sorting_asc:after{content:"\f0de"}table.dataTable thead .sorting_desc:after{content:"\f0dd"}table.dataTable thead .sorting_asc_disabled:after,table.dataTable thead .sorting_desc_disabled:after{color:#eee}@media screen and (max-width: 768px){div.dataTables_wrapper div.dataTables_length,div.dataTables_wrapper div.dataTables_filter,div.dataTables_wrapper div.dataTables_info,div.dataTables_wrapper div.dataTables_paginate{text-align:center}}

+ 131 - 0
api/public/index.html

@@ -0,0 +1,131 @@
+<html>
+<head>
+  <link rel="preconnect" href="https://fonts.googleapis.com">
+  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
+  <link href="https://fonts.googleapis.com/css2?family=Duru+Sans&family=Hammersmith+One&family=Lato&family=Nunito&family=Nunito+Sans:wght@400;600&family=Zen+Kurenaido&display=swap" rel="stylesheet"> 
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.9.1/css/bulma.css" />
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.1/css/all.min.css" integrity="sha512-+4zCK9k+qNFUR5X+cKL9EIR+ZOhtIloNl9GIKS57V1MyNsYpYcUrUeQc9vNfzsWfV28IaLL3i96P9sdNyeRssA==" crossorigin="anonymous" />
+
+<!-- datatables -->
+<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script> 
+
+<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.10.22/css/jquery.dataTables.css">
+
+<script type="text/javascript" charset="utf8" src="https://cdn.datatables.net/1.10.22/js/jquery.dataTables.js"></script>
+
+<script src="/js/dt.js"></script> 
+
+<link rel="stylesheet" type="text/css" href="/css/style.css">
+
+<title>EdgeVPN - Machines index</title>
+</head>
+
+<body>
+  <nav class="navbar is-dark is-spaced has-shadow" role="navigation" aria-label="main navigation">
+    <div class="navbar-brand">
+      <a class="navbar-item" href="/">
+      <!--  <img src="/images/logo.png" width="112" height="28"> -->
+      <i class="fas fa-ship"></i>
+      </a>
+  
+      <a role="button" class="navbar-burger" aria-label="menu" aria-expanded="false" data-target="navbarBasicExample">
+        <span aria-hidden="true"></span>
+        <span aria-hidden="true"></span>
+        <span aria-hidden="true"></span>
+      </a>
+    </div>
+    <div class="navbar-menu">
+      <div class="navbar-start">
+        <a class="navbar-item" href="/index.html">
+          <i class="fas fa-desktop"></i>&nbsp;
+          Machines
+        </a>
+        <a class="navbar-item" href="/blockchain.html">
+          <i class="fas fa-dice-d20"></i>&nbsp;
+          Blockchain
+        </a>
+      </div>
+      <div class="navbar-end">
+        <div class="navbar-item">
+          <div class="buttons">
+            <a class="button is-link" href="https://github.com/mudler/edgevpn" target=_blank>
+             <strong><i class="fab fa-github-alt"></i> GitHub</strong>
+            </a>
+            <a class="button is-light" href="https://github.com/mudler/edgevpn/issues/new" target=_blank>
+             <i class="fas fa-bug"></i> Report issue
+            </a>
+          </div>
+        </div>
+      </div>
+    </div>
+  </nav>
+
+    <section class="hero">
+      <div class="hero-body">
+        <div class="container">
+          <h1 class="title">
+            <i class="fas fa-desktop"></i> Connected Machines
+          </h1>
+          <h2 class="subtitle">
+             
+          </h2>
+        </div>
+      </div>
+    </section>
+    <section class="section">
+        <div class="container">
+
+        </div>
+    </section>
+
+
+    <div class="container">
+        <table  data-toggle="table"
+          data-search="true"
+          data-show-columns="true"
+          id="table"  >
+          <thead>
+        
+          <tr>
+              <th ><abbr title="ip">Address</abbr></th>
+              <th ><abbr title="peer">PeerID</abbr></th>
+              <th  >Hostname</th>
+              <th ><abbr title="OS">OS</abbr></th>
+              <th><abbr title="Arch">Architecture</abbr></th>
+              <th><abbr title="Version">Version</abbr></th>
+
+            </tr>
+          </thead>
+        </table> 
+      </div>
+      
+        <script type="text/javascript">
+            $(document).ready(function() {
+                var table = $('#table').DataTable( {
+                    "processing": true,
+                    "ajax": {
+                        "url": "/api/data",
+                        "type": "GET",
+                        "dataSrc": '',
+                    },
+                    'language':{ 
+                      "loadingRecords": "",
+                      "processing": ""
+                    },
+                    "columns": [
+                        { "data": "Address" },
+                        { "data": "PeerID" },
+                        { "data": "Hostname" },
+                        { "data": "OS" },
+                        { "data": "Arch" },
+                        { "data": "Version" },
+                    ],
+                } );
+
+                setInterval( function () {
+                    table.ajax.reload();
+                }, 5000 ); // 5 s
+            } );
+        </script>  
+    </body>
+</html>

+ 1 - 0
api/public/js/dt.js

@@ -0,0 +1 @@
+!function(e){"function"==typeof define&&define.amd?define(["jquery","datatables.net"],function(a){return e(a,window,document)}):"object"==typeof exports?module.exports=function(a,t){return a||(a=window),t&&t.fn.dataTable||(t=require("datatables.net")(a,t).$),e(t,a,a.document)}:e(jQuery,window,document)}(function(e,a,t){var n=e.fn.dataTable;return e.extend(!0,n.defaults,{dom:"<'columns'<'column is-6'l><'column is-6'f>><'columns'<'column is-12 table-container'tr>><'columns'<'column is-5'i><'column is-7'p>>",renderer:"bulma"}),e.extend(n.ext.classes,{sWrapper:"dataTables_wrapper dt-bulma",sFilterInput:"input is-small",sLengthSelect:"input is-small",sProcessing:"dataTables_processing panel",sPageButton:"pagination-link",sPagePrevious:"pagination-previous",sPageNext:"pagination-next",sPageButtonActive:"is-current"}),n.ext.renderer.pageButton.bulma=function(a,i,s,r,l,o){var u,d,c,p=new n.Api(a),f=a.oClasses,g=a.oLanguage.oPaginate,b=a.oLanguage.oAria.paginate||{},m=0,x=function(t,n){var i,r,c,v,w=function(a){a.preventDefault(),!e(a.currentTarget).is("[disabled]")&&!e(a.currentTarget).is("#table_ellipsis")&&p.page()!=a.data.action&&p.page(a.data.action).draw("page")};for(i=0,r=n.length;i<r;i++)if(v=n[i],e.isArray(v))x(t,v);else{d=u="";var T=!1;switch(v){case"ellipsis":u="&#x2026;",T=!0;break;case"first":u=g.sFirst,T=v+!(0<l);break;case"previous":u=g.sPrevious,T=!(0<l);break;case"next":u=g.sNext,T=!(l<o-1);break;case"last":u=g.sLast,T=v+!(l<o-1);break;default:u=v+1,d=l===v?" is-current":"",T=!1}u&&(c=e("<li>",{id:0===s&&"string"==typeof v?a.sTableId+"_"+v:null}).append(e("<a>",{class:f.sPageButton+" "+d,href:"#","aria-controls":a.sTableId,"aria-label":b[v],"data-dt-idx":m,tabindex:a.iTabIndex,disabled:T}).html(u)).appendTo(t),a.oApi._fnBindAction(c,{action:v},w),m++)}};try{c=e(i).find(t.activeElement).data("dt-idx")}catch(e){}x(e(i).empty().html('<ul class="pagination-list"/>').children("ul"),r),c&&e(i).find("[data-dt-idx="+c+"]").focus()},n});

+ 2 - 14
cmd/api.go

@@ -1,9 +1,7 @@
 package cmd
 package cmd
 
 
 import (
 import (
-	"net/http"
-
-	"github.com/labstack/echo/v4"
+	"github.com/mudler/edgevpn/api"
 	"github.com/mudler/edgevpn/pkg/blockchain"
 	"github.com/mudler/edgevpn/pkg/blockchain"
 	"github.com/mudler/edgevpn/pkg/edgevpn"
 	"github.com/mudler/edgevpn/pkg/edgevpn"
 	"github.com/urfave/cli"
 	"github.com/urfave/cli"
@@ -35,17 +33,7 @@ func API(l *zap.Logger) cli.Command {
 				return err
 				return err
 			}
 			}
 
 
-			ec := echo.New()
-
-			ec.GET("/api/data", func(c echo.Context) error {
-				return c.JSON(http.StatusOK, ledger.CurrentData())
-			})
-
-			ec.GET("/api/blockchain", func(c echo.Context) error {
-				return c.JSON(http.StatusOK, ledger.BlockChain())
-			})
-
-			return ec.Start(c.String("listen"))
+			return api.API(c.String("listen"), ledger)
 		},
 		},
 	}
 	}
 }
 }

+ 1 - 1
go.mod

@@ -1,6 +1,6 @@
 module github.com/mudler/edgevpn
 module github.com/mudler/edgevpn
 
 
-go 1.14
+go 1.16
 
 
 require (
 require (
 	github.com/ipfs/go-ipns v0.1.2 // indirect
 	github.com/ipfs/go-ipns v0.1.2 // indirect

+ 1 - 0
pkg/blockchain/data.go

@@ -5,5 +5,6 @@ type Data struct {
 	Hostname string
 	Hostname string
 	OS       string
 	OS       string
 	Arch     string
 	Arch     string
+	Address  string
 	Version  string
 	Version  string
 }
 }

+ 3 - 2
pkg/edgevpn/edgevpn.go

@@ -65,7 +65,7 @@ func (e *EdgeVPN) Join(ledger *blockchain.Ledger) error {
 	return nil
 	return nil
 }
 }
 
 
-func newBlockChainData(e *EdgeVPN) blockchain.Data {
+func newBlockChainData(e *EdgeVPN, address string) blockchain.Data {
 	hostname, _ := os.Hostname()
 	hostname, _ := os.Hostname()
 
 
 	return blockchain.Data{
 	return blockchain.Data{
@@ -74,6 +74,7 @@ func newBlockChainData(e *EdgeVPN) blockchain.Data {
 		OS:       runtime.GOOS,
 		OS:       runtime.GOOS,
 		Arch:     runtime.GOARCH,
 		Arch:     runtime.GOARCH,
 		Version:  internal.Version,
 		Version:  internal.Version,
+		Address:  address,
 	}
 	}
 }
 }
 
 
@@ -116,7 +117,7 @@ func (e *EdgeVPN) Start() error {
 			// If mismatch, update the blockchain
 			// If mismatch, update the blockchain
 			if !found || existingValue.PeerID != e.host.ID().String() {
 			if !found || existingValue.PeerID != e.host.ID().String() {
 				updatedMap := map[string]blockchain.Data{}
 				updatedMap := map[string]blockchain.Data{}
-				updatedMap[ip.String()] = newBlockChainData(e)
+				updatedMap[ip.String()] = newBlockChainData(e, ip.String())
 				ledger.Add(updatedMap)
 				ledger.Add(updatedMap)
 			}
 			}
 		},
 		},