[TASK] add websocket and webroot
This commit is contained in:
parent
d954d7c851
commit
7de61d62fb
|
@ -9,14 +9,14 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/NYTimes/gziphandler"
|
||||
goji "goji.io"
|
||||
"goji.io/pat"
|
||||
|
||||
configPackage "github.com/FreifunkBremen/freifunkmanager/config"
|
||||
httpLib "github.com/FreifunkBremen/freifunkmanager/lib/http"
|
||||
"github.com/FreifunkBremen/freifunkmanager/lib/log"
|
||||
"github.com/FreifunkBremen/freifunkmanager/lib/worker"
|
||||
"github.com/FreifunkBremen/freifunkmanager/runtime"
|
||||
"github.com/FreifunkBremen/freifunkmanager/ssh"
|
||||
"github.com/FreifunkBremen/freifunkmanager/websocket"
|
||||
"github.com/FreifunkBremen/freifunkmanager/yanic"
|
||||
)
|
||||
|
||||
|
@ -38,10 +38,10 @@ func main() {
|
|||
sshmanager := ssh.NewManager(config.SSHPrivateKey)
|
||||
nodes := runtime.NewNodes(config.StatePath, config.SSHInterface, sshmanager)
|
||||
nodesUpdateWorker := worker.NewWorker(time.Duration(3)*time.Minute, nodes.Updater)
|
||||
nodesSaveWorker := worker.NewWorker(time.Duration(3)*time.Minute, nodes.Saver)
|
||||
nodesSaveWorker := worker.NewWorker(time.Duration(3)*time.Second, nodes.Saver)
|
||||
|
||||
nodesUpdateWorker.Start()
|
||||
nodesSaveWorker.Start()
|
||||
go nodesUpdateWorker.Start()
|
||||
go nodesSaveWorker.Start()
|
||||
|
||||
if config.Yanic.Enable {
|
||||
yanicDialer := yanic.Dial(config.Yanic.Type, config.Yanic.Address)
|
||||
|
@ -49,15 +49,19 @@ func main() {
|
|||
yanicDialer.Start()
|
||||
}
|
||||
|
||||
// Startwebserver
|
||||
router := goji.NewMux()
|
||||
websocket.Start(nodes)
|
||||
|
||||
router.Handle(pat.New("/*"), gziphandler.GzipHandler(http.FileServer(http.Dir(config.Webroot))))
|
||||
// Startwebserver
|
||||
http.HandleFunc("/nodes", func(w http.ResponseWriter, r *http.Request) {
|
||||
httpLib.Write(w, nodes)
|
||||
log.HTTP(r).Info("done")
|
||||
})
|
||||
http.Handle("/", gziphandler.GzipHandler(http.FileServer(http.Dir(config.Webroot))))
|
||||
|
||||
srv := &http.Server{
|
||||
Addr: config.WebserverBind,
|
||||
Handler: router,
|
||||
Addr: config.WebserverBind,
|
||||
}
|
||||
|
||||
go func() {
|
||||
if err := srv.ListenAndServe(); err != nil {
|
||||
panic(err)
|
||||
|
@ -72,6 +76,7 @@ func main() {
|
|||
sig := <-sigs
|
||||
|
||||
// Stop services
|
||||
websocket.Close()
|
||||
srv.Close()
|
||||
if config.Yanic.Enable {
|
||||
yanicDialer.Close()
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
webserver_bind = ":8080"
|
||||
webroot = "webroot"
|
||||
webroot = "./webroot"
|
||||
|
||||
state_path = "/tmp/freifunkmanager.json"
|
||||
|
||||
ssh_key = "/etc/id_rsa"
|
||||
ssh_interface = "wlp4s0"
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
// Package http provides the
|
||||
// logic of the webserver
|
||||
package http
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// Function to read data from a request via json format
|
||||
// Input: pointer to http request r, interface to
|
||||
func Read(r *http.Request, to interface{}) (err error) {
|
||||
if r.Header.Get("Content-Type") != "application/json" {
|
||||
err = errors.New("no json data recived")
|
||||
return
|
||||
}
|
||||
err = json.NewDecoder(r.Body).Decode(to)
|
||||
return
|
||||
}
|
||||
|
||||
// Function to write data as json to a http output
|
||||
// Input: http response writer w, interface data
|
||||
func Write(w http.ResponseWriter, data interface{}) {
|
||||
js, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
http.Error(w, "failed to encode response: "+err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.Write(js)
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
// Package http provides the
|
||||
// logic of the webserver
|
||||
package http
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// Function to test the writing into a http response
|
||||
// Input: pointer to testing object
|
||||
func TestWrite(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
from := map[string]string{"a": "b"}
|
||||
Write(w, from)
|
||||
result := w.Result()
|
||||
|
||||
assert.Equal([]string{"application/json"}, result.Header["Content-Type"], "no header information")
|
||||
buf := new(bytes.Buffer)
|
||||
buf.ReadFrom(result.Body)
|
||||
to := buf.String()
|
||||
assert.Equal("{\"a\":\"b\"}", to, "wrong content")
|
||||
|
||||
w = httptest.NewRecorder()
|
||||
value := make(chan int)
|
||||
Write(w, value)
|
||||
result = w.Result()
|
||||
|
||||
assert.Equal(http.StatusInternalServerError, result.StatusCode, "wrong statuscode")
|
||||
|
||||
}
|
||||
|
||||
// Function to test the reading from a http response
|
||||
// Input: pointer to testing object
|
||||
func TestRead(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
to := make(map[string]string)
|
||||
r, _ := http.NewRequest("GET", "/a", strings.NewReader("{\"a\":\"b\"}"))
|
||||
|
||||
r.Header["Content-Type"] = []string{"application/json"}
|
||||
err := Read(r, &to)
|
||||
assert.NoError(err, "no error")
|
||||
assert.Equal(map[string]string{"a": "b"}, to, "wrong content")
|
||||
|
||||
r.Header["Content-Type"] = []string{""}
|
||||
err = Read(r, &to)
|
||||
assert.Error(err, "no error")
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package http
|
||||
|
||||
import "net/http"
|
||||
|
||||
func GetRemoteIP(r *http.Request) string {
|
||||
ip := r.Header.Get("X-Forwarded-For")
|
||||
if len(ip) <= 1 {
|
||||
ip = r.RemoteAddr
|
||||
}
|
||||
return ip
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package http
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// Function to test the logging
|
||||
// Input: pointer to teh testing object
|
||||
func TestGetIP(t *testing.T) {
|
||||
assertion := assert.New(t)
|
||||
|
||||
req, _ := http.NewRequest("GET", "https://google.com/lola/duda?q=wasd", nil)
|
||||
ip := GetRemoteIP(req)
|
||||
|
||||
assertion.Equal("", ip, "no remote ip address")
|
||||
}
|
|
@ -7,6 +7,8 @@ import (
|
|||
"net/http"
|
||||
|
||||
logger "github.com/Sirupsen/logrus"
|
||||
|
||||
httpLib "github.com/FreifunkBremen/freifunkmanager/lib/http"
|
||||
)
|
||||
|
||||
// current logger with configuration
|
||||
|
@ -21,12 +23,8 @@ func init() {
|
|||
// Function to add the information of a http request to the log
|
||||
// Input: pointer to the http request r
|
||||
func HTTP(r *http.Request) *logger.Entry {
|
||||
ip := r.Header.Get("X-Forwarded-For")
|
||||
if len(ip) <= 1 {
|
||||
ip = r.RemoteAddr
|
||||
}
|
||||
return Log.WithFields(logger.Fields{
|
||||
"remote": ip,
|
||||
"remote": httpLib.GetRemoteIP(r),
|
||||
"method": r.Method,
|
||||
"url": r.URL.RequestURI(),
|
||||
})
|
||||
|
|
|
@ -49,6 +49,9 @@ func NewNode(node *yanicRuntime.Node) *Node {
|
|||
}
|
||||
|
||||
func (n *Node) SSHUpdate(ssh *ssh.Manager, iface string, oldnode *Node) {
|
||||
if oldnode == nil {
|
||||
return
|
||||
}
|
||||
addr := n.GetAddress(iface)
|
||||
if n.Hostname != oldnode.Hostname {
|
||||
ssh.ExecuteOn(addr, fmt.Sprintf(SSHUpdateHostname, n.Hostname))
|
||||
|
|
|
@ -12,17 +12,18 @@ import (
|
|||
)
|
||||
|
||||
type Nodes struct {
|
||||
List map[string]*Node `json:"nodes"`
|
||||
ToUpdate map[string]struct{}
|
||||
ssh *ssh.Manager
|
||||
statePath string
|
||||
iface string
|
||||
List map[string]*Node `json:"nodes"`
|
||||
ToUpdate map[string]*Node
|
||||
ssh *ssh.Manager
|
||||
statePath string
|
||||
iface string
|
||||
notifyFunc []func(*Node, bool)
|
||||
}
|
||||
|
||||
func NewNodes(path string, iface string, mgmt *ssh.Manager) *Nodes {
|
||||
nodes := &Nodes{
|
||||
List: make(map[string]*Node),
|
||||
ToUpdate: make(map[string]struct{}),
|
||||
ToUpdate: make(map[string]*Node),
|
||||
ssh: mgmt,
|
||||
statePath: path,
|
||||
iface: iface,
|
||||
|
@ -43,9 +44,12 @@ func (nodes *Nodes) AddNode(n *yanic.Node) {
|
|||
if _, ok := nodes.ToUpdate[node.NodeID]; ok {
|
||||
if nodes.List[node.NodeID].IsEqual(node) {
|
||||
delete(nodes.ToUpdate, node.NodeID)
|
||||
nodes.List[node.NodeID] = node
|
||||
nodes.notify(node, true)
|
||||
}
|
||||
} else {
|
||||
nodes.List[node.NodeID] = node
|
||||
nodes.notify(node, true)
|
||||
}
|
||||
logger.Debugf("know already these node")
|
||||
return
|
||||
|
@ -61,14 +65,28 @@ func (nodes *Nodes) AddNode(n *yanic.Node) {
|
|||
logger.Infof("new node with uptime: %s", uptime)
|
||||
|
||||
nodes.List[node.NodeID] = node
|
||||
nodes.notify(node, true)
|
||||
}
|
||||
|
||||
func (nodes *Nodes) AddNotify(f func(*Node, bool)) {
|
||||
nodes.notifyFunc = append(nodes.notifyFunc, f)
|
||||
}
|
||||
func (nodes *Nodes) notify(node *Node, real bool) {
|
||||
for _, f := range nodes.notifyFunc {
|
||||
f(node, real)
|
||||
}
|
||||
}
|
||||
|
||||
func (nodes *Nodes) UpdateNode(node *Node) {
|
||||
if node == nil {
|
||||
log.Log.Warn("no new node to update")
|
||||
return
|
||||
}
|
||||
if n, ok := nodes.List[node.NodeID]; ok {
|
||||
go node.SSHUpdate(nodes.ssh, nodes.iface, n)
|
||||
}
|
||||
nodes.List[node.NodeID] = node
|
||||
nodes.ToUpdate[node.NodeID] = struct{}{}
|
||||
nodes.ToUpdate[node.NodeID] = node
|
||||
nodes.notify(node, false)
|
||||
}
|
||||
|
||||
func (nodes *Nodes) Updater() {
|
||||
|
@ -77,12 +95,13 @@ func (nodes *Nodes) Updater() {
|
|||
go node.SSHSet(nodes.ssh, nodes.iface)
|
||||
}
|
||||
}
|
||||
log.Log.Debug("updater per ssh runs")
|
||||
}
|
||||
|
||||
func (nodes *Nodes) load() {
|
||||
if f, err := os.Open(nodes.statePath); err == nil { // transform data to legacy meshviewer
|
||||
if err = json.NewDecoder(f).Decode(nodes); err == nil {
|
||||
log.Log.Info("loaded", len(nodes.List), "nodes")
|
||||
log.Log.Infof("loaded %d nodes", len(nodes.List))
|
||||
} else {
|
||||
log.Log.Error("failed to unmarshal nodes:", err)
|
||||
}
|
||||
|
@ -93,4 +112,5 @@ func (nodes *Nodes) load() {
|
|||
|
||||
func (nodes *Nodes) Saver() {
|
||||
yanic.SaveJSON(nodes, nodes.statePath)
|
||||
log.Log.Debug("saved state file")
|
||||
}
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
!node_modules/
|
||||
node_modules/*
|
||||
|
||||
!node_modules/semantic-ui-css/
|
||||
node_modules/semantic-ui-css/*
|
||||
!node_modules/semantic-ui-css/semantic.min.css
|
||||
!node_modules/semantic-ui-css/themes/
|
||||
node_modules/semantic-ui-css/themes/*
|
||||
!node_modules/semantic-ui-css/themes/default/
|
||||
node_modules/semantic-ui-css/themes/default/*
|
||||
!node_modules/semantic-ui-css/themes/default/assets/
|
||||
node_modules/semantic-ui-css/themes/default/assets/*
|
||||
!node_modules/semantic-ui-css/themes/default/assets/fonts/
|
|
@ -0,0 +1,11 @@
|
|||
|
||||
.loader {
|
||||
color: #dc0067;
|
||||
font-size: 1.8em;
|
||||
line-height: 2;
|
||||
margin: 30vh auto;
|
||||
text-align: center;
|
||||
}
|
||||
.loader img {
|
||||
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
|
@ -0,0 +1,29 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
|
||||
<title>FreifunkManager</title>
|
||||
<link rel="stylesheet" href="/css/main.css">
|
||||
<script src="//localhost:35729/livereload.js"></script>
|
||||
<script src="/js/config.js"></script>
|
||||
<script src="/js/store.js"></script>
|
||||
<script src="/js/gui.js"></script>
|
||||
<script src="/js/socket.js"></script>
|
||||
<script src="/js/app.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="loader">
|
||||
<p>
|
||||
Lade
|
||||
<br />
|
||||
<img src="/img/logo.jpg" class="spinner" />
|
||||
<br />
|
||||
Karten & Knoten...
|
||||
</p>
|
||||
<noscript>
|
||||
<strong>JavaScript required</strong>
|
||||
</noscript>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1 @@
|
|||
document.title = config.title
|
|
@ -0,0 +1,4 @@
|
|||
var config = {
|
||||
title: 'FreifunkManager - Breminale',
|
||||
backend: 'ws://localhost:8080/websocket'
|
||||
};
|
|
@ -0,0 +1,20 @@
|
|||
var gui = {};
|
||||
|
||||
(function(){
|
||||
|
||||
gui.enable = function enable(){
|
||||
|
||||
};
|
||||
|
||||
gui.render = function render (){
|
||||
console.log(store.will())
|
||||
}
|
||||
gui.disable = function disable(err){
|
||||
document.querySelector('.loader').innerHTML += err
|
||||
+ '<br /><br /><button onclick="location.reload(true)" class="btn text">Try to reload</button>';
|
||||
console.warn(err);
|
||||
};
|
||||
gui.connecting = function connecting(){
|
||||
|
||||
};
|
||||
})();
|
|
@ -0,0 +1,35 @@
|
|||
var socket = new WebSocket(config.backend);
|
||||
|
||||
(function(){
|
||||
// When the connection is open, send some data to the server
|
||||
socket.onopen = function () {
|
||||
gui.enable();
|
||||
};
|
||||
|
||||
// Log errors
|
||||
socket.onerror = function (err) {
|
||||
gui.disable(err);
|
||||
};
|
||||
|
||||
// Log messages from the server
|
||||
socket.onmessage = function (e) {
|
||||
var msg = JSON.parse(e.data);
|
||||
|
||||
if(msg.state === "current") {
|
||||
store.updateReal(msg.node);
|
||||
gui.render();
|
||||
|
||||
} else if (msg.state === "to-update") {
|
||||
store.update(msg.node);
|
||||
gui.render();
|
||||
} else {
|
||||
gui.disable(e);
|
||||
}
|
||||
};
|
||||
|
||||
socket.sendnode = function(node) {
|
||||
var msg = {node:node};
|
||||
var string = JSON.stringify(msg);
|
||||
socket.send(string);
|
||||
};
|
||||
})();
|
|
@ -0,0 +1,21 @@
|
|||
var store = {
|
||||
list:{},
|
||||
toupdate:{}
|
||||
};
|
||||
|
||||
(function(){
|
||||
store.updateReal = function updateReal(node){
|
||||
store.list[node.node_id] = node;
|
||||
};
|
||||
store.update = function update(node){
|
||||
store.toupdate[node.node_id] = node;
|
||||
};
|
||||
store.will = function() {
|
||||
return Object.keys(store.list).map(function(nodeid){
|
||||
if (store.toupdate[nodeid]) {
|
||||
return store.toupdate[nodeid];
|
||||
}
|
||||
return store.list[nodeid];
|
||||
});
|
||||
};
|
||||
})();
|
File diff suppressed because one or more lines are too long
BIN
webroot/node_modules/semantic-ui-css/themes/default/assets/fonts/icons.eot
generated
vendored
Normal file
BIN
webroot/node_modules/semantic-ui-css/themes/default/assets/fonts/icons.eot
generated
vendored
Normal file
Binary file not shown.
BIN
webroot/node_modules/semantic-ui-css/themes/default/assets/fonts/icons.otf
generated
vendored
Normal file
BIN
webroot/node_modules/semantic-ui-css/themes/default/assets/fonts/icons.otf
generated
vendored
Normal file
Binary file not shown.
2671
webroot/node_modules/semantic-ui-css/themes/default/assets/fonts/icons.svg
generated
vendored
Normal file
2671
webroot/node_modules/semantic-ui-css/themes/default/assets/fonts/icons.svg
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
After Width: | Height: | Size: 434 KiB |
BIN
webroot/node_modules/semantic-ui-css/themes/default/assets/fonts/icons.ttf
generated
vendored
Normal file
BIN
webroot/node_modules/semantic-ui-css/themes/default/assets/fonts/icons.ttf
generated
vendored
Normal file
Binary file not shown.
BIN
webroot/node_modules/semantic-ui-css/themes/default/assets/fonts/icons.woff
generated
vendored
Normal file
BIN
webroot/node_modules/semantic-ui-css/themes/default/assets/fonts/icons.woff
generated
vendored
Normal file
Binary file not shown.
BIN
webroot/node_modules/semantic-ui-css/themes/default/assets/fonts/icons.woff2
generated
vendored
Normal file
BIN
webroot/node_modules/semantic-ui-css/themes/default/assets/fonts/icons.woff2
generated
vendored
Normal file
Binary file not shown.
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"dependencies": {
|
||||
"semantic-ui-css": "^2.2.10"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
jquery@x.*:
|
||||
version "3.2.1"
|
||||
resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.2.1.tgz#5c4d9de652af6cd0a770154a631bba12b015c787"
|
||||
|
||||
semantic-ui-css@^2.2.10:
|
||||
version "2.2.10"
|
||||
resolved "https://registry.yarnpkg.com/semantic-ui-css/-/semantic-ui-css-2.2.10.tgz#f8f4470dbeffca0f0f3ff4fb71a35c71e88ad89c"
|
||||
dependencies:
|
||||
jquery x.*
|
|
@ -0,0 +1,106 @@
|
|||
package websocket
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"golang.org/x/net/websocket"
|
||||
|
||||
"github.com/FreifunkBremen/freifunkmanager/lib/log"
|
||||
)
|
||||
|
||||
const channelBufSize = 100
|
||||
|
||||
type Client struct {
|
||||
ip string
|
||||
ws *websocket.Conn
|
||||
ch chan *Message
|
||||
doneCh chan bool
|
||||
}
|
||||
|
||||
func disconnect(c *Client) {
|
||||
delete(clients, c.ip)
|
||||
}
|
||||
|
||||
func NewClient(ip string, ws *websocket.Conn) *Client {
|
||||
|
||||
if ws == nil {
|
||||
panic("ws cannot be nil")
|
||||
}
|
||||
|
||||
return &Client{
|
||||
ws: ws,
|
||||
ch: make(chan *Message, channelBufSize),
|
||||
doneCh: make(chan bool),
|
||||
ip: ip,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) Write(msg *Message) {
|
||||
select {
|
||||
case c.ch <- msg:
|
||||
default:
|
||||
disconnect(c)
|
||||
log.HTTP(c.ws.Request()).Error("client is disconnected.")
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) Done() {
|
||||
c.doneCh <- true
|
||||
}
|
||||
|
||||
func (c *Client) allNodes() {
|
||||
for _, node := range nodes.List {
|
||||
c.Write(&Message{State: StateCurrentNode, Node: node})
|
||||
}
|
||||
for _, node := range nodes.ToUpdate {
|
||||
c.Write(&Message{State: StateUpdateNode, Node: node})
|
||||
}
|
||||
}
|
||||
|
||||
// Listen Write and Read request via chanel
|
||||
func (c *Client) Listen() {
|
||||
go c.listenWrite()
|
||||
c.allNodes()
|
||||
c.listenRead()
|
||||
}
|
||||
|
||||
// Listen write request via chanel
|
||||
func (c *Client) listenWrite() {
|
||||
for {
|
||||
select {
|
||||
case msg := <-c.ch:
|
||||
websocket.JSON.Send(c.ws, msg)
|
||||
|
||||
case <-c.doneCh:
|
||||
disconnect(c)
|
||||
c.doneCh <- true
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Listen read request via chanel
|
||||
func (c *Client) listenRead() {
|
||||
for {
|
||||
select {
|
||||
|
||||
case <-c.doneCh:
|
||||
disconnect(c)
|
||||
c.doneCh <- true
|
||||
return
|
||||
|
||||
default:
|
||||
var msg Message
|
||||
err := websocket.JSON.Receive(c.ws, &msg)
|
||||
if err == io.EOF {
|
||||
log.HTTP(c.ws.Request()).Info("disconnect")
|
||||
c.doneCh <- true
|
||||
} else if err != nil {
|
||||
log.HTTP(c.ws.Request()).Error(err)
|
||||
} else {
|
||||
log.HTTP(c.ws.Request()).Info("recieve nodeupdate")
|
||||
nodes.UpdateNode(msg.Node)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package websocket
|
||||
|
||||
import "github.com/FreifunkBremen/freifunkmanager/runtime"
|
||||
|
||||
type Message struct {
|
||||
State string `json:"state"`
|
||||
Node *runtime.Node `json:"node"`
|
||||
}
|
||||
|
||||
const (
|
||||
StateUpdateNode = "to-update"
|
||||
StateCurrentNode = "current"
|
||||
)
|
|
@ -0,0 +1,59 @@
|
|||
package websocket
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"golang.org/x/net/websocket"
|
||||
|
||||
httpLib "github.com/FreifunkBremen/freifunkmanager/lib/http"
|
||||
"github.com/FreifunkBremen/freifunkmanager/lib/log"
|
||||
"github.com/FreifunkBremen/freifunkmanager/runtime"
|
||||
)
|
||||
|
||||
var nodes *runtime.Nodes
|
||||
var clients map[string]*Client
|
||||
var quit chan struct{}
|
||||
|
||||
func Start(nodeBind *runtime.Nodes) {
|
||||
nodes = nodeBind
|
||||
clients = make(map[string]*Client)
|
||||
quit = make(chan struct{})
|
||||
|
||||
http.Handle("/websocket", websocket.Handler(func(ws *websocket.Conn) {
|
||||
defer func() {
|
||||
ws.Close()
|
||||
}()
|
||||
r := ws.Request()
|
||||
log.HTTP(r).Infof("new client")
|
||||
ip := httpLib.GetRemoteIP(r)
|
||||
client := NewClient(ip, ws)
|
||||
clients[ip] = client
|
||||
client.Listen()
|
||||
}))
|
||||
|
||||
nodes.AddNotify(Notify)
|
||||
/*
|
||||
for {
|
||||
select {
|
||||
case <-quit:
|
||||
return
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
func Notify(node *runtime.Node, real bool) {
|
||||
state := StateUpdateNode
|
||||
if real {
|
||||
state = StateCurrentNode
|
||||
}
|
||||
for _, c := range clients {
|
||||
c.Write(&Message{State: state, Node: node})
|
||||
}
|
||||
}
|
||||
|
||||
func Close() {
|
||||
for _, c := range clients {
|
||||
c.Done()
|
||||
}
|
||||
close(quit)
|
||||
}
|
|
@ -22,7 +22,7 @@ type Dialer struct {
|
|||
func Dial(ctype, addr string) *Dialer {
|
||||
conn, err := net.Dial(ctype, addr)
|
||||
if err != nil {
|
||||
log.Log.Panic("yanic dial failed")
|
||||
log.Log.Panicf("yanic dial to %s:%s failed", ctype, addr)
|
||||
}
|
||||
dialer := &Dialer{
|
||||
conn: conn,
|
||||
|
|
Loading…
Reference in New Issue