diff --git a/bin/lamassu-admin-server b/bin/lamassu-admin-server index 503bb87a..efa1cceb 100755 --- a/bin/lamassu-admin-server +++ b/bin/lamassu-admin-server @@ -1,5 +1,7 @@ #!/usr/bin/env node +const EventEmitter = require('events') +const qs = require('querystring') const os = require('os') const fs = require('fs') const path = require('path') @@ -13,6 +15,9 @@ const argv = require('minimist')(process.argv.slice(2)) const got = require('got') const morgan = require('morgan') const helmet = require('helmet') +const WebSocket = require('ws') +const http = require('http') +const SocketIo = require('socket.io') const machineLoader = require('../lib/machine-loader') const T = require('../lib/time') @@ -26,6 +31,7 @@ const server = require('../lib/admin/server') const transactions = require('../lib/admin/transactions') const NEVER = new Date(Date.now() + 100 * T.years) +const REAUTHENTICATE_INTERVAL = T.minute const devMode = argv.dev @@ -200,12 +206,58 @@ process.on('unhandledRejection', err => { process.exit(1) }) +const socketServer = http.createServer() +const io = SocketIo(socketServer) +socketServer.listen(3060) +const socketEmitter = new EventEmitter() + +io.on('connection', client => { + client.on('message', msg => socketEmitter.emit('message', msg)) +}) + +const webServer = https.createServer(options, app) +const wss = new WebSocket.Server({server: webServer}) + +function establishSocket (ws, token) { + return login.authenticate(token) + .then(success => { + if (!success) return ws.close(1008, 'Authentication error') + + const listener = data => { + console.log('DEBUG200: %j', data) + ws.send(JSON.stringify(data)) + } + + // Reauthenticate every once in a while, in case token expired + setInterval(() => { + return login.authenticate(token) + .then(success => { + if (!success) { + socketEmitter.removeListener('message', listener) + ws.close() + } + }) + }, REAUTHENTICATE_INTERVAL) + + socketEmitter.on('message', listener) + + console.log('DEBUG120: %j', token) + ws.send('Testing123') + }) +} + +wss.on('connection', ws => { + const token = qs.parse(ws.upgradeReq.headers.cookie).token + + return establishSocket(ws, token) +}) + if (devMode) { - https.createServer(options, app).listen(8070, () => { + webServer.listen(8070, () => { console.log('lamassu-admin-server listening on port 8070') }) } else { - https.createServer(options, app).listen(443, () => { + webServer.listen(443, () => { console.log('lamassu-admin-server listening on port 443') }) } diff --git a/lib/cash-out-tx.js b/lib/cash-out-tx.js index 24593b29..904e9d3b 100644 --- a/lib/cash-out-tx.js +++ b/lib/cash-out-tx.js @@ -7,6 +7,7 @@ const T = require('./time') const logger = require('./logger') const plugins = require('./plugins') const helper = require('./cash-out-helper') +const socket = require('./socket-client') module.exports = { post, @@ -171,7 +172,8 @@ function updateCassettes (tx) { const sql = `update devices set cassette1 = cassette1 - $1, cassette2 = cassette2 - $2 - where device_id = $3` + where device_id = $3 + returning cassette1, cassette2` const values = [ tx.bills[0].dispensed + tx.bills[0].rejected, @@ -179,7 +181,8 @@ function updateCassettes (tx) { tx.deviceId ] - return db.none(sql, values) + return db.one(sql, values) + .then(r => socket.emit(_.assign(r, {op: 'cassetteUpdate', deviceId: tx.deviceId}))) } function wasJustAuthorized (oldTx, newTx, isZeroConf) { diff --git a/lib/socket-client.js b/lib/socket-client.js new file mode 100644 index 00000000..dd748647 --- /dev/null +++ b/lib/socket-client.js @@ -0,0 +1,9 @@ +const io = require('socket.io-client') + +const socket = io('http://localhost:3060') + +module.exports = {emit} + +function emit (msg) { + socket.emit('message', msg) +} diff --git a/package.json b/package.json index 19aee600..404154fb 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "express": "^4.13.4", "express-limiter": "^1.6.0", "express-rate-limit": "^2.6.0", + "express-ws": "^3.0.0", "got": "^6.6.3", "helmet": "^3.1.0", "lodash": "^4.17.2", @@ -39,12 +40,13 @@ "promise-sequential": "^1.1.1", "ramda": "^0.22.1", "serve-static": "^1.11.1", - "socket.io": "^1.7.1", + "socket.io": "^1.7.4", "socket.io-client": "^1.7.1", "twilio": "^2.11.1", "uuid": "^3.0.0", "web3": "^0.18.4", - "winston": "^2.3.0" + "winston": "^2.3.0", + "ws": "^2.3.1" }, "repository": { "type": "git", diff --git a/public/elm.js b/public/elm.js index 6b9a67ec..53accea4 100644 --- a/public/elm.js +++ b/public/elm.js @@ -10683,6 +10683,541 @@ var _elm_lang$svg$Svg_Attributes$accumulate = _elm_lang$virtual_dom$VirtualDom$a var _elm_lang$svg$Svg_Attributes$accelerate = _elm_lang$virtual_dom$VirtualDom$attribute('accelerate'); var _elm_lang$svg$Svg_Attributes$accentHeight = _elm_lang$virtual_dom$VirtualDom$attribute('accent-height'); +var _elm_lang$websocket$Native_WebSocket = function() { + +function open(url, settings) +{ + return _elm_lang$core$Native_Scheduler.nativeBinding(function(callback) + { + try + { + var socket = new WebSocket(url); + socket.elm_web_socket = true; + } + catch(err) + { + return callback(_elm_lang$core$Native_Scheduler.fail({ + ctor: err.name === 'SecurityError' ? 'BadSecurity' : 'BadArgs', + _0: err.message + })); + } + + socket.addEventListener("open", function(event) { + callback(_elm_lang$core$Native_Scheduler.succeed(socket)); + }); + + socket.addEventListener("message", function(event) { + _elm_lang$core$Native_Scheduler.rawSpawn(A2(settings.onMessage, socket, event.data)); + }); + + socket.addEventListener("close", function(event) { + _elm_lang$core$Native_Scheduler.rawSpawn(settings.onClose({ + code: event.code, + reason: event.reason, + wasClean: event.wasClean + })); + }); + + return function() + { + if (socket && socket.close) + { + socket.close(); + } + }; + }); +} + +function send(socket, string) +{ + return _elm_lang$core$Native_Scheduler.nativeBinding(function(callback) + { + var result = + socket.readyState === WebSocket.OPEN + ? _elm_lang$core$Maybe$Nothing + : _elm_lang$core$Maybe$Just({ ctor: 'NotOpen' }); + + try + { + socket.send(string); + } + catch(err) + { + result = _elm_lang$core$Maybe$Just({ ctor: 'BadString' }); + } + + callback(_elm_lang$core$Native_Scheduler.succeed(result)); + }); +} + +function close(code, reason, socket) +{ + return _elm_lang$core$Native_Scheduler.nativeBinding(function(callback) { + try + { + socket.close(code, reason); + } + catch(err) + { + return callback(_elm_lang$core$Native_Scheduler.fail(_elm_lang$core$Maybe$Just({ + ctor: err.name === 'SyntaxError' ? 'BadReason' : 'BadCode' + }))); + } + callback(_elm_lang$core$Native_Scheduler.succeed(_elm_lang$core$Maybe$Nothing)); + }); +} + +function bytesQueued(socket) +{ + return _elm_lang$core$Native_Scheduler.nativeBinding(function(callback) { + callback(_elm_lang$core$Native_Scheduler.succeed(socket.bufferedAmount)); + }); +} + +return { + open: F2(open), + send: F2(send), + close: F3(close), + bytesQueued: bytesQueued +}; + +}(); + +var _elm_lang$websocket$WebSocket_LowLevel$bytesQueued = _elm_lang$websocket$Native_WebSocket.bytesQueued; +var _elm_lang$websocket$WebSocket_LowLevel$send = _elm_lang$websocket$Native_WebSocket.send; +var _elm_lang$websocket$WebSocket_LowLevel$closeWith = _elm_lang$websocket$Native_WebSocket.close; +var _elm_lang$websocket$WebSocket_LowLevel$close = function (socket) { + return A2( + _elm_lang$core$Task$map, + _elm_lang$core$Basics$always( + {ctor: '_Tuple0'}), + A3(_elm_lang$websocket$WebSocket_LowLevel$closeWith, 1000, '', socket)); +}; +var _elm_lang$websocket$WebSocket_LowLevel$open = _elm_lang$websocket$Native_WebSocket.open; +var _elm_lang$websocket$WebSocket_LowLevel$Settings = F2( + function (a, b) { + return {onMessage: a, onClose: b}; + }); +var _elm_lang$websocket$WebSocket_LowLevel$WebSocket = {ctor: 'WebSocket'}; +var _elm_lang$websocket$WebSocket_LowLevel$BadArgs = {ctor: 'BadArgs'}; +var _elm_lang$websocket$WebSocket_LowLevel$BadSecurity = {ctor: 'BadSecurity'}; +var _elm_lang$websocket$WebSocket_LowLevel$BadReason = {ctor: 'BadReason'}; +var _elm_lang$websocket$WebSocket_LowLevel$BadCode = {ctor: 'BadCode'}; +var _elm_lang$websocket$WebSocket_LowLevel$BadString = {ctor: 'BadString'}; +var _elm_lang$websocket$WebSocket_LowLevel$NotOpen = {ctor: 'NotOpen'}; + +var _elm_lang$websocket$WebSocket$closeConnection = function (connection) { + var _p0 = connection; + if (_p0.ctor === 'Opening') { + return _elm_lang$core$Process$kill(_p0._1); + } else { + return _elm_lang$websocket$WebSocket_LowLevel$close(_p0._0); + } +}; +var _elm_lang$websocket$WebSocket$after = function (backoff) { + return (_elm_lang$core$Native_Utils.cmp(backoff, 1) < 0) ? _elm_lang$core$Task$succeed( + {ctor: '_Tuple0'}) : _elm_lang$core$Process$sleep( + _elm_lang$core$Basics$toFloat( + 10 * Math.pow(2, backoff))); +}; +var _elm_lang$websocket$WebSocket$removeQueue = F2( + function (name, state) { + return _elm_lang$core$Native_Utils.update( + state, + { + queues: A2(_elm_lang$core$Dict$remove, name, state.queues) + }); + }); +var _elm_lang$websocket$WebSocket$updateSocket = F3( + function (name, connection, state) { + return _elm_lang$core$Native_Utils.update( + state, + { + sockets: A3(_elm_lang$core$Dict$insert, name, connection, state.sockets) + }); + }); +var _elm_lang$websocket$WebSocket$add = F2( + function (value, maybeList) { + var _p1 = maybeList; + if (_p1.ctor === 'Nothing') { + return _elm_lang$core$Maybe$Just( + { + ctor: '::', + _0: value, + _1: {ctor: '[]'} + }); + } else { + return _elm_lang$core$Maybe$Just( + {ctor: '::', _0: value, _1: _p1._0}); + } + }); +var _elm_lang$websocket$WebSocket$buildSubDict = F2( + function (subs, dict) { + buildSubDict: + while (true) { + var _p2 = subs; + if (_p2.ctor === '[]') { + return dict; + } else { + if (_p2._0.ctor === 'Listen') { + var _v3 = _p2._1, + _v4 = A3( + _elm_lang$core$Dict$update, + _p2._0._0, + _elm_lang$websocket$WebSocket$add(_p2._0._1), + dict); + subs = _v3; + dict = _v4; + continue buildSubDict; + } else { + var _v5 = _p2._1, + _v6 = A3( + _elm_lang$core$Dict$update, + _p2._0._0, + function (_p3) { + return _elm_lang$core$Maybe$Just( + A2( + _elm_lang$core$Maybe$withDefault, + {ctor: '[]'}, + _p3)); + }, + dict); + subs = _v5; + dict = _v6; + continue buildSubDict; + } + } + } + }); +var _elm_lang$websocket$WebSocket_ops = _elm_lang$websocket$WebSocket_ops || {}; +_elm_lang$websocket$WebSocket_ops['&>'] = F2( + function (t1, t2) { + return A2( + _elm_lang$core$Task$andThen, + function (_p4) { + return t2; + }, + t1); + }); +var _elm_lang$websocket$WebSocket$sendMessagesHelp = F3( + function (cmds, socketsDict, queuesDict) { + sendMessagesHelp: + while (true) { + var _p5 = cmds; + if (_p5.ctor === '[]') { + return _elm_lang$core$Task$succeed(queuesDict); + } else { + var _p9 = _p5._1; + var _p8 = _p5._0._0; + var _p7 = _p5._0._1; + var _p6 = A2(_elm_lang$core$Dict$get, _p8, socketsDict); + if ((_p6.ctor === 'Just') && (_p6._0.ctor === 'Connected')) { + return A2( + _elm_lang$websocket$WebSocket_ops['&>'], + A2(_elm_lang$websocket$WebSocket_LowLevel$send, _p6._0._0, _p7), + A3(_elm_lang$websocket$WebSocket$sendMessagesHelp, _p9, socketsDict, queuesDict)); + } else { + var _v9 = _p9, + _v10 = socketsDict, + _v11 = A3( + _elm_lang$core$Dict$update, + _p8, + _elm_lang$websocket$WebSocket$add(_p7), + queuesDict); + cmds = _v9; + socketsDict = _v10; + queuesDict = _v11; + continue sendMessagesHelp; + } + } + } + }); +var _elm_lang$websocket$WebSocket$subscription = _elm_lang$core$Native_Platform.leaf('WebSocket'); +var _elm_lang$websocket$WebSocket$command = _elm_lang$core$Native_Platform.leaf('WebSocket'); +var _elm_lang$websocket$WebSocket$State = F3( + function (a, b, c) { + return {sockets: a, queues: b, subs: c}; + }); +var _elm_lang$websocket$WebSocket$init = _elm_lang$core$Task$succeed( + A3(_elm_lang$websocket$WebSocket$State, _elm_lang$core$Dict$empty, _elm_lang$core$Dict$empty, _elm_lang$core$Dict$empty)); +var _elm_lang$websocket$WebSocket$Send = F2( + function (a, b) { + return {ctor: 'Send', _0: a, _1: b}; + }); +var _elm_lang$websocket$WebSocket$send = F2( + function (url, message) { + return _elm_lang$websocket$WebSocket$command( + A2(_elm_lang$websocket$WebSocket$Send, url, message)); + }); +var _elm_lang$websocket$WebSocket$cmdMap = F2( + function (_p11, _p10) { + var _p12 = _p10; + return A2(_elm_lang$websocket$WebSocket$Send, _p12._0, _p12._1); + }); +var _elm_lang$websocket$WebSocket$KeepAlive = function (a) { + return {ctor: 'KeepAlive', _0: a}; +}; +var _elm_lang$websocket$WebSocket$keepAlive = function (url) { + return _elm_lang$websocket$WebSocket$subscription( + _elm_lang$websocket$WebSocket$KeepAlive(url)); +}; +var _elm_lang$websocket$WebSocket$Listen = F2( + function (a, b) { + return {ctor: 'Listen', _0: a, _1: b}; + }); +var _elm_lang$websocket$WebSocket$listen = F2( + function (url, tagger) { + return _elm_lang$websocket$WebSocket$subscription( + A2(_elm_lang$websocket$WebSocket$Listen, url, tagger)); + }); +var _elm_lang$websocket$WebSocket$subMap = F2( + function (func, sub) { + var _p13 = sub; + if (_p13.ctor === 'Listen') { + return A2( + _elm_lang$websocket$WebSocket$Listen, + _p13._0, + function (_p14) { + return func( + _p13._1(_p14)); + }); + } else { + return _elm_lang$websocket$WebSocket$KeepAlive(_p13._0); + } + }); +var _elm_lang$websocket$WebSocket$Connected = function (a) { + return {ctor: 'Connected', _0: a}; +}; +var _elm_lang$websocket$WebSocket$Opening = F2( + function (a, b) { + return {ctor: 'Opening', _0: a, _1: b}; + }); +var _elm_lang$websocket$WebSocket$BadOpen = function (a) { + return {ctor: 'BadOpen', _0: a}; +}; +var _elm_lang$websocket$WebSocket$GoodOpen = F2( + function (a, b) { + return {ctor: 'GoodOpen', _0: a, _1: b}; + }); +var _elm_lang$websocket$WebSocket$Die = function (a) { + return {ctor: 'Die', _0: a}; +}; +var _elm_lang$websocket$WebSocket$Receive = F2( + function (a, b) { + return {ctor: 'Receive', _0: a, _1: b}; + }); +var _elm_lang$websocket$WebSocket$open = F2( + function (name, router) { + return A2( + _elm_lang$websocket$WebSocket_LowLevel$open, + name, + { + onMessage: F2( + function (_p15, msg) { + return A2( + _elm_lang$core$Platform$sendToSelf, + router, + A2(_elm_lang$websocket$WebSocket$Receive, name, msg)); + }), + onClose: function (details) { + return A2( + _elm_lang$core$Platform$sendToSelf, + router, + _elm_lang$websocket$WebSocket$Die(name)); + } + }); + }); +var _elm_lang$websocket$WebSocket$attemptOpen = F3( + function (router, backoff, name) { + var badOpen = function (_p16) { + return A2( + _elm_lang$core$Platform$sendToSelf, + router, + _elm_lang$websocket$WebSocket$BadOpen(name)); + }; + var goodOpen = function (ws) { + return A2( + _elm_lang$core$Platform$sendToSelf, + router, + A2(_elm_lang$websocket$WebSocket$GoodOpen, name, ws)); + }; + var actuallyAttemptOpen = A2( + _elm_lang$core$Task$onError, + badOpen, + A2( + _elm_lang$core$Task$andThen, + goodOpen, + A2(_elm_lang$websocket$WebSocket$open, name, router))); + return _elm_lang$core$Process$spawn( + A2( + _elm_lang$websocket$WebSocket_ops['&>'], + _elm_lang$websocket$WebSocket$after(backoff), + actuallyAttemptOpen)); + }); +var _elm_lang$websocket$WebSocket$onEffects = F4( + function (router, cmds, subs, state) { + var newSubs = A2(_elm_lang$websocket$WebSocket$buildSubDict, subs, _elm_lang$core$Dict$empty); + var cleanup = function (newQueues) { + var rightStep = F3( + function (name, connection, getNewSockets) { + return A2( + _elm_lang$websocket$WebSocket_ops['&>'], + _elm_lang$websocket$WebSocket$closeConnection(connection), + getNewSockets); + }); + var bothStep = F4( + function (name, _p17, connection, getNewSockets) { + return A2( + _elm_lang$core$Task$map, + A2(_elm_lang$core$Dict$insert, name, connection), + getNewSockets); + }); + var leftStep = F3( + function (name, _p18, getNewSockets) { + return A2( + _elm_lang$core$Task$andThen, + function (newSockets) { + return A2( + _elm_lang$core$Task$andThen, + function (pid) { + return _elm_lang$core$Task$succeed( + A3( + _elm_lang$core$Dict$insert, + name, + A2(_elm_lang$websocket$WebSocket$Opening, 0, pid), + newSockets)); + }, + A3(_elm_lang$websocket$WebSocket$attemptOpen, router, 0, name)); + }, + getNewSockets); + }); + var newEntries = A2( + _elm_lang$core$Dict$union, + newQueues, + A2( + _elm_lang$core$Dict$map, + F2( + function (k, v) { + return {ctor: '[]'}; + }), + newSubs)); + var collectNewSockets = A6( + _elm_lang$core$Dict$merge, + leftStep, + bothStep, + rightStep, + newEntries, + state.sockets, + _elm_lang$core$Task$succeed(_elm_lang$core$Dict$empty)); + return A2( + _elm_lang$core$Task$andThen, + function (newSockets) { + return _elm_lang$core$Task$succeed( + A3(_elm_lang$websocket$WebSocket$State, newSockets, newQueues, newSubs)); + }, + collectNewSockets); + }; + var sendMessagesGetNewQueues = A3(_elm_lang$websocket$WebSocket$sendMessagesHelp, cmds, state.sockets, state.queues); + return A2(_elm_lang$core$Task$andThen, cleanup, sendMessagesGetNewQueues); + }); +var _elm_lang$websocket$WebSocket$onSelfMsg = F3( + function (router, selfMsg, state) { + var _p19 = selfMsg; + switch (_p19.ctor) { + case 'Receive': + var sends = A2( + _elm_lang$core$List$map, + function (tagger) { + return A2( + _elm_lang$core$Platform$sendToApp, + router, + tagger(_p19._1)); + }, + A2( + _elm_lang$core$Maybe$withDefault, + {ctor: '[]'}, + A2(_elm_lang$core$Dict$get, _p19._0, state.subs))); + return A2( + _elm_lang$websocket$WebSocket_ops['&>'], + _elm_lang$core$Task$sequence(sends), + _elm_lang$core$Task$succeed(state)); + case 'Die': + var _p21 = _p19._0; + var _p20 = A2(_elm_lang$core$Dict$get, _p21, state.sockets); + if (_p20.ctor === 'Nothing') { + return _elm_lang$core$Task$succeed(state); + } else { + return A2( + _elm_lang$core$Task$andThen, + function (pid) { + return _elm_lang$core$Task$succeed( + A3( + _elm_lang$websocket$WebSocket$updateSocket, + _p21, + A2(_elm_lang$websocket$WebSocket$Opening, 0, pid), + state)); + }, + A3(_elm_lang$websocket$WebSocket$attemptOpen, router, 0, _p21)); + } + case 'GoodOpen': + var _p24 = _p19._1; + var _p23 = _p19._0; + var _p22 = A2(_elm_lang$core$Dict$get, _p23, state.queues); + if (_p22.ctor === 'Nothing') { + return _elm_lang$core$Task$succeed( + A3( + _elm_lang$websocket$WebSocket$updateSocket, + _p23, + _elm_lang$websocket$WebSocket$Connected(_p24), + state)); + } else { + return A3( + _elm_lang$core$List$foldl, + F2( + function (msg, task) { + return A2( + _elm_lang$websocket$WebSocket_ops['&>'], + A2(_elm_lang$websocket$WebSocket_LowLevel$send, _p24, msg), + task); + }), + _elm_lang$core$Task$succeed( + A2( + _elm_lang$websocket$WebSocket$removeQueue, + _p23, + A3( + _elm_lang$websocket$WebSocket$updateSocket, + _p23, + _elm_lang$websocket$WebSocket$Connected(_p24), + state))), + _p22._0); + } + default: + var _p27 = _p19._0; + var _p25 = A2(_elm_lang$core$Dict$get, _p27, state.sockets); + if (_p25.ctor === 'Nothing') { + return _elm_lang$core$Task$succeed(state); + } else { + if (_p25._0.ctor === 'Opening') { + var _p26 = _p25._0._0; + return A2( + _elm_lang$core$Task$andThen, + function (pid) { + return _elm_lang$core$Task$succeed( + A3( + _elm_lang$websocket$WebSocket$updateSocket, + _p27, + A2(_elm_lang$websocket$WebSocket$Opening, _p26 + 1, pid), + state)); + }, + A3(_elm_lang$websocket$WebSocket$attemptOpen, router, _p26 + 1, _p27)); + } else { + return _elm_lang$core$Task$succeed(state); + } + } + } + }); +_elm_lang$core$Native_Platform.effectManagers['WebSocket'] = {pkg: 'elm-lang/websocket', init: _elm_lang$websocket$WebSocket$init, onEffects: _elm_lang$websocket$WebSocket$onEffects, onSelfMsg: _elm_lang$websocket$WebSocket$onSelfMsg, tag: 'fx', cmdMap: _elm_lang$websocket$WebSocket$cmdMap, subMap: _elm_lang$websocket$WebSocket$subMap}; + var _evancz$elm_markdown$Native_Markdown = function() { @@ -33008,6 +33543,9 @@ var _user$project$CoreTypes$PairRoute = {ctor: 'PairRoute'}; var _user$project$CoreTypes$AccountRoute = function (a) { return {ctor: 'AccountRoute', _0: a}; }; +var _user$project$CoreTypes$WebSocketMsg = function (a) { + return {ctor: 'WebSocketMsg', _0: a}; +}; var _user$project$CoreTypes$Interval = {ctor: 'Interval'}; var _user$project$CoreTypes$UrlChange = function (a) { return {ctor: 'UrlChange', _0: a}; @@ -34570,11 +35108,20 @@ var _user$project$Maintenance_View$view = function (model) { }; var _user$project$Main$subscriptions = function (model) { - return A2( - _elm_lang$core$Time$every, - 10 * _elm_lang$core$Time$second, - function (_p0) { - return _user$project$CoreTypes$Interval; + return _elm_lang$core$Platform_Sub$batch( + { + ctor: '::', + _0: A2( + _elm_lang$core$Time$every, + 5 * _elm_lang$core$Time$second, + function (_p0) { + return _user$project$CoreTypes$Interval; + }), + _1: { + ctor: '::', + _0: A2(_elm_lang$websocket$WebSocket$listen, 'wss://localhost:8070', _user$project$CoreTypes$WebSocketMsg), + _1: {ctor: '[]'} + } }); }; var _user$project$Main$statusBar = function (maybeStatus) { @@ -35119,7 +35666,7 @@ var _user$project$Main$update = F2( }); case 'UrlChange': return A2(_user$project$Main$urlUpdate, _p12._0, model); - default: + case 'Interval': var route = A2( _elm_lang$core$Maybe$withDefault, _user$project$CoreTypes$NotFoundRoute, @@ -35140,6 +35687,12 @@ var _user$project$Main$update = F2( _1: {ctor: '[]'} }, extraCmds)); + default: + var _p20 = A2(_elm_lang$core$Debug$log, 'DEBUG100', _p12._0); + return A2( + _elm_lang$core$Platform_Cmd_ops['!'], + model, + {ctor: '[]'}); } }); var _user$project$Main$main = A2(