summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--server/api/clients.js2
-rw-r--r--server/api/users.js9
-rwxr-xr-xserver/bin/www7
-rw-r--r--server/lib/authentication.js8
-rw-r--r--server/lib/socketio.js41
-rw-r--r--server/package-lock.json290
-rw-r--r--server/package.json1
-rw-r--r--webapp/config/index.js6
-rw-r--r--webapp/package-lock.json227
-rw-r--r--webapp/package.json1
-rw-r--r--webapp/src/components/AccountModule.vue9
-rw-r--r--webapp/src/components/DashboardPage.vue6
-rw-r--r--webapp/src/components/GroupModuleDialog.vue34
-rw-r--r--webapp/src/components/LoginPage.vue2
-rw-r--r--webapp/src/components/NotificationsAlerts.vue37
-rw-r--r--webapp/src/components/NotificationsSnackbars.vue2
-rw-r--r--webapp/src/main.js25
-rw-r--r--webapp/src/store/groups.js18
-rw-r--r--webapp/src/store/notifications.js7
19 files changed, 688 insertions, 44 deletions
diff --git a/server/api/clients.js b/server/api/clients.js
index a436213..54832eb 100644
--- a/server/api/clients.js
+++ b/server/api/clients.js
@@ -1,6 +1,7 @@
/* global __appdir */
var path = require('path')
var db = require(path.join(__appdir, 'lib', 'sequelize'))
+var io = require(path.join(__appdir, 'lib', 'socketio'))
const backendHelper = require(path.join(__appdir, 'lib', 'external-backends', 'backendhelper'))
// GET Requests
@@ -35,6 +36,7 @@ module.exports.post = {
})
} else {
db.client.create(req.body.info).then(client => {
+ io.in('broadcast newClient').emit('notifications newAlert', { type: 'info', text: 'New client!' })
if (req.body.groupIds) client.setGroups(req.body.groupIds).then(() => { res.send({ id: client.id }) })
})
}
diff --git a/server/api/users.js b/server/api/users.js
index 98c72f9..35da1db 100644
--- a/server/api/users.js
+++ b/server/api/users.js
@@ -7,13 +7,10 @@ var jwt = require('jsonwebtoken')
module.exports.get = {
getUserInfo: function (req, res) {
- // Because veryfyToken was succesfully excecuted the request has the attribute token.
- const token = req.token
- // Decode the token.
- var decoded = jwt.decode(token, { complete: true })
- var userid = decoded.payload.user.id
+ var decodedToken = jwt.decode(req.token, { complete: true })
+ var userId = decodedToken.payload.user.id
- db.user.findOne({ where: { id: userid } }).then(userDb => {
+ db.user.findOne({ where: { id: userId } }).then(userDb => {
var user = { }
user.id = userDb.id
user.username = userDb.username
diff --git a/server/bin/www b/server/bin/www
index 3618506..3cfebbd 100755
--- a/server/bin/www
+++ b/server/bin/www
@@ -32,6 +32,13 @@ var options = {
var server = https.createServer(options, app)
/**
+ * Setup socket.io.
+ */
+
+var io = require(path.join(__appdir, 'lib', 'socketio'))
+io.attach(server)
+
+/**
* Listen on provided port, on all network interfaces.
*/
diff --git a/server/lib/authentication.js b/server/lib/authentication.js
index 5623b1e..0681011 100644
--- a/server/lib/authentication.js
+++ b/server/lib/authentication.js
@@ -92,11 +92,15 @@ module.exports = {
} else if (req.cookies.jwt_hp && req.cookies.jwt_s) {
token = req.cookies.jwt_hp + '.' + req.cookies.jwt_s
} else {
- return res.status(403).send({ auth: false, status: 'TOKEN_MISSING', error_message: 'This service requires a token.' })
+ if (res) return res.status(403).send({ auth: false, status: 'TOKEN_MISSING', error_message: 'This service requires a token.' })
+ else return next(new Error('TOKEN_MISSING'))
}
// Verify the token with the secret.
jwt.verify(token, config.secret, function (err) {
- if (err) return res.status(500).send({ auth: false, status: 'TOKEN_INVALID', error_message: 'The provided token is invalid.' })
+ if (err) {
+ if (res) return res.status(500).send({ auth: false, status: 'TOKEN_INVALID', error_message: 'The provided token is invalid.' })
+ else return next(new Error('TOKEN_INVALID'))
+ }
req.token = token
next()
})
diff --git a/server/lib/socketio.js b/server/lib/socketio.js
new file mode 100644
index 0000000..37d20cd
--- /dev/null
+++ b/server/lib/socketio.js
@@ -0,0 +1,41 @@
+var path = require('path')
+var cookie = require('cookie')
+var jwt = require('jsonwebtoken')
+var io = require('socket.io')({ serveClient: false });
+
+// ############################################################################
+// ####################### websocket middleware #############################
+
+/* global __appdir */
+var auth = require(path.join(__appdir, 'lib', 'authentication'))
+io.use(function (socket, next){
+ socket.request.cookies = cookie.parse(socket.request.headers.cookie || '')
+ auth.verifyToken(socket.request, null, next);
+})
+
+// ############################################################################
+// ####################### websocket listeners ##############################
+
+io.on('connection', socket => {
+ console.log('Socket.io: A user connected.')
+ socket.on('disconnect', () => console.log('Socket.io: A user disconnected.'))
+
+ var decodedToken = jwt.decode(socket.request.token, { complete: true })
+ var userId = decodedToken.payload.user.id
+
+ socket.join(userId)
+
+ // Join broadcast rooms (TODO: check if user has permission to join those rooms)
+ socket.join('broadcast newClient')
+
+ // Notification broadcasts
+ socket.on('notifications clearAll', () => socket.to(userId).emit('notifications clearAll'))
+ socket.on('notifications newAlert', data => socket.to(userId).emit('notifications newAlert', data))
+ socket.on('notifications closeAlert', id => socket.to(userId).emit('notifications closeAlert', id))
+ socket.on('notifications resetNewAlertCount', () => socket.to(userId).emit('notifications resetNewAlertCount'))
+})
+
+// ############################################################################
+// ############################################################################
+
+module.exports = io \ No newline at end of file
diff --git a/server/package-lock.json b/server/package-lock.json
index a2024ac..0e58316 100644
--- a/server/package-lock.json
+++ b/server/package-lock.json
@@ -83,6 +83,11 @@
"acorn": "^5.0.3"
}
},
+ "after": {
+ "version": "0.8.2",
+ "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz",
+ "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8="
+ },
"ajv": {
"version": "5.5.2",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz",
@@ -158,6 +163,11 @@
"resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz",
"integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY="
},
+ "arraybuffer.slice": {
+ "version": "0.0.7",
+ "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz",
+ "integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog=="
+ },
"arrify": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz",
@@ -176,6 +186,11 @@
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
"integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU="
},
+ "async-limiter": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz",
+ "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg=="
+ },
"asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
@@ -217,7 +232,7 @@
},
"chalk": {
"version": "1.1.3",
- "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
"requires": {
"ansi-styles": "^2.2.1",
@@ -243,11 +258,26 @@
"regenerator-runtime": "^0.11.0"
}
},
+ "backo2": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz",
+ "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc="
+ },
"balanced-match": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
},
+ "base64-arraybuffer": {
+ "version": "0.1.5",
+ "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz",
+ "integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg="
+ },
+ "base64id": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/base64id/-/base64id-1.0.0.tgz",
+ "integrity": "sha1-R2iMuZu2gE8OBtPnY7HDLlfY5rY="
+ },
"basic-auth": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.0.tgz",
@@ -265,11 +295,24 @@
"tweetnacl": "^0.14.3"
}
},
+ "better-assert": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz",
+ "integrity": "sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI=",
+ "requires": {
+ "callsite": "1.0.0"
+ }
+ },
"bignumber.js": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-4.1.0.tgz",
"integrity": "sha512-eJzYkFYy9L4JzXsbymsFn3p54D+llV27oTQ+ziJG7WFRheJcNZilgVXMG0LoZtlQSKBsJdWtLFqOD0u+U0jZKA=="
},
+ "blob": {
+ "version": "0.0.5",
+ "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz",
+ "integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig=="
+ },
"bluebird": {
"version": "3.5.1",
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz",
@@ -350,6 +393,11 @@
"callsites": "^0.2.0"
}
},
+ "callsite": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz",
+ "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA="
+ },
"callsites": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz",
@@ -488,6 +536,21 @@
"resolved": "https://registry.npmjs.org/commander/-/commander-2.18.0.tgz",
"integrity": "sha512-6CYPa+JP2ftfRU2qkDK+UTVeQYosOg/2GbcjIcKPHfinyOLPVGXu/ovN86RP49Re5ndJK1N0kuiidFFuepc4ZQ=="
},
+ "component-bind": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz",
+ "integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E="
+ },
+ "component-emitter": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz",
+ "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY="
+ },
+ "component-inherit": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz",
+ "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM="
+ },
"compressible": {
"version": "2.0.15",
"resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.15.tgz",
@@ -768,6 +831,69 @@
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
"integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
},
+ "engine.io": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.2.1.tgz",
+ "integrity": "sha512-+VlKzHzMhaU+GsCIg4AoXF1UdDFjHHwMmMKqMJNDNLlUlejz58FCy4LBqB2YVJskHGYl06BatYWKP2TVdVXE5w==",
+ "requires": {
+ "accepts": "~1.3.4",
+ "base64id": "1.0.0",
+ "cookie": "0.3.1",
+ "debug": "~3.1.0",
+ "engine.io-parser": "~2.1.0",
+ "ws": "~3.3.1"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+ "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+ "requires": {
+ "ms": "2.0.0"
+ }
+ }
+ }
+ },
+ "engine.io-client": {
+ "version": "3.2.1",
+ "resolved": "http://registry.npmjs.org/engine.io-client/-/engine.io-client-3.2.1.tgz",
+ "integrity": "sha512-y5AbkytWeM4jQr7m/koQLc5AxpRKC1hEVUb/s1FUAWEJq5AzJJ4NLvzuKPuxtDi5Mq755WuDvZ6Iv2rXj4PTzw==",
+ "requires": {
+ "component-emitter": "1.2.1",
+ "component-inherit": "0.0.3",
+ "debug": "~3.1.0",
+ "engine.io-parser": "~2.1.1",
+ "has-cors": "1.1.0",
+ "indexof": "0.0.1",
+ "parseqs": "0.0.5",
+ "parseuri": "0.0.5",
+ "ws": "~3.3.1",
+ "xmlhttprequest-ssl": "~1.5.4",
+ "yeast": "0.1.2"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+ "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+ "requires": {
+ "ms": "2.0.0"
+ }
+ }
+ }
+ },
+ "engine.io-parser": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.1.3.tgz",
+ "integrity": "sha512-6HXPre2O4Houl7c4g7Ic/XzPnHBvaEmN90vtRO9uLmwtRqQmTOw0QMevL1TOfL2Cpu1VzsaTmMotQgMdkzGkVA==",
+ "requires": {
+ "after": "0.8.2",
+ "arraybuffer.slice": "~0.0.7",
+ "base64-arraybuffer": "0.1.5",
+ "blob": "0.0.5",
+ "has-binary2": "~1.0.2"
+ }
+ },
"error-ex": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz",
@@ -1550,6 +1676,26 @@
"ansi-regex": "^2.0.0"
}
},
+ "has-binary2": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz",
+ "integrity": "sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==",
+ "requires": {
+ "isarray": "2.0.1"
+ },
+ "dependencies": {
+ "isarray": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
+ "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4="
+ }
+ }
+ },
+ "has-cors": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz",
+ "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk="
+ },
"has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
@@ -1604,6 +1750,11 @@
"resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
"integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o="
},
+ "indexof": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz",
+ "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10="
+ },
"inflection": {
"version": "1.12.0",
"resolved": "https://registry.npmjs.org/inflection/-/inflection-1.12.0.tgz",
@@ -2325,6 +2476,11 @@
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
},
+ "object-component": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz",
+ "integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE="
+ },
"object-keys": {
"version": "1.0.12",
"resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.12.tgz",
@@ -2435,6 +2591,22 @@
"error-ex": "^1.2.0"
}
},
+ "parseqs": {
+ "version": "0.0.5",
+ "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz",
+ "integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=",
+ "requires": {
+ "better-assert": "~1.0.0"
+ }
+ },
+ "parseuri": {
+ "version": "0.0.5",
+ "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz",
+ "integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=",
+ "requires": {
+ "better-assert": "~1.0.0"
+ }
+ },
"parseurl": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz",
@@ -3079,6 +3251,90 @@
}
}
},
+ "socket.io": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.1.1.tgz",
+ "integrity": "sha512-rORqq9c+7W0DAK3cleWNSyfv/qKXV99hV4tZe+gGLfBECw3XEhBy7x85F3wypA9688LKjtwO9pX9L33/xQI8yA==",
+ "requires": {
+ "debug": "~3.1.0",
+ "engine.io": "~3.2.0",
+ "has-binary2": "~1.0.2",
+ "socket.io-adapter": "~1.1.0",
+ "socket.io-client": "2.1.1",
+ "socket.io-parser": "~3.2.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+ "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+ "requires": {
+ "ms": "2.0.0"
+ }
+ }
+ }
+ },
+ "socket.io-adapter": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.1.tgz",
+ "integrity": "sha1-KoBeihTWNyEk3ZFZrUUC+MsH8Gs="
+ },
+ "socket.io-client": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.1.1.tgz",
+ "integrity": "sha512-jxnFyhAuFxYfjqIgduQlhzqTcOEQSn+OHKVfAxWaNWa7ecP7xSNk2Dx/3UEsDcY7NcFafxvNvKPmmO7HTwTxGQ==",
+ "requires": {
+ "backo2": "1.0.2",
+ "base64-arraybuffer": "0.1.5",
+ "component-bind": "1.0.0",
+ "component-emitter": "1.2.1",
+ "debug": "~3.1.0",
+ "engine.io-client": "~3.2.0",
+ "has-binary2": "~1.0.2",
+ "has-cors": "1.1.0",
+ "indexof": "0.0.1",
+ "object-component": "0.0.3",
+ "parseqs": "0.0.5",
+ "parseuri": "0.0.5",
+ "socket.io-parser": "~3.2.0",
+ "to-array": "0.1.4"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+ "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+ "requires": {
+ "ms": "2.0.0"
+ }
+ }
+ }
+ },
+ "socket.io-parser": {
+ "version": "3.2.0",
+ "resolved": "http://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.2.0.tgz",
+ "integrity": "sha512-FYiBx7rc/KORMJlgsXysflWx/RIvtqZbyGLlHZvjfmPTPeuD/I8MaW7cfFrj5tRltICJdgwflhfZ3NVVbVLFQA==",
+ "requires": {
+ "component-emitter": "1.2.1",
+ "debug": "~3.1.0",
+ "isarray": "2.0.1"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+ "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "isarray": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
+ "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4="
+ }
+ }
+ },
"sodium-native": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/sodium-native/-/sodium-native-2.2.1.tgz",
@@ -3534,7 +3790,7 @@
},
"through": {
"version": "2.3.8",
- "resolved": "http://registry.npmjs.org/through/-/through-2.3.8.tgz",
+ "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
"integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU="
},
"timers-ext": {
@@ -3554,6 +3810,11 @@
"os-tmpdir": "~1.0.2"
}
},
+ "to-array": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz",
+ "integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA="
+ },
"toidentifier": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
@@ -3615,6 +3876,11 @@
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
"integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
},
+ "ultron": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz",
+ "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og=="
+ },
"umzug": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/umzug/-/umzug-2.1.0.tgz",
@@ -3762,6 +4028,21 @@
"mkdirp": "^0.5.1"
}
},
+ "ws": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz",
+ "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==",
+ "requires": {
+ "async-limiter": "~1.0.0",
+ "safe-buffer": "~5.1.0",
+ "ultron": "~1.1.0"
+ }
+ },
+ "xmlhttprequest-ssl": {
+ "version": "1.5.5",
+ "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz",
+ "integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4="
+ },
"xtend": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz",
@@ -3804,6 +4085,11 @@
"requires": {
"camelcase": "^4.1.0"
}
+ },
+ "yeast": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz",
+ "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk="
}
}
}
diff --git a/server/package.json b/server/package.json
index 09bbb42..7061c78 100644
--- a/server/package.json
+++ b/server/package.json
@@ -23,6 +23,7 @@
"sequelize": "^4.38.1",
"sequelize-cli": "^4.1.1",
"shelljs": "^0.8.2",
+ "socket.io": "^2.1.1",
"standard": "^11.0.1"
},
"devDependencies": {
diff --git a/webapp/config/index.js b/webapp/config/index.js
index e283935..c1ed721 100644
--- a/webapp/config/index.js
+++ b/webapp/config/index.js
@@ -42,9 +42,13 @@ module.exports = {
cssSourceMap: true,
proxyTable: {
- // proxy all requests starting with /api to express server
+ // proxy all requests starting with /api and /socket.io to the express server
'/api': {
target: 'https://localhost:' + process.env.API_PORT + '/'
+ },
+ '/socket.io': {
+ target: 'https://localhost:' + process.env.API_PORT + '/',
+ ws: true
}
}
},
diff --git a/webapp/package-lock.json b/webapp/package-lock.json
index 83b8532..1989e9f 100644
--- a/webapp/package-lock.json
+++ b/webapp/package-lock.json
@@ -216,6 +216,11 @@
}
}
},
+ "after": {
+ "version": "0.8.2",
+ "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz",
+ "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8="
+ },
"ajv": {
"version": "5.5.2",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz",
@@ -369,6 +374,11 @@
"integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=",
"dev": true
},
+ "arraybuffer.slice": {
+ "version": "0.0.7",
+ "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz",
+ "integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog=="
+ },
"arrify": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz",
@@ -419,8 +429,7 @@
"async-limiter": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz",
- "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==",
- "dev": true
+ "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg=="
},
"atob": {
"version": "2.1.1",
@@ -1306,6 +1315,11 @@
"integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==",
"dev": true
},
+ "backo2": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz",
+ "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc="
+ },
"balanced-match": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
@@ -1372,6 +1386,11 @@
}
}
},
+ "base64-arraybuffer": {
+ "version": "0.1.5",
+ "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz",
+ "integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg="
+ },
"base64-js": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz",
@@ -1384,6 +1403,14 @@
"integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=",
"dev": true
},
+ "better-assert": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz",
+ "integrity": "sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI=",
+ "requires": {
+ "callsite": "1.0.0"
+ }
+ },
"bfj-node4": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/bfj-node4/-/bfj-node4-5.3.1.tgz",
@@ -1407,6 +1434,11 @@
"integrity": "sha1-RqoXUftqL5PuXmibsQh9SxTGwgU=",
"dev": true
},
+ "blob": {
+ "version": "0.0.5",
+ "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz",
+ "integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig=="
+ },
"bluebird": {
"version": "3.5.1",
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz",
@@ -1705,6 +1737,11 @@
"callsites": "^0.2.0"
}
},
+ "callsite": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz",
+ "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA="
+ },
"callsites": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz",
@@ -2084,11 +2121,20 @@
"integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=",
"dev": true
},
+ "component-bind": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz",
+ "integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E="
+ },
"component-emitter": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz",
- "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=",
- "dev": true
+ "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY="
+ },
+ "component-inherit": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz",
+ "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM="
},
"compressible": {
"version": "2.0.15",
@@ -3034,6 +3080,56 @@
"once": "^1.4.0"
}
},
+ "engine.io-client": {
+ "version": "3.2.1",
+ "resolved": "http://registry.npmjs.org/engine.io-client/-/engine.io-client-3.2.1.tgz",
+ "integrity": "sha512-y5AbkytWeM4jQr7m/koQLc5AxpRKC1hEVUb/s1FUAWEJq5AzJJ4NLvzuKPuxtDi5Mq755WuDvZ6Iv2rXj4PTzw==",
+ "requires": {
+ "component-emitter": "1.2.1",
+ "component-inherit": "0.0.3",
+ "debug": "~3.1.0",
+ "engine.io-parser": "~2.1.1",
+ "has-cors": "1.1.0",
+ "indexof": "0.0.1",
+ "parseqs": "0.0.5",
+ "parseuri": "0.0.5",
+ "ws": "~3.3.1",
+ "xmlhttprequest-ssl": "~1.5.4",
+ "yeast": "0.1.2"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+ "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "ws": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz",
+ "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==",
+ "requires": {
+ "async-limiter": "~1.0.0",
+ "safe-buffer": "~5.1.0",
+ "ultron": "~1.1.0"
+ }
+ }
+ }
+ },
+ "engine.io-parser": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.1.3.tgz",
+ "integrity": "sha512-6HXPre2O4Houl7c4g7Ic/XzPnHBvaEmN90vtRO9uLmwtRqQmTOw0QMevL1TOfL2Cpu1VzsaTmMotQgMdkzGkVA==",
+ "requires": {
+ "after": "0.8.2",
+ "arraybuffer.slice": "~0.0.7",
+ "base64-arraybuffer": "0.1.5",
+ "blob": "0.0.5",
+ "has-binary2": "~1.0.2"
+ }
+ },
"enhanced-resolve": {
"version": "3.4.1",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-3.4.1.tgz",
@@ -4863,6 +4959,26 @@
"ansi-regex": "^2.0.0"
}
},
+ "has-binary2": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz",
+ "integrity": "sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==",
+ "requires": {
+ "isarray": "2.0.1"
+ },
+ "dependencies": {
+ "isarray": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
+ "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4="
+ }
+ }
+ },
+ "has-cors": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz",
+ "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk="
+ },
"has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
@@ -5330,8 +5446,7 @@
"indexof": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz",
- "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=",
- "dev": true
+ "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10="
},
"inflight": {
"version": "1.0.6",
@@ -6598,6 +6713,11 @@
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
"dev": true
},
+ "object-component": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz",
+ "integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE="
+ },
"object-copy": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz",
@@ -6897,6 +7017,22 @@
"error-ex": "^1.2.0"
}
},
+ "parseqs": {
+ "version": "0.0.5",
+ "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz",
+ "integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=",
+ "requires": {
+ "better-assert": "~1.0.0"
+ }
+ },
+ "parseuri": {
+ "version": "0.0.5",
+ "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz",
+ "integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=",
+ "requires": {
+ "better-assert": "~1.0.0"
+ }
+ },
"parseurl": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz",
@@ -9930,8 +10066,7 @@
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
- "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
- "dev": true
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
},
"safe-regex": {
"version": "1.1.0",
@@ -10264,6 +10399,62 @@
"kind-of": "^3.2.0"
}
},
+ "socket.io-client": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.1.1.tgz",
+ "integrity": "sha512-jxnFyhAuFxYfjqIgduQlhzqTcOEQSn+OHKVfAxWaNWa7ecP7xSNk2Dx/3UEsDcY7NcFafxvNvKPmmO7HTwTxGQ==",
+ "requires": {
+ "backo2": "1.0.2",
+ "base64-arraybuffer": "0.1.5",
+ "component-bind": "1.0.0",
+ "component-emitter": "1.2.1",
+ "debug": "~3.1.0",
+ "engine.io-client": "~3.2.0",
+ "has-binary2": "~1.0.2",
+ "has-cors": "1.1.0",
+ "indexof": "0.0.1",
+ "object-component": "0.0.3",
+ "parseqs": "0.0.5",
+ "parseuri": "0.0.5",
+ "socket.io-parser": "~3.2.0",
+ "to-array": "0.1.4"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+ "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+ "requires": {
+ "ms": "2.0.0"
+ }
+ }
+ }
+ },
+ "socket.io-parser": {
+ "version": "3.2.0",
+ "resolved": "http://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.2.0.tgz",
+ "integrity": "sha512-FYiBx7rc/KORMJlgsXysflWx/RIvtqZbyGLlHZvjfmPTPeuD/I8MaW7cfFrj5tRltICJdgwflhfZ3NVVbVLFQA==",
+ "requires": {
+ "component-emitter": "1.2.1",
+ "debug": "~3.1.0",
+ "isarray": "2.0.1"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+ "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "isarray": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
+ "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4="
+ }
+ }
+ },
"sockjs": {
"version": "0.3.19",
"resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.19.tgz",
@@ -10692,6 +10883,11 @@
"os-tmpdir": "~1.0.2"
}
},
+ "to-array": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz",
+ "integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA="
+ },
"to-arraybuffer": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz",
@@ -10890,6 +11086,11 @@
}
}
},
+ "ultron": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz",
+ "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og=="
+ },
"union-value": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz",
@@ -11931,6 +12132,11 @@
"safe-buffer": "~5.1.0"
}
},
+ "xmlhttprequest-ssl": {
+ "version": "1.5.5",
+ "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz",
+ "integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4="
+ },
"xtend": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz",
@@ -12042,6 +12248,11 @@
"dev": true
}
}
+ },
+ "yeast": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz",
+ "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk="
}
}
}
diff --git a/webapp/package.json b/webapp/package.json
index 73d6809..d27666a 100644
--- a/webapp/package.json
+++ b/webapp/package.json
@@ -12,6 +12,7 @@
},
"dependencies": {
"axios": "^0.18.0",
+ "socket.io-client": "^2.1.1",
"vue": "^2.5.17",
"vue-codemirror": "^4.0.6",
"vue-i18n": "^7.8.1",
diff --git a/webapp/src/components/AccountModule.vue b/webapp/src/components/AccountModule.vue
index 8d93dbe..e9a4cce 100644
--- a/webapp/src/components/AccountModule.vue
+++ b/webapp/src/components/AccountModule.vue
@@ -9,7 +9,9 @@
<template>
<div class="account-page">
-
+ <div style="display: flex; justify-content: center; margin-top: 40px">
+ <v-btn color="primary" @click="newAlert">New Alert</v-btn>
+ </div>
</div>
</template>
@@ -19,9 +21,14 @@ export default {
name: 'AccountPage',
data () {
return {
+ testId: 0
}
},
methods: {
+ newAlert () {
+ this.$alert({ type: 'success', text: 'test ' + this.testId })
+ this.testId++
+ }
}
}
</script>
diff --git a/webapp/src/components/DashboardPage.vue b/webapp/src/components/DashboardPage.vue
index 89c8c0c..53d19be 100644
--- a/webapp/src/components/DashboardPage.vue
+++ b/webapp/src/components/DashboardPage.vue
@@ -65,7 +65,7 @@
<v-spacer></v-spacer>
<v-btn v-if="devMode" icon @click="$store.commit('saveSetting', { name: 'locale', value: settings.locale === 'en' ? 'de' : 'en' })"><v-icon>language</v-icon></v-btn>
<v-btn v-if="devMode" icon @click="$store.commit('saveSetting', { name: 'dark', value: !settings.dark })"><v-icon>invert_colors</v-icon></v-btn>
- <notifications-alerts></notifications-alerts>
+ <notifications-alerts style="margin-right: 20px"></notifications-alerts>
<v-toolbar-items>
<v-menu offset-y>
<v-btn class="user-button" flat slot="activator">
@@ -125,7 +125,8 @@ export default {
drawerTranslateX: 0,
drawerMini: localStorage.getItem('drawerMini') === 'true',
drawerOpen: localStorage.getItem('drawerOpen') !== 'false',
- userFullName: ''
+ userFullName: '',
+ clientCount: 0
}
},
computed: {
@@ -190,6 +191,7 @@ export default {
this.$http.post('/api/logout').then(response => {
this.setLoginRedirect(this.$route.fullPath)
this.$router.push('/login')
+ this.$socket.close()
})
}
},
diff --git a/webapp/src/components/GroupModuleDialog.vue b/webapp/src/components/GroupModuleDialog.vue
index 7c0905d..8f88f29 100644
--- a/webapp/src/components/GroupModuleDialog.vue
+++ b/webapp/src/components/GroupModuleDialog.vue
@@ -15,6 +15,20 @@
"client": "Add clients | Add this client? | Add these {0} clients?"
}
},
+ "success": {
+ "delete": {
+ "client": "Successfully deleted client | Successfully deleted {0} clients",
+ "group": "Successfully deleted group | Successfully deleted {0} groups"
+ },
+ "remove": {
+ "client": "Successfully removed client | Successfully removed {0} clients",
+ "group": "Successfully removed group | Successfully removed {0} groups"
+ },
+ "add": {
+ "client": "Successfully added client | Successfully added {0} clients",
+ "group": "Successfully added group | Successfully added {0} groups"
+ }
+ },
"deletePermanently": {
"group": "Permanently delete group | Permanently delete groups",
"client": "Permanently delete client | Permanently delete clients"
@@ -38,6 +52,20 @@
"client": "Clienten hinzufügen | Diesen Clienten hinzufügen? | Diese {0} Clienten hinzufügen?"
}
},
+ "success": {
+ "delete": {
+ "client": "Client erfolgreich gelöscht | {0} Clienten erfolgreich gelöscht",
+ "group": "Gruppe erfolgreich gelöscht | {0} Gruppen erfolgreich gelöscht"
+ },
+ "remove": {
+ "client": "Client erfolgreich entfernt | {0} Clienten erfolgreich entfernt",
+ "group": "Gruppe erfolgreich entfernt | {0} Gruppen erfolgreich entfernt"
+ },
+ "add": {
+ "client": "Client erfolgreich hinzugefügt | {0} Clienten erfolgreich hinzugefügt",
+ "group": "Gruppe erfolgreich hinzugefügt | {0} Gruppen erfolgreich hinzugefügt"
+ }
+ },
"deletePermanently": {
"group": "Gruppe dauerhaft löschen | Gruppen dauerhaft löschen",
"client": "Client dauerhaft löschen | Clienten dauerhaft löschen"
@@ -157,7 +185,11 @@ export default {
'remove': { 'group': 'removeSubroups', 'client': 'removeClients' },
'add': { 'group': 'addSubgroups', 'client': 'addClients' }
}
- var data = { ...this.dialog.info }
+ var count = this.action === 'add' ? this.selected.length : this.dialog.info.selected.length
+ var data = { ...this.dialog.info, callback: () => this.$snackbar({
+ color: 'success',
+ text: this.$tc('success.' + this.action + '.' + this.dialog.info.type, count, [count])
+ }) }
if (this.action === 'add') data.selected = this.selected
this.$store.dispatch('groups/' + actionMap[this.action][this.dialog.info.type], data)
this.setDialog({ show: false })
diff --git a/webapp/src/components/LoginPage.vue b/webapp/src/components/LoginPage.vue
index aeaf66e..40b08ee 100644
--- a/webapp/src/components/LoginPage.vue
+++ b/webapp/src/components/LoginPage.vue
@@ -48,6 +48,7 @@
</template>
<script>
+
export default {
name: 'LoginPage',
data () {
@@ -83,6 +84,7 @@ export default {
const nextRoute = this.$store.state.loginRedirect
if (nextRoute) this.$router.replace(nextRoute)
else this.$router.replace({ name: 'dashboard' })
+ this.$socket.open()
})
.catch(error => {
if (error.response.data.status === 'USER_NOTFOUND') {
diff --git a/webapp/src/components/NotificationsAlerts.vue b/webapp/src/components/NotificationsAlerts.vue
index 058cc4f..c16ad58 100644
--- a/webapp/src/components/NotificationsAlerts.vue
+++ b/webapp/src/components/NotificationsAlerts.vue
@@ -19,7 +19,7 @@
<div>
<v-menu v-model="menu" offset-y left :close-on-content-click="false" :nudge-bottom="10" content-class="notifications">
<v-btn icon slot="activator">
- <v-badge :value="newAlertCount" color="red darken-3" bottom>
+ <v-badge :value="!menu && newAlertCount" color="red darken-3" bottom>
<span slot="badge">{{newAlertCount}}</span>
<v-icon>notifications</v-icon>
</v-badge>
@@ -30,7 +30,7 @@
<div style="max-height: 70vh; overflow: auto">
<div v-if="alerts.length === 0" class="text-xs-center" style="width: 100%">{{ $t('noNotifications') }}</div>
<div v-else class="flex-between-center">
- <v-subheader v-if="newCount > 0">{{ $t('new') }}</v-subheader>
+ <v-subheader v-if="newAlertCount > 0">{{ $t('new') }}</v-subheader>
<v-subheader v-else>{{ $t('notifications') }}</v-subheader>
<v-btn @click="clearAll" icon small style="margin-right: 16px"><v-icon>clear_all</v-icon></v-btn>
</div>
@@ -48,7 +48,7 @@
<v-btn @click="closeAlert(a)" small :ripple="false" style="margin: 0; margin-top: -4px" icon><v-icon>close</v-icon></v-btn>
</div>
</v-alert>
- <v-subheader :key="'subheader' + index" v-if="newCount < alerts.length && index == newCount - 1">{{ $t('old') }}</v-subheader>
+ <v-subheader :key="'subheader' + index" v-if="newAlertCount < alerts.length && index == newAlertCount - 1">{{ $t('old') }}</v-subheader>
</template>
</div>
</v-card-text>
@@ -64,8 +64,7 @@ export default {
name: 'NotificationsAlerts',
data () {
return {
- menu: false,
- newCount: 0
+ menu: false
}
},
computed: {
@@ -76,32 +75,48 @@ export default {
},
watch: {
menu (value) {
- if (value) {
- this.newCount = this.newAlertCount
+ if (!value) {
+ this.$socket.emit('notifications resetNewAlertCount')
this.resetNewAlertCount()
}
}
},
methods: {
...mapMutations('notifications', ['resetNewAlertCount', 'removeAlert']),
- closeAlert (a) {
- this.removeAlert(a)
+ closeAlert (a, emit = true) {
+ if (emit) this.$socket.emit('notifications closeAlert', a)
+ this.removeAlert(a.id)
if (!this.alertsShown) this.menu = false
},
- clearAll () {
+ clearAll (emit = true) {
+ if (emit) this.$socket.emit('notifications clearAll')
var timeout = this.alerts.length * 50
setTimeout(this.closeMenu, timeout)
var removeAlert = this.removeAlert
this.alerts.forEach(a => {
timeout -= 50
setTimeout(function () {
- removeAlert(a)
+ removeAlert(a.id)
}, timeout)
})
},
closeMenu () {
this.menu = false
}
+ },
+ created () {
+ this.$socket.on('notifications clearAll', () => {
+ this.clearAll(false)
+ })
+ this.$socket.on('notifications newAlert', data => {
+ this.$alert(data, false)
+ })
+ this.$socket.on('notifications closeAlert', id => {
+ this.closeAlert(id, false)
+ })
+ this.$socket.on('notifications resetNewAlertCount', () => {
+ this.resetNewAlertCount()
+ })
}
}
</script>
diff --git a/webapp/src/components/NotificationsSnackbars.vue b/webapp/src/components/NotificationsSnackbars.vue
index ce768d6..c01c5c4 100644
--- a/webapp/src/components/NotificationsSnackbars.vue
+++ b/webapp/src/components/NotificationsSnackbars.vue
@@ -14,7 +14,7 @@
v-model="showSnackbar"
bottom
right
- :timeout="currentSnackbar.timeout || 2000"
+ :timeout="currentSnackbar.timeout || 4000"
:color="currentSnackbar.color"
>
{{ currentSnackbar.text }}
diff --git a/webapp/src/main.js b/webapp/src/main.js
index 3f59ede..8637c96 100644
--- a/webapp/src/main.js
+++ b/webapp/src/main.js
@@ -10,6 +10,7 @@ import Vuex from 'vuex'
import globalStore from '@/store/global'
import storeModules from '@/config/store'
import axios from 'axios'
+import io from 'socket.io-client'
import { router, registerRouterGuards } from '@/router'
import VueI18n from 'vue-i18n'
import i18nMessages from '@/config/i18n'
@@ -60,8 +61,30 @@ axios.interceptors.response.use(null, error => {
return Promise.reject(error)
})
+const socket = io({
+ autoConnect: false
+});
+
+socket.on('error', function(err) {
+ console.log('Socket.io error: ' + err);
+ if (err === 'TOKEN_MISSING' || err === 'TOKEN_INVALID') {
+ socket.close()
+ console.log('Closing socket.');
+ }
+});
+
+socket.on('hello', data => {
+ console.log(data)
+})
+
+socket.open()
+
Vue.prototype.$http = axios
-Vue.prototype.$alert = function (data) {
+Vue.prototype.$socket = socket
+
+Vue.prototype.$alert = function (data, emit = true) {
+ if (!data.id) data.id = '_' + Math.random().toString(36).substr(2, 9)
+ if (emit) socket.emit('notifications newAlert', data)
store.commit('notifications/newAlert', data)
}
Vue.prototype.$snackbar = function (data) {
diff --git a/webapp/src/store/groups.js b/webapp/src/store/groups.js
index 1bbded9..adc78f8 100644
--- a/webapp/src/store/groups.js
+++ b/webapp/src/store/groups.js
@@ -130,7 +130,7 @@ export default {
}
})
},
- deleteGroups (context, { selected }) {
+ deleteGroups (context, { selected, callback }) {
const ids = selected.map(x => x.id)
axios.post('/api/groups/delete', { ids }).then(() => {
var i = 1
@@ -142,18 +142,20 @@ export default {
i++
}
context.dispatch('reload')
+ if (callback) callback()
})
},
- deleteClients (context, { selected }) {
+ deleteClients (context, { selected, callback }) {
const ids = selected.map(x => x.id)
axios.post('/api/clients/delete', { ids }).then(() => {
const index = context.state.tabChain.length - 1
const item = context.state.tabChain[index]
if (item.tabType === 'client' && ids.includes(item.id)) context.commit('deleteFromTabChain', { index, count: 1 })
context.dispatch('reload')
+ if (callback) callback()
})
},
- removeSubroups (context, { tabIndex, selected }) {
+ removeSubroups (context, { tabIndex, selected,callback }) {
const id = context.state.tabChain[tabIndex].id
const ids = selected.map(x => x.id)
axios.post('/api/groups/removeSubgroups', { id, ids }).then(() => {
@@ -163,9 +165,10 @@ export default {
context.commit('deleteFromTabChain', { index: tabIndex + 1, count: context.state.tabChain.length - (tabIndex + 1) })
}
context.dispatch('reload')
+ if (callback) callback()
})
},
- removeClients (context, { tabIndex, selected }) {
+ removeClients (context, { tabIndex, selected, callback }) {
const id = context.state.tabChain[tabIndex].id
const ids = selected.map(x => x.id)
axios.post('/api/groups/removeClients', { id, ids }).then(() => {
@@ -175,20 +178,23 @@ export default {
context.commit('deleteFromTabChain', { index: tabIndex + 1, count: 1 })
}
context.dispatch('reload')
+ if (callback) callback()
})
},
- addSubgroups (context, { tabIndex, selected }) {
+ addSubgroups (context, { tabIndex, selected, callback }) {
const id = context.state.tabChain[tabIndex].id
const ids = selected.map(x => x.id)
axios.post('/api/groups/addSubgroups', { id, ids }).then(() => {
context.dispatch('reload')
+ if (callback) callback()
})
},
- addClients (context, { tabIndex, selected }) {
+ addClients (context, { tabIndex, selected, callback }) {
const id = context.state.tabChain[tabIndex].id
const ids = selected.map(x => x.id)
axios.post('/api/groups/addClients', { id, ids }).then(() => {
context.dispatch('reload')
+ if (callback) callback()
})
}
}
diff --git a/webapp/src/store/notifications.js b/webapp/src/store/notifications.js
index 9f41d07..4687b78 100644
--- a/webapp/src/store/notifications.js
+++ b/webapp/src/store/notifications.js
@@ -23,10 +23,13 @@ export default {
state.newAlertCount++
state.alerts.unshift(data)
},
- removeAlert (state, a) {
+ removeAlert (state, id) {
+ const a = state.alerts.find(el => el.id === id)
a.show = false
setTimeout(function () {
- state.alerts.splice(state.alerts.indexOf(a), 1)
+ var index = state.alerts.indexOf(a)
+ state.alerts.splice(index, 1)
+ if (index < state.newAlertCount && index >= 0) state.newAlertCount--
}, 200)
},
resetNewAlertCount (state) {