From 832c30fa2682114fc1bdf9fb623afe70ede38d3c Mon Sep 17 00:00:00 2001 From: Martin/Geno Date: Fri, 24 Aug 2018 23:34:02 +0200 Subject: [PATCH] update websocket + handle settings in session --- runtime/node_ssh.go | 11 +++++---- webroot/css/_menu.less | 7 +++++- webroot/js/element/menu.js | 28 +++++++++++++++------- webroot/js/index.js | 3 +++ webroot/js/socket.js | 1 + webroot/js/store.js | 4 +--- webroot/js/view/list.js | 3 +++ webroot/js/view/settings.js | 42 +++++++++++++++++++++++++++++++++ websocket/handler.go | 27 --------------------- websocket/hd_auth.go | 37 +++++++++++++++++++++++++---- websocket/msg.go | 1 + websocket/server.go | 47 +++++++++++++++++++++++++------------ 12 files changed, 147 insertions(+), 64 deletions(-) create mode 100644 webroot/js/view/settings.js delete mode 100644 websocket/handler.go diff --git a/runtime/node_ssh.go b/runtime/node_ssh.go index d924159..9792c15 100644 --- a/runtime/node_ssh.go +++ b/runtime/node_ssh.go @@ -2,7 +2,6 @@ package runtime import ( "fmt" - "strings" log "github.com/sirupsen/logrus" @@ -66,12 +65,13 @@ func (n *Node) SSHUpdate(sshmgmt *ssh.Manager) bool { runWifi = true } if n.Wireless.Channel24 != n.WirelessRespondd.Channel24 { + //ubus call hostapd.%s switch_chan '{"freq":%d}' ssh.Execute(n.Address, client, fmt.Sprintf(` - ubus call hostapd.%s switch_chan '{"freq":%d}' uci set wireless.%s.channel='%d'; uci commit wireless;`, - strings.Replace(radio, "radio", "client", 1), ch.Frequency, + //strings.Replace(radio, "radio", "client", 1), ch.Frequency, radio, n.Wireless.Channel24)) + runWifi = true } } @@ -95,12 +95,13 @@ func (n *Node) SSHUpdate(sshmgmt *ssh.Manager) bool { runWifi = true } if n.Wireless.Channel5 != n.WirelessRespondd.Channel5 { + //ubus call hostapd.%s switch_chan '{"freq":%d}' ssh.Execute(n.Address, client, fmt.Sprintf(` - ubus call hostapd.%s switch_chan '{"freq":%d}' uci set wireless.%s.channel='%d'; uci commit wireless;`, - strings.Replace(radio, "radio", "client", 1), ch.Frequency, + //strings.Replace(radio, "radio", "client", 1), ch.Frequency, radio, n.Wireless.Channel5)) + runWifi = true } } return true diff --git a/webroot/css/_menu.less b/webroot/css/_menu.less index 70822bb..b9a80ec 100644 --- a/webroot/css/_menu.less +++ b/webroot/css/_menu.less @@ -21,10 +21,15 @@ nav { list-style-type: none; } - ul > li { + ul > li, + ul li.right { float:left; display: inline-block; + &.right { + float: right; + } + a, span { display: inline-block; diff --git a/webroot/js/element/menu.js b/webroot/js/element/menu.js index 3e5726f..14a875a 100644 --- a/webroot/js/element/menu.js +++ b/webroot/js/element/menu.js @@ -31,6 +31,7 @@ export class MenuView extends View { login() { socket.sendjson({'subject': 'login', 'body': this._loginInput}, (msg) => { if (msg.body) { + store.settings = msg.body; store.isLogin = true; render(); }else { @@ -60,7 +61,7 @@ export class MenuView extends View { render () { const socketStatus = socket.getStatus(); let statusClass = 'status ', - vLogin = V.h('li', { + vLogin = [V.h('li', { 'class': 'login', }, [ V.h('input', { @@ -70,15 +71,24 @@ export class MenuView extends View { }), V.h('a', { 'onclick': this.login.bind(this) - }, 'Login' - ) - ]); + }, 'Login') + ])]; if (store.isLogin) { - vLogin = V.h('li', { - 'class': 'login', - 'onclick': this.logout.bind(this) - }, 'Logout'); + vLogin = [ + V.h('li', { + 'class': 'login', + },[ + V.h('a', { + 'onclick': this.logout.bind(this) + }, 'Logout') + ]), + V.h('li', { + 'class':'right item-3' + },[V.h('a', { + 'href': '#/settings' + }, 'Settings')]) + ]; } if (socketStatus !== 1) { @@ -93,6 +103,6 @@ export class MenuView extends View { V.render(this.vMenu, this.vMenu = V.h('span',{},[V.h('li', { 'class': statusClass, 'onclick': () => location.reload(true) - }), vLogin]), this.menuList); + })].concat(vLogin)), this.menuList); } } diff --git a/webroot/js/index.js b/webroot/js/index.js index b0c468d..2aafa81 100644 --- a/webroot/js/index.js +++ b/webroot/js/index.js @@ -12,6 +12,7 @@ import config from './config'; import {ListView} from './view/list'; import {MapView} from './view/map'; import {StatisticsView} from './view/statistics'; +import {SettingsView} from './view/settings'; import {NodeView} from './view/node'; @@ -20,6 +21,7 @@ window.onload = () => { const listView = new ListView(); const mapView = new MapView(); const statisticsView = new StatisticsView(); + const settingsView = new SettingsView(); const nodeView = new NodeView(); @@ -27,6 +29,7 @@ window.onload = () => { '/list': () => gui.setView(listView), '/map': () => gui.setView(mapView), '/statistics': () => gui.setView(statisticsView), + '/settings': () => gui.setView(settingsView), '/n/:nodeID': { 'as': 'node', // eslint-disable-next-line func-name-matching diff --git a/webroot/js/socket.js b/webroot/js/socket.js index 0ac6f4f..9e7ee4c 100644 --- a/webroot/js/socket.js +++ b/webroot/js/socket.js @@ -178,6 +178,7 @@ export function sendnode(node, callback) { setEvent('auth_status', (msg) => { if (msg.body) { + store.settings = msg.body store.isLogin = true; notify.send({ diff --git a/webroot/js/store.js b/webroot/js/store.js index bb96d87..a43e8fc 100644 --- a/webroot/js/store.js +++ b/webroot/js/store.js @@ -1,7 +1,6 @@ import config from './config'; -const current = {}, - list = {}; +const list = {}; // Returns the node with specified id (or null if node doesn't exist). export function getNode (nodeid) { @@ -30,7 +29,6 @@ export function createNode (nodeid) { }; // Overwrites the values for the specified node (identified by its node_id) with new values. -// If system==false, the special "current" node will be modified instead. export function updateNode (node) { list[node.node_id] = node; }; diff --git a/webroot/js/view/list.js b/webroot/js/view/list.js index 2feacae..e1f3fea 100644 --- a/webroot/js/view/list.js +++ b/webroot/js/view/list.js @@ -107,6 +107,7 @@ export class ListView extends View { V.h('td', {}, V.h('input',{ 'value': this._hostname || node.hostname, 'oninput':(e) => { + this._node_id = node.node_id; this._hostname = e.target.value; }, 'onfocusout':(e) => { @@ -167,6 +168,7 @@ export class ListView extends View { 'max': 23, 'value': this._txpower24 || node.wireless.txpower24, 'oninput':(e) => { + this._node_id = node.node_id; this._txpower24 = e.target.value; }, 'onfocusout':(e) => { @@ -188,6 +190,7 @@ export class ListView extends View { 'max': 23, 'value': this._txpower5 || node.wireless.txpower5, 'oninput':(e) => { + this._node_id = node.node_id; this._txpower5 = e.target.value; }, 'onfocusout':(e) => { diff --git a/webroot/js/view/settings.js b/webroot/js/view/settings.js new file mode 100644 index 0000000..abbc60c --- /dev/null +++ b/webroot/js/view/settings.js @@ -0,0 +1,42 @@ +import * as domlib from '../domlib'; +import * as gui from '../gui'; +import * as socket from '../socket'; +import * as store from '../store'; +import View from '../view'; + +export class SettingsView extends View { + + constructor () { + super(); + domlib.newAt(this.el, 'h1').innerHTML = 'Settings'; + + this.ping = domlib.newAt(this.el, 'input',{ + 'type': 'checkbox', + 'id': 'settings_ping', + 'onchange': this.updateSettings('ping').bind(this), + }); + domlib.newAt(this.el, 'label', {'for': 'settings_ping'},'Ping'); + } + + updateSettings (key) { + return function(ev){ + const copySettings = Object.assign({}, store.settings); + if (ev.target.type === 'checkbox') { + copySettings[key] = ev.target.checked; + socket.sendjson({'subject': 'settings','body': copySettings}, (msg) => { + if (msg.body) { + store.settings = copySettings; + } + }); + } + + } + } + + render () { + if(!store.settings){ + return + } + this.ping.checked = store.settings.ping || false; + } +} diff --git a/websocket/handler.go b/websocket/handler.go deleted file mode 100644 index 2b7641c..0000000 --- a/websocket/handler.go +++ /dev/null @@ -1,27 +0,0 @@ -package websocket - -import ( - log "github.com/sirupsen/logrus" - - "dev.sum7.eu/genofire/golang-lib/websocket" -) - -type WebsocketHandlerFunc func(*log.Entry, *websocket.Message) error - -func (ws *WebsocketServer) MessageHandler() { - for msg := range ws.inputMSG { - logger := log.WithFields(log.Fields{ - "session": msg.Session, - "id": msg.ID, - "subject": msg.Subject, - }) - if handler, ok := ws.handlers[msg.Subject]; ok { - err := handler(logger, msg) - if err != nil { - logger.Errorf("websocket message '%s' cound not handle: %s", msg.Subject, err) - } - } else { - logger.Warnf("websocket message '%s' cound not handle", msg.Subject) - } - } -} diff --git a/websocket/hd_auth.go b/websocket/hd_auth.go index d6f3085..c69563c 100644 --- a/websocket/hd_auth.go +++ b/websocket/hd_auth.go @@ -4,15 +4,16 @@ import ( "time" "github.com/google/uuid" + "github.com/mitchellh/mapstructure" log "github.com/sirupsen/logrus" "dev.sum7.eu/genofire/golang-lib/websocket" ) type Session struct { - SessionID uuid.UUID `json:"-" gorm:"primary_key"` - Lastseen *time.Time `json:"-"` - Ping bool `json:"ping"` + SessionID uuid.UUID `json:"-" gorm:"primary_key" mapstructure:"-"` + Lastseen *time.Time `json:"-" mapstructure:"-"` + Ping bool `json:"ping" mapstructure:"ping"` } func (ws *WebsocketServer) IsLoggedIn(msg *websocket.Message) (*Session, bool) { @@ -35,7 +36,10 @@ func (ws *WebsocketServer) loginHandler(logger *log.Entry, msg *websocket.Messag } err := ws.db.First(&session) if err.Error == nil { - msg.Answer(msg.Subject, true) + msg.Answer(msg.Subject, session) + now := time.Now() + session.Lastseen = &now + ws.db.Save(&session) logger.Warn("already loggedIn") return nil } @@ -73,6 +77,31 @@ func (ws *WebsocketServer) authStatusHandler(logger *log.Entry, msg *websocket.M } return nil } + +func (ws *WebsocketServer) settingsHandler(logger *log.Entry, msg *websocket.Message) error { + session, ok := ws.IsLoggedIn(msg) + if !ok { + msg.Answer(msg.Subject, false) + logger.Warn("logout without login") + return nil + } + + var setting Session + if err := mapstructure.Decode(msg.Body, &setting); err != nil { + msg.Answer(msg.Subject, false) + logger.Warnf("not able to decode data: %s", err) + return nil + } + setting.SessionID = session.SessionID + setting.Lastseen = session.Lastseen + + err := ws.db.Save(&setting) + + logger.Debug("done") + msg.Answer(msg.Subject, err.Error == nil) + return err.Error +} + func (ws *WebsocketServer) logoutHandler(logger *log.Entry, msg *websocket.Message) error { session := Session{ SessionID: msg.Session, diff --git a/websocket/msg.go b/websocket/msg.go index 7a41a55..f11a4a9 100644 --- a/websocket/msg.go +++ b/websocket/msg.go @@ -5,6 +5,7 @@ const ( MessageTypeLogin = "login" MessageTypeAuthStatus = "auth_status" + MessageTypeSettings = "settings" MessageTypeLogout = "logout" MessageTypeChannelsWifi24 = "channels_wifi24" diff --git a/websocket/server.go b/websocket/server.go index cca2a8f..0916a63 100644 --- a/websocket/server.go +++ b/websocket/server.go @@ -1,11 +1,11 @@ package websocket import ( - "net/http" "time" wsLib "dev.sum7.eu/genofire/golang-lib/websocket" "github.com/jinzhu/gorm" + log "github.com/sirupsen/logrus" "github.com/FreifunkBremen/yanic/runtime" ) @@ -16,10 +16,21 @@ type WebsocketServer struct { blacklistFor time.Duration secret string ipPrefix string + ws *wsLib.WebsocketHandlerService +} - inputMSG chan *wsLib.Message - ws *wsLib.Server - handlers map[string]WebsocketHandlerFunc +func websocketHandlerFunc(f func(logger *log.Entry, msg *wsLib.Message) error) wsLib.MessageHandleFunc { + return func(msg *wsLib.Message) { + logger := log.WithFields(log.Fields{ + "session": msg.Session, + "id": msg.ID, + "subject": msg.Subject, + }) + err := f(logger, msg) + if err != nil { + logger.Warnf("websocket message '%s' cound not handle: %s", msg.Subject, err) + } + } } func NewWebsocketServer(secret string, ipPrefix string, db *gorm.DB, blacklistFor time.Duration, nodes *runtime.Nodes) *WebsocketServer { @@ -27,27 +38,33 @@ func NewWebsocketServer(secret string, ipPrefix string, db *gorm.DB, blacklistFo nodes: nodes, db: db, blacklistFor: blacklistFor, - handlers: make(map[string]WebsocketHandlerFunc), - inputMSG: make(chan *wsLib.Message), secret: secret, ipPrefix: ipPrefix, } - ownWS.ws = wsLib.NewServer(ownWS.inputMSG, wsLib.NewSessionManager()) + ownWS.ws = wsLib.NewWebsocketHandlerService() // Register Handlers - ownWS.handlers[MessageTypeConnect] = ownWS.connectHandler + ownWS.ws.SetHandler(MessageTypeConnect, websocketHandlerFunc(ownWS.connectHandler)) - ownWS.handlers[MessageTypeLogin] = ownWS.loginHandler - ownWS.handlers[MessageTypeAuthStatus] = ownWS.authStatusHandler - ownWS.handlers[MessageTypeLogout] = ownWS.logoutHandler + ownWS.ws.SetHandler(MessageTypeLogin, websocketHandlerFunc(ownWS.loginHandler)) + ownWS.ws.SetHandler(MessageTypeAuthStatus, websocketHandlerFunc(ownWS.authStatusHandler)) + ownWS.ws.SetHandler(MessageTypeSettings, websocketHandlerFunc(ownWS.settingsHandler)) + ownWS.ws.SetHandler(MessageTypeLogout, websocketHandlerFunc(ownWS.logoutHandler)) - ownWS.handlers[MessageTypeNode] = ownWS.nodeHandler + ownWS.ws.SetHandler(MessageTypeNode, websocketHandlerFunc(ownWS.nodeHandler)) - http.HandleFunc("/ws", ownWS.ws.Handler) - go ownWS.MessageHandler() + ownWS.ws.FallbackHandler = func(msg *wsLib.Message) { + log.WithFields(log.Fields{ + "session": msg.Session, + "id": msg.ID, + "subject": msg.Subject, + }).Warnf("websocket message '%s' cound not handle", msg.Subject) + } + + ownWS.ws.Listen("/ws") return &ownWS } func (ws *WebsocketServer) Close() { - close(ws.inputMSG) + ws.ws.Close() }