[TASK] nice list
This commit is contained in:
parent
ac87050fa0
commit
b190bd43c4
|
@ -7,6 +7,6 @@ ssh_key = "/etc/id_rsa"
|
||||||
ssh_interface = "wlp4s0"
|
ssh_interface = "wlp4s0"
|
||||||
|
|
||||||
[yanic]
|
[yanic]
|
||||||
enable = true
|
enable = false
|
||||||
type = "unix"
|
type = "unix"
|
||||||
address = "/tmp/yanic-database.socket"
|
address = "/tmp/yanic-database.socket"
|
||||||
|
|
|
@ -4,10 +4,10 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/FreifunkBremen/freifunkmanager/ssh"
|
"github.com/FreifunkBremen/freifunkmanager/ssh"
|
||||||
"github.com/FreifunkBremen/yanic/data"
|
"github.com/FreifunkBremen/yanic/data"
|
||||||
|
"github.com/FreifunkBremen/yanic/jsontime"
|
||||||
yanicRuntime "github.com/FreifunkBremen/yanic/runtime"
|
yanicRuntime "github.com/FreifunkBremen/yanic/runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -15,10 +15,11 @@ const (
|
||||||
SSHUpdateHostname = "uci set system.@system[0].hostname='%s';uci commit system;echo $(uci get system.@system[0].hostname) > /proc/sys/kernel/hostname"
|
SSHUpdateHostname = "uci set system.@system[0].hostname='%s';uci commit system;echo $(uci get system.@system[0].hostname) > /proc/sys/kernel/hostname"
|
||||||
SSHUpdateOwner = "uci set gluon-node-info.@owner[0].contact='%s';uci commit gluon-node-info;"
|
SSHUpdateOwner = "uci set gluon-node-info.@owner[0].contact='%s';uci commit gluon-node-info;"
|
||||||
SSHUpdateLocation = "uci set gluon-node-info.@location[0].latitude='%d';uci set gluon-node-info.@location[0].longitude='%d';uci set gluon-node-info.@location[0].share_location=1;uci commit gluon-node-info;"
|
SSHUpdateLocation = "uci set gluon-node-info.@location[0].latitude='%d';uci set gluon-node-info.@location[0].longitude='%d';uci set gluon-node-info.@location[0].share_location=1;uci commit gluon-node-info;"
|
||||||
|
SSHUpdateWifiFreq = "uci set gluon-node-info.@location[0].latitude='%d';uci set gluon-node-info.@location[0].longitude='%d';uci set gluon-node-info.@location[0].share_location=1;uci commit gluon-node-info;wifi"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Node struct {
|
type Node struct {
|
||||||
Lastseen time.Time `json:"lastseen"`
|
Lastseen jsontime.Time `json:"lastseen"`
|
||||||
NodeID string `json:"node_id"`
|
NodeID string `json:"node_id"`
|
||||||
Hostname string `json:"hostname"`
|
Hostname string `json:"hostname"`
|
||||||
Location data.Location `json:"location"`
|
Location data.Location `json:"location"`
|
||||||
|
|
|
@ -3,8 +3,9 @@ package runtime
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"os"
|
"os"
|
||||||
"time"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/FreifunkBremen/yanic/jsontime"
|
||||||
yanic "github.com/FreifunkBremen/yanic/runtime"
|
yanic "github.com/FreifunkBremen/yanic/runtime"
|
||||||
|
|
||||||
"github.com/FreifunkBremen/freifunkmanager/lib/log"
|
"github.com/FreifunkBremen/freifunkmanager/lib/log"
|
||||||
|
@ -18,6 +19,7 @@ type Nodes struct {
|
||||||
statePath string
|
statePath string
|
||||||
iface string
|
iface string
|
||||||
notifyFunc []func(*Node, bool)
|
notifyFunc []func(*Node, bool)
|
||||||
|
sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewNodes(path string, iface string, mgmt *ssh.Manager) *Nodes {
|
func NewNodes(path string, iface string, mgmt *ssh.Manager) *Nodes {
|
||||||
|
@ -37,13 +39,15 @@ func (nodes *Nodes) AddNode(n *yanic.Node) {
|
||||||
if node == nil {
|
if node == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
node.Lastseen = jsontime.Now()
|
||||||
logger := log.Log.WithField("method", "AddNode").WithField("node_id", node.NodeID)
|
logger := log.Log.WithField("method", "AddNode").WithField("node_id", node.NodeID)
|
||||||
|
nodes.Lock()
|
||||||
|
nodes.Unlock()
|
||||||
if cNode := nodes.List[node.NodeID]; cNode != nil {
|
if cNode := nodes.List[node.NodeID]; cNode != nil {
|
||||||
cNode.Lastseen = time.Now()
|
cNode.Lastseen = jsontime.Now()
|
||||||
cNode.Stats = node.Stats
|
cNode.Stats = node.Stats
|
||||||
if uNode, ok := nodes.ToUpdate[node.NodeID]; ok {
|
if uNode, ok := nodes.ToUpdate[node.NodeID]; ok {
|
||||||
uNode.Lastseen = time.Now()
|
uNode.Lastseen = jsontime.Now()
|
||||||
uNode.Stats = node.Stats
|
uNode.Stats = node.Stats
|
||||||
if nodes.List[node.NodeID].IsEqual(node) {
|
if nodes.List[node.NodeID].IsEqual(node) {
|
||||||
delete(nodes.ToUpdate, node.NodeID)
|
delete(nodes.ToUpdate, node.NodeID)
|
||||||
|
@ -59,7 +63,6 @@ func (nodes *Nodes) AddNode(n *yanic.Node) {
|
||||||
logger.Debugf("know already these node")
|
logger.Debugf("know already these node")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
node.Lastseen = time.Now()
|
|
||||||
// session := nodes.ssh.ConnectTo(node.Address)
|
// session := nodes.ssh.ConnectTo(node.Address)
|
||||||
result, err := nodes.ssh.RunOn(node.GetAddress(nodes.iface), "uptime")
|
result, err := nodes.ssh.RunOn(node.GetAddress(nodes.iface), "uptime")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -87,6 +90,8 @@ func (nodes *Nodes) UpdateNode(node *Node) {
|
||||||
log.Log.Warn("no new node to update")
|
log.Log.Warn("no new node to update")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
nodes.Lock()
|
||||||
|
defer nodes.Unlock()
|
||||||
if n, ok := nodes.List[node.NodeID]; ok {
|
if n, ok := nodes.List[node.NodeID]; ok {
|
||||||
go node.SSHUpdate(nodes.ssh, nodes.iface, n)
|
go node.SSHUpdate(nodes.ssh, nodes.iface, n)
|
||||||
}
|
}
|
||||||
|
@ -95,6 +100,8 @@ func (nodes *Nodes) UpdateNode(node *Node) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (nodes *Nodes) Updater() {
|
func (nodes *Nodes) Updater() {
|
||||||
|
nodes.Lock()
|
||||||
|
defer nodes.Unlock()
|
||||||
for nodeid := range nodes.ToUpdate {
|
for nodeid := range nodes.ToUpdate {
|
||||||
if node := nodes.List[nodeid]; node != nil {
|
if node := nodes.List[nodeid]; node != nil {
|
||||||
go node.SSHSet(nodes.ssh, nodes.iface)
|
go node.SSHSet(nodes.ssh, nodes.iface)
|
||||||
|
@ -116,6 +123,8 @@ func (nodes *Nodes) load() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (nodes *Nodes) Saver() {
|
func (nodes *Nodes) Saver() {
|
||||||
|
nodes.Lock()
|
||||||
yanic.SaveJSON(nodes, nodes.statePath)
|
yanic.SaveJSON(nodes, nodes.statePath)
|
||||||
|
nodes.Unlock()
|
||||||
log.Log.Debug("saved state file")
|
log.Log.Debug("saved state file")
|
||||||
}
|
}
|
||||||
|
|
|
@ -104,23 +104,54 @@ thead {
|
||||||
thead tr th{
|
thead tr th{
|
||||||
border-bottom: 4px solid #dc0067;
|
border-bottom: 4px solid #dc0067;
|
||||||
}
|
}
|
||||||
|
table th > input {
|
||||||
table th.sort-down:after {
|
border: none;
|
||||||
content: " \25BE"
|
color: #000;
|
||||||
|
font-weight: bold;
|
||||||
|
background: #fff;
|
||||||
}
|
}
|
||||||
table th.sort-up:after {
|
table th.sortable.sort-down:after {
|
||||||
content: " \25B4"
|
content: " \25BC"
|
||||||
|
}
|
||||||
|
table th.sortable.sort-up:after {
|
||||||
|
content: " \25B2"
|
||||||
|
}
|
||||||
|
table th.sortable:not(.sort-down):not(.sort-up):after {
|
||||||
|
content: " \25B4\25BE";
|
||||||
|
}
|
||||||
|
table.nodes {
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
button {
|
table.nodes td > span{
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
table.nodes tbody tr:nth-child(even) {
|
||||||
|
background: #eee;
|
||||||
|
}
|
||||||
|
table.nodes tbody tr:nth-child(odd) {
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
table.nodes tbody tr:hover {
|
||||||
|
background: #ccc;
|
||||||
|
}
|
||||||
|
table.nodes tbody tr.offline{
|
||||||
|
background: #ffb400;
|
||||||
|
}
|
||||||
|
table.nodes tbody tr.offline:hover{
|
||||||
|
background: #dc0067;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
padding: .5em 1em;
|
padding: .3em .5em;
|
||||||
border-radius: 2em;
|
border-radius: 1em;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
background-color: #dc0067;
|
background-color: #dc0067;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
button:hover {
|
.btn:hover {
|
||||||
background: lighten(#dc0067, 5%);
|
background: lighten(#dc0067, 5%);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
|
||||||
<title>FreifunkManager</title>
|
<title>FreifunkManager</title>
|
||||||
<link rel="stylesheet" href="/css/main.css">
|
<link rel="stylesheet" href="/css/main.css">
|
||||||
|
<script src="/js/moment.js"></script>
|
||||||
<script src="/js/navigo.js"></script>
|
<script src="/js/navigo.js"></script>
|
||||||
<script src="/js/config.js"></script>
|
<script src="/js/config.js"></script>
|
||||||
<script src="/js/domlib.js"></script>
|
<script src="/js/domlib.js"></script>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
var config = {
|
var config = {
|
||||||
title: 'FreifunkManager - Breminale',
|
title: 'FreifunkManager - Breminale',
|
||||||
backend: 'ws://localhost:8080/websocket'
|
backend: 'ws://'+location.host+'/websocket'
|
||||||
};
|
};
|
||||||
|
|
|
@ -8,18 +8,15 @@ var router = new Navigo(null, true, '#');
|
||||||
router.on({
|
router.on({
|
||||||
'/list': function () {
|
'/list': function () {
|
||||||
clean();
|
clean();
|
||||||
console.log("list view");
|
|
||||||
guiList.bind(document.querySelector('main'));
|
guiList.bind(document.querySelector('main'));
|
||||||
guiList.render();
|
guiList.render();
|
||||||
},
|
},
|
||||||
'/map':function(){
|
'/map':function(){
|
||||||
clean();
|
clean();
|
||||||
console.log("map view");
|
|
||||||
domlib.newAt(main,"div").innerHTML = "Map";
|
domlib.newAt(main,"div").innerHTML = "Map";
|
||||||
},
|
},
|
||||||
'/statistics':function(){
|
'/statistics':function(){
|
||||||
clean();
|
clean();
|
||||||
console.log("stats view");
|
|
||||||
domlib.newAt(document.querySelector('main'),"div").innerHTML = "Stats";
|
domlib.newAt(document.querySelector('main'),"div").innerHTML = "Stats";
|
||||||
},
|
},
|
||||||
'/n/:nodeID': {
|
'/n/:nodeID': {
|
||||||
|
|
|
@ -6,10 +6,41 @@ var guiList = {};
|
||||||
var sortReverse = false;
|
var sortReverse = false;
|
||||||
var sortIndex;
|
var sortIndex;
|
||||||
|
|
||||||
|
var hostnameFilter,nodeidFilter;
|
||||||
|
|
||||||
function sort(a,b){
|
function sort(a,b){
|
||||||
|
function sortNumber(a,b){
|
||||||
|
return a - b;
|
||||||
|
}
|
||||||
if(sortIndex === undefined)
|
if(sortIndex === undefined)
|
||||||
return a.node_id.localeCompare(b.node_id);
|
return a.node_id.localeCompare(b.node_id);
|
||||||
switch (sortIndex.innerHTML) {
|
switch (sortIndex.innerHTML) {
|
||||||
|
case "Lastseen":
|
||||||
|
return a.lastseen - b.lastseen;
|
||||||
|
case "CurPower":
|
||||||
|
return a._wireless.txpower24 - b._wireless.txpower24;
|
||||||
|
case "Power":
|
||||||
|
return a.wireless.txpower24 - b.wireless.txpower24;
|
||||||
|
case "CurChannel":
|
||||||
|
return a._wireless.channel24 - b._wireless.channel24;
|
||||||
|
case "Channel":
|
||||||
|
return a.wireless.channel24 - b.wireless.channel24;
|
||||||
|
case "Clients":
|
||||||
|
return a.statistics.clients.wifi24 - b.statistics.clients.wifi24;
|
||||||
|
case "ChanUtil":
|
||||||
|
var aMax = a.statistics.wireless.map(function(d){
|
||||||
|
return d.ChanUtil
|
||||||
|
}).sort(sortNumber);
|
||||||
|
|
||||||
|
var bMax = b.statistics.wireless.map(function(d){
|
||||||
|
return d.ChanUtil
|
||||||
|
}).sort(sortNumber);
|
||||||
|
|
||||||
|
if(!sortReverse){
|
||||||
|
aMax = aMax.reverse();
|
||||||
|
bMax = bMax.reverse();
|
||||||
|
}
|
||||||
|
return bMax[0] - aMax[0];
|
||||||
case "Hostname":
|
case "Hostname":
|
||||||
return a.hostname.localeCompare(b.hostname);
|
return a.hostname.localeCompare(b.hostname);
|
||||||
default:
|
default:
|
||||||
|
@ -19,13 +50,56 @@ var guiList = {};
|
||||||
|
|
||||||
function renderRow(data){
|
function renderRow(data){
|
||||||
var tr = document.createElement('tr');
|
var tr = document.createElement('tr');
|
||||||
|
var startdate = new Date();
|
||||||
|
startdate.setMinutes(startdate.getMinutes() - 1);
|
||||||
|
if(new Date(data.lastseen) < startdate)
|
||||||
|
tr.classList.add('offline')
|
||||||
var td;
|
var td;
|
||||||
|
|
||||||
|
domlib.newAt(tr,'td').innerHTML = moment(data.lastseen).fromNow(true);
|
||||||
domlib.newAt(tr,'td').innerHTML = data.node_id;
|
domlib.newAt(tr,'td').innerHTML = data.node_id;
|
||||||
|
|
||||||
var cell1 = domlib.newAt(tr,'td');
|
domlib.newAt(tr,'td').innerHTML = data.hostname;
|
||||||
cell1.innerHTML = data.hostname;
|
|
||||||
cell1.addEventListener('click',function(){
|
var freq = domlib.newAt(tr,'td');
|
||||||
|
domlib.newAt(freq,'span').innerHTML = '2.4 Ghz';
|
||||||
|
domlib.newAt(freq,'span').innerHTML = '5 Ghz';
|
||||||
|
|
||||||
|
var curchannel = domlib.newAt(tr,'td');
|
||||||
|
domlib.newAt(curchannel,'span').innerHTML = data._wireless.channel24||'-';
|
||||||
|
domlib.newAt(curchannel,'span').innerHTML = data._wireless.channel5||'-';
|
||||||
|
|
||||||
|
var channel = domlib.newAt(tr,'td');
|
||||||
|
domlib.newAt(channel,'span').innerHTML = data.wireless.channel24||'-';
|
||||||
|
domlib.newAt(channel,'span').innerHTML = data.wireless.channel5||'-';
|
||||||
|
|
||||||
|
var curpower = domlib.newAt(tr,'td');
|
||||||
|
domlib.newAt(curpower,'span').innerHTML = data._wireless.txpower24||'-';
|
||||||
|
domlib.newAt(curpower,'span').innerHTML = data._wireless.txpower5||'-';
|
||||||
|
|
||||||
|
var power = domlib.newAt(tr,'td');
|
||||||
|
domlib.newAt(power,'span').innerHTML = data.wireless.txpower24||'-';
|
||||||
|
domlib.newAt(power,'span').innerHTML = data.wireless.txpower5||'-';
|
||||||
|
|
||||||
|
var client = domlib.newAt(tr,'td');
|
||||||
|
domlib.newAt(client,'span').innerHTML = data.statistics.clients.wifi24;
|
||||||
|
domlib.newAt(client,'span').innerHTML = data.statistics.clients.wifi5;
|
||||||
|
|
||||||
|
var chanUtil = domlib.newAt(tr,'td');
|
||||||
|
var chanUtil24 = data.statistics.wireless.filter(function(d){
|
||||||
|
return d.frequency < 5000;
|
||||||
|
})[0];
|
||||||
|
var chanUtil5 = data.statistics.wireless.filter(function(d){
|
||||||
|
return d.frequency > 5000;
|
||||||
|
})[0];
|
||||||
|
domlib.newAt(chanUtil,'span').innerHTML = chanUtil24.ChanUtil||'-';
|
||||||
|
domlib.newAt(chanUtil,'span').innerHTML = chanUtil5.ChanUtil||'-';
|
||||||
|
|
||||||
|
var option = domlib.newAt(tr,'td');
|
||||||
|
edit = domlib.newAt(option,'div');
|
||||||
|
edit.classList.add('btn');
|
||||||
|
edit.innerHTML = 'Edit';
|
||||||
|
edit.addEventListener('click',function(){
|
||||||
router.navigate(router.generate('node', { nodeID: data.node_id }));
|
router.navigate(router.generate('node', { nodeID: data.node_id }));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -36,11 +110,20 @@ var guiList = {};
|
||||||
domlib.removeChildren(tbody);
|
domlib.removeChildren(tbody);
|
||||||
var data = store.will();
|
var data = store.will();
|
||||||
|
|
||||||
if(sortReverse)
|
if(hostnameFilter && hostnameFilter.value != "")
|
||||||
data = data.reverse(sort);
|
data = data.filter(function(d){
|
||||||
else
|
return d.hostname.toLowerCase().indexOf(hostnameFilter.value) > -1;
|
||||||
|
})
|
||||||
|
if(nodeidFilter && nodeidFilter.value != "")
|
||||||
|
data = data.filter(function(d){
|
||||||
|
return d.node_id.indexOf(nodeidFilter.value) > -1;
|
||||||
|
})
|
||||||
|
|
||||||
data = data.sort(sort);
|
data = data.sort(sort);
|
||||||
|
|
||||||
|
if(sortReverse)
|
||||||
|
data = data.reverse();
|
||||||
|
|
||||||
for(var i=0; i<data.length; i++){
|
for(var i=0; i<data.length; i++){
|
||||||
var row = renderRow(data[i]);
|
var row = renderRow(data[i]);
|
||||||
tbody.appendChild(row);
|
tbody.appendChild(row);
|
||||||
|
@ -63,7 +146,6 @@ var guiList = {};
|
||||||
|
|
||||||
guiList.render = function render(){
|
guiList.render = function render(){
|
||||||
if (container === undefined){
|
if (container === undefined){
|
||||||
console.log("unable to render guiList");
|
|
||||||
return;
|
return;
|
||||||
} else if (tbody !== undefined){
|
} else if (tbody !== undefined){
|
||||||
container.appendChild(tbody.parentNode);
|
container.appendChild(tbody.parentNode);
|
||||||
|
@ -78,18 +160,73 @@ var guiList = {};
|
||||||
var tr = domlib.newAt(thead,'tr');
|
var tr = domlib.newAt(thead,'tr');
|
||||||
|
|
||||||
var cell1 = domlib.newAt(tr,'th');
|
var cell1 = domlib.newAt(tr,'th');
|
||||||
cell1.innerHTML = "NodeID";
|
cell1.innerHTML = "Lastseen";
|
||||||
cell1.addEventListener('click', function(){
|
cell1.addEventListener('click', function(){
|
||||||
sortTable(cell1);
|
sortTable(cell1);
|
||||||
});
|
});
|
||||||
|
|
||||||
var cell2 = domlib.newAt(tr,'th');
|
var cell2 = domlib.newAt(tr,'th');
|
||||||
cell2.innerHTML = "Hostname";
|
cell2.classList.add('sortable');
|
||||||
cell2.addEventListener('click', function(){
|
nodeidFilter = domlib.newAt(cell2,'input');
|
||||||
|
nodeidFilter.setAttribute("placeholder","NodeID");
|
||||||
|
nodeidFilter.setAttribute("size","9");
|
||||||
|
nodeidFilter.addEventListener('keyup', updateTable);
|
||||||
|
cell2.addEventListener('dblclick', function(){
|
||||||
sortTable(cell2);
|
sortTable(cell2);
|
||||||
});
|
});
|
||||||
|
|
||||||
table.classList.add('sorttable');
|
var cell3 = domlib.newAt(tr,'th');
|
||||||
|
cell3.classList.add('sortable');
|
||||||
|
hostnameFilter = domlib.newAt(cell3,'input');
|
||||||
|
hostnameFilter.setAttribute("placeholder","Hostname");
|
||||||
|
hostnameFilter.addEventListener('keyup', updateTable);
|
||||||
|
cell3.addEventListener('dblclick', function(){
|
||||||
|
sortTable(cell3);
|
||||||
|
});
|
||||||
|
|
||||||
|
domlib.newAt(tr,'th').innerHTML = 'Freq';
|
||||||
|
|
||||||
|
var cell4 = domlib.newAt(tr,'th');
|
||||||
|
cell4.innerHTML = "CurChannel";
|
||||||
|
cell4.classList.add('sortable');
|
||||||
|
cell4.addEventListener('click', function(){
|
||||||
|
sortTable(cell4);
|
||||||
|
});
|
||||||
|
var cell5 = domlib.newAt(tr,'th');
|
||||||
|
cell5.innerHTML = "Channel";
|
||||||
|
cell5.classList.add('sortable');
|
||||||
|
cell5.addEventListener('click', function(){
|
||||||
|
sortTable(cell5);
|
||||||
|
});
|
||||||
|
|
||||||
|
var cell6 = domlib.newAt(tr,'th');
|
||||||
|
cell6.innerHTML = "CurPower";
|
||||||
|
cell6.classList.add('sortable');
|
||||||
|
cell6.addEventListener('click', function(){
|
||||||
|
sortTable(cell6);
|
||||||
|
});
|
||||||
|
var cell7 = domlib.newAt(tr,'th');
|
||||||
|
cell7.innerHTML = "Power";
|
||||||
|
cell7.classList.add('sortable');
|
||||||
|
cell7.addEventListener('click', function(){
|
||||||
|
sortTable(cell7);
|
||||||
|
});
|
||||||
|
|
||||||
|
var cell8 = domlib.newAt(tr,'th');
|
||||||
|
cell8.innerHTML = "Clients";
|
||||||
|
cell8.classList.add('sortable');
|
||||||
|
cell8.addEventListener('click', function(){
|
||||||
|
sortTable(cell8);
|
||||||
|
});
|
||||||
|
var cell9 = domlib.newAt(tr,'th');
|
||||||
|
cell9.innerHTML = "ChanUtil";
|
||||||
|
cell9.classList.add('sortable');
|
||||||
|
cell9.addEventListener('click', function(){
|
||||||
|
sortTable(cell9);
|
||||||
|
});
|
||||||
|
domlib.newAt(tr,'th').innerHTML = "Option";
|
||||||
|
|
||||||
|
table.classList.add('nodes');
|
||||||
|
|
||||||
updateTable();
|
updateTable();
|
||||||
};
|
};
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -12,10 +12,14 @@ var store = {
|
||||||
};
|
};
|
||||||
store.will = function() {
|
store.will = function() {
|
||||||
return Object.keys(store.list).map(function(nodeid){
|
return Object.keys(store.list).map(function(nodeid){
|
||||||
|
var node;
|
||||||
if (store.toupdate[nodeid]) {
|
if (store.toupdate[nodeid]) {
|
||||||
return store.toupdate[nodeid];
|
node = store.toupdate[nodeid];
|
||||||
|
} else{
|
||||||
|
node = store.list[nodeid];
|
||||||
}
|
}
|
||||||
return store.list[nodeid];
|
node._wireless = store.list[nodeid].wireless;
|
||||||
|
return node;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -37,7 +37,9 @@ func (c *Client) Write(msg *Message) {
|
||||||
select {
|
select {
|
||||||
case c.ch <- msg:
|
case c.ch <- msg:
|
||||||
default:
|
default:
|
||||||
|
clientsMutex.Lock()
|
||||||
delete(clients, c.ip)
|
delete(clients, c.ip)
|
||||||
|
clientsMutex.Unlock()
|
||||||
log.HTTP(c.ws.Request()).Error("client disconnected")
|
log.HTTP(c.ws.Request()).Error("client disconnected")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -81,7 +83,9 @@ func (c *Client) listenWrite() {
|
||||||
case <-c.writeQuit:
|
case <-c.writeQuit:
|
||||||
close(c.ch)
|
close(c.ch)
|
||||||
close(c.writeQuit)
|
close(c.writeQuit)
|
||||||
|
clientsMutex.Lock()
|
||||||
delete(clients, c.ip)
|
delete(clients, c.ip)
|
||||||
|
clientsMutex.Unlock()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -94,7 +98,9 @@ func (c *Client) listenRead() {
|
||||||
|
|
||||||
case <-c.readQuit:
|
case <-c.readQuit:
|
||||||
close(c.readQuit)
|
close(c.readQuit)
|
||||||
|
clientsMutex.Lock()
|
||||||
delete(clients, c.ip)
|
delete(clients, c.ip)
|
||||||
|
clientsMutex.Unlock()
|
||||||
return
|
return
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -2,6 +2,7 @@ package websocket
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"golang.org/x/net/websocket"
|
"golang.org/x/net/websocket"
|
||||||
|
|
||||||
|
@ -12,6 +13,7 @@ import (
|
||||||
|
|
||||||
var nodes *runtime.Nodes
|
var nodes *runtime.Nodes
|
||||||
var clients map[string]*Client
|
var clients map[string]*Client
|
||||||
|
var clientsMutex sync.Mutex
|
||||||
|
|
||||||
func Start(nodeBind *runtime.Nodes) {
|
func Start(nodeBind *runtime.Nodes) {
|
||||||
nodes = nodeBind
|
nodes = nodeBind
|
||||||
|
@ -23,14 +25,18 @@ func Start(nodeBind *runtime.Nodes) {
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
ws.Close()
|
ws.Close()
|
||||||
|
clientsMutex.Lock()
|
||||||
delete(clients, ip)
|
delete(clients, ip)
|
||||||
|
clientsMutex.Unlock()
|
||||||
log.HTTP(r).Info("client disconnected")
|
log.HTTP(r).Info("client disconnected")
|
||||||
}()
|
}()
|
||||||
|
|
||||||
log.HTTP(r).Infof("new client")
|
log.HTTP(r).Infof("new client")
|
||||||
|
|
||||||
client := NewClient(ip, ws)
|
client := NewClient(ip, ws)
|
||||||
|
clientsMutex.Lock()
|
||||||
clients[ip] = client
|
clients[ip] = client
|
||||||
|
clientsMutex.Unlock()
|
||||||
client.Listen()
|
client.Listen()
|
||||||
|
|
||||||
}))
|
}))
|
||||||
|
@ -43,9 +49,11 @@ func Notify(node *runtime.Node, real bool) {
|
||||||
if real {
|
if real {
|
||||||
msgType = MessageTypeCurrentNode
|
msgType = MessageTypeCurrentNode
|
||||||
}
|
}
|
||||||
|
clientsMutex.Lock()
|
||||||
for _, c := range clients {
|
for _, c := range clients {
|
||||||
c.Write(&Message{Type: msgType, Node: node})
|
c.Write(&Message{Type: msgType, Node: node})
|
||||||
}
|
}
|
||||||
|
clientsMutex.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func Close() {
|
func Close() {
|
||||||
|
|
Loading…
Reference in New Issue