add easy webinterface
This commit is contained in:
parent
d20e749038
commit
16fdeb85d4
|
@ -24,7 +24,7 @@ test-my-project:
|
|||
stage: test
|
||||
script:
|
||||
- go get github.com/client9/misspell/cmd/misspell
|
||||
- misspell -error .
|
||||
- find . -type f | grep -v webroot/assets | xargs misspell -error
|
||||
- ./.ci/check-gofmt
|
||||
- ./.ci/check-testfiles
|
||||
- go test $(go list ./... | grep -v /vendor/) -v -coverprofile .testCoverage.txt
|
||||
|
@ -44,7 +44,10 @@ deploy:
|
|||
script:
|
||||
- go install "dev.sum7.eu/$CI_PROJECT_PATH"
|
||||
- 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )'
|
||||
- 'which rsync || ( apt-get update -y && apt-get install rsync -y )'
|
||||
- eval $(ssh-agent -s)
|
||||
- echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add - > /dev/null
|
||||
- rsync -e "ssh -6 -o StrictHostKeyChecking=no -p $SSH_PORT" -a --delete "/builds/$CI_PROJECT_PATH/webroot/" "$CI_PROJECT_NAME@$SSH_HOST":/opt/$CI_PROJECT_NAME/webroot/
|
||||
- ssh -6 -o StrictHostKeyChecking=no -p $SSH_PORT "$CI_PROJECT_NAME@$SSH_HOST" sudo /usr/bin/systemctl stop $CI_PROJECT_NAME
|
||||
- scp -6 -o StrictHostKeyChecking=no -P $SSH_PORT "/go/bin/$CI_PROJECT_NAME" "$CI_PROJECT_NAME@$SSH_HOST":/opt/$CI_PROJECT_NAME/bin
|
||||
- ssh -6 -o StrictHostKeyChecking=no -p $SSH_PORT "$CI_PROJECT_NAME@$SSH_HOST" sudo /usr/bin/systemctl restart $CI_PROJECT_NAME
|
||||
- ssh -6 -o StrictHostKeyChecking=no -p $SSH_PORT "$CI_PROJECT_NAME@$SSH_HOST" sudo /usr/bin/systemctl start $CI_PROJECT_NAME
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"net"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
@ -12,7 +13,9 @@ import (
|
|||
"dev.sum7.eu/genofire/wifictld-analyzer/capture"
|
||||
"dev.sum7.eu/genofire/wifictld-analyzer/config"
|
||||
"dev.sum7.eu/genofire/wifictld-analyzer/controller"
|
||||
"dev.sum7.eu/genofire/wifictld-analyzer/data"
|
||||
"dev.sum7.eu/genofire/wifictld-analyzer/database"
|
||||
"dev.sum7.eu/genofire/wifictld-analyzer/web"
|
||||
)
|
||||
|
||||
// queryCmd represents the query command
|
||||
|
@ -31,7 +34,26 @@ var controllerCmd = &cobra.Command{
|
|||
ctr := controller.NewController(db)
|
||||
defer ctr.Close()
|
||||
|
||||
coll := capture.NewCollector(ctr.Handler, configObj.Interfaces)
|
||||
var handlers []data.Handler
|
||||
|
||||
if configObj.Webserver.Enable {
|
||||
log.Infof("starting webserver on %s", configObj.Webserver.Bind)
|
||||
srv := web.New(configObj.Webserver)
|
||||
go srv.Start()
|
||||
handlers = append(handlers, srv.Handler)
|
||||
defer srv.Close()
|
||||
}
|
||||
|
||||
coll := capture.NewCollector(func(addr *net.UDPAddr, msg *data.SocketMSG) (*data.SocketMSG, error) {
|
||||
for _, a := range handlers {
|
||||
a(addr, msg)
|
||||
}
|
||||
if !configObj.Answer {
|
||||
ctr.Handler(addr, msg)
|
||||
return nil, nil
|
||||
}
|
||||
return ctr.Handler(addr, msg)
|
||||
}, configObj.Interfaces)
|
||||
defer coll.Close()
|
||||
|
||||
ctr.Send = coll.Send
|
||||
|
@ -47,5 +69,5 @@ var controllerCmd = &cobra.Command{
|
|||
}
|
||||
|
||||
func init() {
|
||||
RootCmd.AddCommand(controllerCmd)
|
||||
RootCMD.AddCommand(controllerCmd)
|
||||
}
|
||||
|
|
|
@ -58,7 +58,7 @@ var dumpCmd = &cobra.Command{
|
|||
}
|
||||
|
||||
func init() {
|
||||
RootCmd.AddCommand(dumpCmd)
|
||||
RootCMD.AddCommand(dumpCmd)
|
||||
dumpCmd.Flags().IntVar(&port, "port", capture.Port, "define a port to listen (if not set or set to 0 the kernel will use a random free port at its own)")
|
||||
dumpCmd.Flags().StringVar(&ipAddress, "listen", capture.MulticastAddressDefault, "")
|
||||
}
|
||||
|
|
19
cmd/root.go
19
cmd/root.go
|
@ -6,19 +6,22 @@ import (
|
|||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var debug bool
|
||||
var (
|
||||
debug bool
|
||||
timestamps bool
|
||||
)
|
||||
|
||||
// RootCmd represents the base command when called without any subcommands
|
||||
var RootCmd = &cobra.Command{
|
||||
// RootCMD represents the base command when called without any subcommands
|
||||
var RootCMD = &cobra.Command{
|
||||
Use: "analyzer",
|
||||
Short: "wifictld analyzer",
|
||||
Long: `capture wifictld traffic and display thus`,
|
||||
}
|
||||
|
||||
// Execute adds all child commands to the root command and sets flags appropriately.
|
||||
// This is called by main.main(). It only needs to happen once to the rootCmd.
|
||||
// This is called by main.main(). It only needs to happen once to the RootCMD.
|
||||
func Execute() {
|
||||
if err := RootCmd.Execute(); err != nil {
|
||||
if err := RootCMD.Execute(); err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
}
|
||||
|
@ -27,7 +30,11 @@ func init() {
|
|||
if debug {
|
||||
log.SetLevel(log.DebugLevel)
|
||||
}
|
||||
log.SetFormatter(&log.TextFormatter{
|
||||
DisableTimestamp: timestamps,
|
||||
})
|
||||
log.Debug("show debug")
|
||||
})
|
||||
RootCmd.PersistentFlags().BoolVar(&debug, "v", false, "show debug log")
|
||||
RootCMD.PersistentFlags().BoolVar(&debug, "v", false, "show debug log")
|
||||
RootCMD.PersistentFlags().BoolVar(×tamps, "timestamps", false, "Enables timestamps for log output")
|
||||
}
|
||||
|
|
|
@ -2,9 +2,12 @@ package config
|
|||
|
||||
import (
|
||||
"dev.sum7.eu/genofire/wifictld-analyzer/capture"
|
||||
"dev.sum7.eu/genofire/wifictld-analyzer/web"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
StatePath string `toml:"state_path"`
|
||||
Answer bool `toml:"answer"`
|
||||
Webserver *web.Config `toml:"webserver"`
|
||||
Interfaces []*capture.IFaceConfig `toml:"interfaces"`
|
||||
}
|
||||
|
|
|
@ -1,6 +1,13 @@
|
|||
state_path = "/tmp/wifictld.json"
|
||||
answer = false
|
||||
|
||||
|
||||
[webserver]
|
||||
enable = true
|
||||
bind = ":8080"
|
||||
webroot = "./webroot/"
|
||||
|
||||
[[interfaces]]
|
||||
ifname = "wlp4s0"
|
||||
#port = 1000
|
||||
#ip_address = "ff02::31f1"
|
||||
ifname = "wlp4s0"
|
||||
#port = 1000
|
||||
#ip_address = "ff02::31f1"
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
package data
|
||||
|
||||
import "net"
|
||||
|
||||
type HardwareAddr struct{ net.HardwareAddr }
|
||||
|
||||
//MarshalJSON to bytearray
|
||||
func (a HardwareAddr) MarshalText() ([]byte, error) {
|
||||
return []byte(a.String()), nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON from bytearray
|
||||
func (a HardwareAddr) UnmarshalText(data []byte) (err error) {
|
||||
a.HardwareAddr, err = net.ParseMAC(string(data))
|
||||
return
|
||||
}
|
|
@ -3,7 +3,6 @@ package data
|
|||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/bdlm/log"
|
||||
|
@ -31,8 +30,8 @@ func (a SocketMSGType) Is(b SocketMSGType) bool {
|
|||
|
||||
// SocketMSG package of wifictld format
|
||||
type SocketMSG struct {
|
||||
Types SocketMSGType
|
||||
Client *WifiClient
|
||||
Types SocketMSGType `json:"types"`
|
||||
Client *WifiClient `json:"client,omitempty"`
|
||||
}
|
||||
|
||||
func NewSocketMSG(obj []byte) (*SocketMSG, error) {
|
||||
|
@ -51,17 +50,17 @@ func (msg *SocketMSG) Marshal() ([]byte, error) {
|
|||
pos += 4
|
||||
|
||||
if msg.Types.Is(SocketMSGTypeClient) {
|
||||
obj[pos] = msg.Client.Addr[0]
|
||||
obj[pos] = msg.Client.Addr.HardwareAddr[0]
|
||||
pos++
|
||||
obj[pos] = msg.Client.Addr[1]
|
||||
obj[pos] = msg.Client.Addr.HardwareAddr[1]
|
||||
pos++
|
||||
obj[pos] = msg.Client.Addr[2]
|
||||
obj[pos] = msg.Client.Addr.HardwareAddr[2]
|
||||
pos++
|
||||
obj[pos] = msg.Client.Addr[3]
|
||||
obj[pos] = msg.Client.Addr.HardwareAddr[3]
|
||||
pos++
|
||||
obj[pos] = msg.Client.Addr[4]
|
||||
obj[pos] = msg.Client.Addr.HardwareAddr[4]
|
||||
pos++
|
||||
obj[pos] = msg.Client.Addr[5]
|
||||
obj[pos] = msg.Client.Addr.HardwareAddr[5]
|
||||
pos++
|
||||
binary.BigEndian.PutUint32(obj[pos:(pos+4)], uint32(msg.Client.Time.Unix()))
|
||||
pos += 4
|
||||
|
@ -100,7 +99,7 @@ func (msg *SocketMSG) Unmarshal(obj []byte) error {
|
|||
|
||||
if msg.Types.Is(SocketMSGTypeClient) {
|
||||
msg.Client = &WifiClient{
|
||||
Addr: net.HardwareAddr(obj[pos:(pos + 6)]),
|
||||
Addr: HardwareAddr{HardwareAddr: obj[pos:(pos + 6)]},
|
||||
Time: time.Unix(int64(binary.BigEndian.Uint32(obj[(pos+6):(pos+10)])), 0),
|
||||
TryProbe: binary.BigEndian.Uint16(obj[(pos + 10):(pos + 12)]),
|
||||
TryAuth: binary.BigEndian.Uint16(obj[(pos + 12):(pos + 14)]),
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
package data
|
||||
|
||||
import (
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
import "time"
|
||||
|
||||
const (
|
||||
// default multicast group used by announced
|
||||
|
@ -18,13 +15,13 @@ const (
|
|||
|
||||
// WifiClient datatype of wifictld
|
||||
type WifiClient struct {
|
||||
Addr net.HardwareAddr
|
||||
Time time.Time
|
||||
TryProbe uint16
|
||||
TryAuth uint16
|
||||
Connected bool
|
||||
Authed bool
|
||||
FreqHighest uint16
|
||||
SignalLowFreq int16
|
||||
SignalHighFreq int16
|
||||
Addr HardwareAddr `json:"addr"`
|
||||
Time time.Time `json:"time"`
|
||||
TryProbe uint16 `json:"try_probe"`
|
||||
TryAuth uint16 `json:"try_auth"`
|
||||
Connected bool `json:"connected"`
|
||||
Authed bool `json:"authed"`
|
||||
FreqHighest uint16 `json:"freq_highest"`
|
||||
SignalLowFreq int16 `json:"signal_low_freq"`
|
||||
SignalHighFreq int16 `json:"signal_high_freq"`
|
||||
}
|
||||
|
|
|
@ -11,17 +11,17 @@ import (
|
|||
)
|
||||
|
||||
type Client struct {
|
||||
AP *AP `json:"-"`
|
||||
APAddr string `json:"ap"`
|
||||
Addr net.HardwareAddr `json:"-"`
|
||||
TryProbe uint16 `json:"try_probe"`
|
||||
TryAuth uint16 `json:"try_auth"`
|
||||
Connected bool `json:"connected"`
|
||||
Authed bool `json:"authed"`
|
||||
FreqHighest uint16 `json:"freq_highest"`
|
||||
SignalLowFreq int16 `json:"signal_low_freq"`
|
||||
SignalHighFreq int16 `json:"signal_high_freq"`
|
||||
Lastseen jsontime.Time `json:"lastseen"`
|
||||
AP *AP `json:"-"`
|
||||
APAddr string `json:"ap"`
|
||||
Addr data.HardwareAddr `json:"-"`
|
||||
TryProbe uint16 `json:"try_probe"`
|
||||
TryAuth uint16 `json:"try_auth"`
|
||||
Connected bool `json:"connected"`
|
||||
Authed bool `json:"authed"`
|
||||
FreqHighest uint16 `json:"freq_highest"`
|
||||
SignalLowFreq int16 `json:"signal_low_freq"`
|
||||
SignalHighFreq int16 `json:"signal_high_freq"`
|
||||
Lastseen jsontime.Time `json:"lastseen"`
|
||||
}
|
||||
|
||||
func (db *DB) LearnClient(apIP net.IP, clientWifictl *data.WifiClient) bool {
|
||||
|
@ -70,7 +70,7 @@ func (db *DB) LearnClient(apIP net.IP, clientWifictl *data.WifiClient) bool {
|
|||
return ret
|
||||
}
|
||||
|
||||
func (db *DB) GetClient(addr net.HardwareAddr) *data.WifiClient {
|
||||
func (db *DB) GetClient(addr data.HardwareAddr) *data.WifiClient {
|
||||
client, ok := db.Clients[addr.String()]
|
||||
wClient := &data.WifiClient{
|
||||
Addr: addr,
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/bdlm/log"
|
||||
|
@ -25,7 +24,7 @@ func NewDB(path string) *DB {
|
|||
file.ReadJSON(path, db)
|
||||
|
||||
for addr, client := range db.Clients {
|
||||
client.Addr, _ = net.ParseMAC(addr)
|
||||
client.Addr.UnmarshalText([]byte(addr))
|
||||
if ap, ok := db.APs[client.APAddr]; ok {
|
||||
client.AP = ap
|
||||
}
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
package web
|
||||
|
||||
type Config struct {
|
||||
Enable bool `toml:"enable"`
|
||||
Bind string `toml:"bind"`
|
||||
Webroot string `toml:"webroot"`
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
package web
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/http"
|
||||
|
||||
"github.com/NYTimes/gziphandler"
|
||||
"github.com/bdlm/log"
|
||||
|
||||
"dev.sum7.eu/genofire/golang-lib/websocket"
|
||||
|
||||
"dev.sum7.eu/genofire/wifictld-analyzer/data"
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
web *http.Server
|
||||
ws *websocket.WebsocketHandlerService
|
||||
}
|
||||
|
||||
// New creates a new webserver and starts it
|
||||
func New(config *Config) *Server {
|
||||
ws := websocket.NewWebsocketHandlerService()
|
||||
ws.Listen("/ws")
|
||||
|
||||
http.Handle("/", gziphandler.GzipHandler(http.FileServer(http.Dir(config.Webroot))))
|
||||
return &Server{
|
||||
web: &http.Server{
|
||||
Addr: config.Bind,
|
||||
},
|
||||
ws: ws,
|
||||
}
|
||||
}
|
||||
|
||||
func (srv *Server) Handler(addr *net.UDPAddr, msg *data.SocketMSG) (*data.SocketMSG, error) {
|
||||
srv.ws.SendAll(&websocket.Message{
|
||||
Subject: "wifictld_pkg",
|
||||
Body: map[string]interface{}{
|
||||
"ip": addr.IP,
|
||||
"msg": msg,
|
||||
},
|
||||
})
|
||||
return msg, nil
|
||||
}
|
||||
func (srv *Server) Start() {
|
||||
// service connections
|
||||
if err := srv.web.ListenAndServe(); err != http.ErrServerClosed {
|
||||
log.Panicf("webserver crashed: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (srv *Server) Close() {
|
||||
srv.web.Close()
|
||||
srv.ws.Close()
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package web
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestWebserver(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
srv := New(&Config{
|
||||
Bind: ":12345",
|
||||
Webroot: "/tmp",
|
||||
})
|
||||
assert.NotNil(srv)
|
||||
|
||||
go srv.Start()
|
||||
|
||||
time.Sleep(time.Millisecond * 200)
|
||||
|
||||
assert.Panics(func() {
|
||||
srv.Start()
|
||||
}, "not allowed to listen twice")
|
||||
|
||||
srv.Close()
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
wget -O vanilla-framework.css https://assets.ubuntu.com/v1/vanilla-framework-version-1.8.1.min.css
|
||||
wget -O vue.js https://cdn.jsdelivr.net/npm/vue@2.6.7/dist/vue.js
|
||||
wget -O vue-route.js https://unpkg.com/vue-router/dist/vue-router.js
|
||||
wget -O vuex.js https://unpkg.com/vuex@2.0.0/dist/vuex.min.js
|
||||
wget -O vue-websocket.js https://raw.githubusercontent.com/nathantsoi/vue-native-websocket/master/dist/build.js
|
||||
wget -O vue-websocket.js.map https://raw.githubusercontent.com/nathantsoi/vue-native-websocket/master/dist/build.js.map
|
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,10 @@
|
|||
.p-navigation__logo {
|
||||
font-size: 1.5rem;
|
||||
line-height: 3rem;
|
||||
}
|
||||
.p-navigation .p-navigation__logo .p-navigation__link {
|
||||
color: red;
|
||||
}
|
||||
.p-navigation .p-navigation__logo.online .p-navigation__link {
|
||||
color: black;
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
||||
<title>Wifictld</title>
|
||||
<link rel="stylesheet" href="assets/vanilla-framework.css" />
|
||||
<link rel="stylesheet" href="css/main.css" />
|
||||
<script src="assets/vue.js"></script>
|
||||
<script src="assets/vue-route.js"></script>
|
||||
<script src="assets/vuex.js"></script>
|
||||
<script src="assets/vue-websocket.js"></script>
|
||||
</head>
|
||||
<body role="document">
|
||||
<div id="app">
|
||||
<header id="navigation" class="p-navigation">
|
||||
<div class="p-navigation__banner">
|
||||
<navbar-logo></navbar-logo>
|
||||
<a href="#navigation" class="p-navigation__toggle--open" title="menu">Menu</a>
|
||||
<a href="#navigation-closed" class="p-navigation__toggle--close" title="close menu">Close menu</a>
|
||||
</div>
|
||||
<nav class="p-navigation__nav" role="menubar">
|
||||
<span class="u-off-screen">
|
||||
<a href="#main-content">Jump to main content</a>
|
||||
</span>
|
||||
<ul class="p-navigation__links" role="menu">
|
||||
<router-link :to="{name: 'aps' }" tag="li" class="p-navigation__link" active-class="is-selected">
|
||||
<a>Access Points</a>
|
||||
</router-link>
|
||||
<router-link :to="{name: 'clients' }" tag="li" class="p-navigation__link" active-class="is-selected">
|
||||
<a>Clients</a>
|
||||
</router-link>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
<div class="content">
|
||||
<router-view></router-view>
|
||||
</div>
|
||||
</div>
|
||||
<script src="js/view/accesspoint.js"></script>
|
||||
<script src="js/view/clients.js"></script>
|
||||
<script src="js/store.js"></script>
|
||||
<script src="js/data.js"></script>
|
||||
<script src="js/main.js"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,20 @@
|
|||
store.commit('setEvent', {
|
||||
subject:"wifictld_pkg",
|
||||
callback: (state, msg) => {
|
||||
|
||||
// add to ap list
|
||||
const ip = msg.body.ip;
|
||||
if(state.controller._ap[ip] === undefined){
|
||||
state.controller.ap.push(ip)
|
||||
state.controller._ap[ip] = null;
|
||||
}
|
||||
|
||||
// add clients
|
||||
const client = msg.body.msg.client;
|
||||
if (state.controller.clients[client.addr] === undefined) {
|
||||
state.controller._clients.push(client.addr)
|
||||
}
|
||||
client.ap = ip;
|
||||
state.controller.clients[client.addr] = client;
|
||||
}
|
||||
})
|
|
@ -0,0 +1,35 @@
|
|||
const router = new VueRouter({
|
||||
store,
|
||||
routes: [
|
||||
{ path: '/ap', component: ViewAccessPoints, name: "aps" },
|
||||
{ path: '/ap/clients/:ip', component: ViewClients, name: "ap.clients"},
|
||||
{ path: '/clients', component: ViewClients, name: "clients" },
|
||||
{ path: '/', redirect: '/ap' }
|
||||
]
|
||||
})
|
||||
|
||||
VueNativeSock.default.install(Vue, `//${location.host}${location.pathname}ws`, {
|
||||
store: store,
|
||||
reconnection: true,
|
||||
reconnectionDelay: 5000,
|
||||
format: 'json',
|
||||
})
|
||||
|
||||
const NavbarLogo = {
|
||||
template: `<div class="p-navigation__logo" v-bind:class="{ online: isOnline }">
|
||||
<a class="p-navigation__link" href="/">Wifictld</a>
|
||||
</div>`,
|
||||
computed: {
|
||||
isOnline () {
|
||||
return this.$store.state.socket.isConnected
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const app = new Vue({
|
||||
el: '#app',
|
||||
store,
|
||||
router,
|
||||
components: { NavbarLogo },
|
||||
})
|
|
@ -0,0 +1,104 @@
|
|||
|
||||
function newUUID () {
|
||||
/* eslint-disable */
|
||||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
|
||||
const r = Math.random() * 16 | 0,
|
||||
v = c === 'x' ? r : r & 0x3 | 0x8;
|
||||
return v.toString(16);
|
||||
});
|
||||
/* eslint-enable */
|
||||
}
|
||||
|
||||
const store = new Vuex.Store({
|
||||
state: {
|
||||
socket: {
|
||||
_session: localStorage.getItem('session'),
|
||||
isConnected: false,
|
||||
reconnectError: false,
|
||||
eventMSGID: {},
|
||||
eventTo: {},
|
||||
},
|
||||
controller: {
|
||||
_ap : {},
|
||||
ap: [],
|
||||
_clients: [],
|
||||
clients: {},
|
||||
},
|
||||
},
|
||||
mutations:{
|
||||
SOCKET_ONOPEN (state, event) {
|
||||
state.socket.isConnected = true
|
||||
},
|
||||
SOCKET_ONCLOSE (state, event) {
|
||||
state.socket.isConnected = false
|
||||
},
|
||||
SOCKET_ONERROR (state, event) {
|
||||
console.error(state, event)
|
||||
},
|
||||
// default handler called for all methods
|
||||
SOCKET_ONMESSAGE (state, msg) {
|
||||
if (msg.subject === 'session_init') {
|
||||
if (state.socket._session === null) {
|
||||
state.socket._session = newUUID();
|
||||
localStorage.setItem('session', state.socket._session);
|
||||
}
|
||||
msg.id = state.socket._session;
|
||||
Vue.prototype.$socket.sendObj(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
const msgFunc = state.socket.eventMSGID[msg.id];
|
||||
if (msgFunc) {
|
||||
msgFunc(state, msg);
|
||||
delete state.socket.eventMSGID[msg.id];
|
||||
return;
|
||||
}
|
||||
|
||||
const eventFuncs = state.socket.eventTo[msg.subject];
|
||||
if (typeof eventFuncs === 'object' && eventFuncs.length > 0) {
|
||||
for (const key in eventFuncs) {
|
||||
const func = eventFuncs[key];
|
||||
if (func) {
|
||||
func(state, msg);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
console.log(`unable to identify message: ${msg.subject}`);
|
||||
|
||||
},
|
||||
// mutations for reconnect methods
|
||||
SOCKET_RECONNECT(state, count) {
|
||||
console.info(state, "reconnect:", count)
|
||||
},
|
||||
SOCKET_RECONNECT_ERROR(state) {
|
||||
state.socket.reconnectError = true;
|
||||
},
|
||||
setEvent (state, data) {
|
||||
state.socket.eventTo[data.subject] = [data.callback];
|
||||
},
|
||||
addEvent (state, data) {
|
||||
if (typeof state.socket.eventTo[data.subject] !== 'object') {
|
||||
state.socket.eventTo[data.subject] = [];
|
||||
}
|
||||
state.socket.eventTo[data.subject].push(data.callback);
|
||||
},
|
||||
delEvent (state, data) {
|
||||
if (typeof state.socket.eventTo[data.subject] === 'object' && state.socket.eventTo[data.subject].length > 1) {
|
||||
state.socket.eventTo[data.subject].pop(data.callback);
|
||||
} else {
|
||||
state.socket.eventTo[data.subject] = [];
|
||||
}
|
||||
},
|
||||
call (state, data) {
|
||||
if (!data.msg.id) {
|
||||
data.msg.id = newUUID();
|
||||
}
|
||||
const ret = Vue.prototype.$socket.sendObj(data.msg);
|
||||
if (typeof data.callback === 'function') {
|
||||
state.socket.eventMSGID[data.msg.id] = data.callback;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
})
|
|
@ -0,0 +1,37 @@
|
|||
const ViewAccessPoints = {
|
||||
template: `<div class="row">
|
||||
<p><span class="p-heading--one">AccessPoints</span></p>
|
||||
<table class="p-table--mobile-card" role="grid">
|
||||
<thead>
|
||||
<tr role="row">
|
||||
<th scope="col" role="columnheader" aria-sort="none">IP</th>
|
||||
<th scope="col" role="columnheader" aria-sort="none" class="u-align--right">Users</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr role="row" v-for="item in getAPs">
|
||||
<td role="rowheader" aria-label="IP">{{ item.ip.substring(21) }}</td>
|
||||
<td role="gridcell" aria-label="Users" class="u-align--right">
|
||||
<router-link :to="{ name: 'ap.clients', params: { ip: item.ip }}">
|
||||
{{ item.clients }}
|
||||
</router-link>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>`,
|
||||
computed: {
|
||||
getAPs () {
|
||||
const state = this.$store.state;
|
||||
return state.controller.ap.map(function (ip) {
|
||||
return {
|
||||
ip: ip,
|
||||
clients: state.controller._clients.filter(function (hwaddr) {
|
||||
const client = state.controller.clients[hwaddr];
|
||||
return client.ap === ip;
|
||||
}).length,
|
||||
};
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
const ViewClients = {
|
||||
template: `<div class="row">
|
||||
<p>
|
||||
<span class="p-heading--one">Clients</span>
|
||||
<sub v-if="$route.params.ip">of Router {{ $route.params.ip.substring(21) }}</sub>
|
||||
</p>
|
||||
<table class="p-table--mobile-card" role="grid">
|
||||
<thead>
|
||||
<tr role="row">
|
||||
<th scope="col" role="columnheader" aria-sort="none">Addr</th>
|
||||
<th scope="col" role="columnheader" aria-sort="none">AP</th>
|
||||
<th scope="col" role="columnheader" aria-sort="none" class="u-align--right">FreqHighs</th>
|
||||
<th scope="col" role="columnheader" aria-sort="none" class="u-align--right">SignalLow</th>
|
||||
<th scope="col" role="columnheader" aria-sort="none" class="u-align--right">SignalHigh</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr role="row" v-for="item in getClients">
|
||||
<td role="rowheader" aria-label="Addr">{{ item.addr.substring(12) }}</td>
|
||||
<td role="gridcell" aria-label="AP">
|
||||
<router-link :to="{ name: 'ap.clients', params: { ip: item.ap }}">
|
||||
{{ item.ap.substring(21) }}
|
||||
</router-link>
|
||||
</td>
|
||||
<td role="gridcell" aria-label="Freq Highs" class="u-align--right">{{ item.freq_highest }}</td>
|
||||
<td role="gridcell" aria-label="Signal LowF" class="u-align--right">{{ item.signal_low_freq }}</td>
|
||||
<td role="gridcell" aria-label="Signal HighF" class="u-align--right">{{ item.signal_high_freq }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>`,
|
||||
computed: {
|
||||
getClients () {
|
||||
const state = this.$store.state,
|
||||
apIP = this.$route.params.ip;
|
||||
|
||||
return state.controller._clients.map(function(addr) {
|
||||
return state.controller.clients[addr];
|
||||
}).filter((client)=> (apIP === undefined || client.ap === apIP));
|
||||
},
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue