Skip to content
This repository was archived by the owner on Dec 15, 2019. It is now read-only.

Commit 0db021a

Browse files
committed
Merge branch 'realtime-sync'
2 parents 4cf457b + 52a435e commit 0db021a

File tree

4 files changed

+124
-6
lines changed

4 files changed

+124
-6
lines changed

package-lock.json

Lines changed: 13 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@
2828
"mongodb": "^3.1.10",
2929
"mongoose": "^5.4.0",
3030
"multer": "^1.4.1",
31-
"pm2": "^3.2.9"
31+
"pm2": "^3.2.9",
32+
"ws": "^6.1.3"
3233
},
3334
"devDependencies": {
3435
"eslint": "^5.10.0",

src/app.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
const compression = require('compression');
22
const express = require('express');
3+
const server = require('http').createServer();
34
const fs = require('fs');
45
const cors = require('cors');
56
const path = require('path');
@@ -31,5 +32,9 @@ app.use(cors());
3132
// GraphQL API Module
3233
app.use('/api', require('./api/api'));
3334

34-
// Start
35-
app.listen(config.server.port);
35+
// Spawn Websocket
36+
require('./websocket')(server);
37+
38+
// Add express http-server
39+
server.on('request', app);
40+
server.listen(config.server.port);

src/websocket.js

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
const userModel = require('./models/user');
2+
const WebSocket = require('ws');
3+
4+
module.exports = server => {
5+
const wss = new WebSocket.Server({server: server});
6+
const userMap = {};
7+
8+
wss.on('connection', ws => {
9+
let user;
10+
11+
ws.on('message', async message => {
12+
13+
// Answer ping request
14+
if (message === '__ping__') {
15+
return ws.send('__pong__');
16+
}
17+
18+
// Try to parse message
19+
try {
20+
message = JSON.parse(message);
21+
} catch (ignored) {
22+
return;
23+
}
24+
25+
const {type, value} = message;
26+
switch (type) {
27+
case 'register': {
28+
if (typeof value === 'string' && value) {
29+
user = await userModel.findOne({apikeys: {$elemMatch: {key: value}}});
30+
31+
if (!user) {
32+
return;
33+
}
34+
35+
const userid = user.id;
36+
if (!(userid in userMap)) {
37+
userMap[userid] = {
38+
websockets: [],
39+
lastBroadCast: 0
40+
};
41+
} else if (userMap[userid].websockets.includes(ws)) {
42+
return;
43+
}
44+
45+
userMap[userid].websockets.push(ws);
46+
47+
// Approve registration
48+
ws.send(JSON.stringify({
49+
type: 'registration-approval',
50+
value: {
51+
lastBroadCast: userMap[userid].lastBroadCast
52+
}
53+
}));
54+
}
55+
56+
break;
57+
}
58+
case 'broadcast': {
59+
if (user) {
60+
const container = userMap[user.id];
61+
const {websockets} = container;
62+
63+
// Broadcast message
64+
for (let i = 0, l = websockets.length; i < l; i++) {
65+
const socket = websockets[i];
66+
67+
if (socket !== ws) {
68+
socket.send(JSON.stringify({
69+
type: 'broadcast',
70+
value
71+
}));
72+
}
73+
}
74+
75+
// Update last broadcast timestamp
76+
container.lastBroadCast = Date.now();
77+
}
78+
break;
79+
}
80+
}
81+
});
82+
83+
ws.on('close', () => {
84+
85+
// Check if socket was registered
86+
if (user) {
87+
const {websockets} = userMap[user.id];
88+
const idx = websockets.indexOf(ws);
89+
90+
// Remove socket
91+
if (~idx) {
92+
websockets.splice(idx, 1);
93+
}
94+
95+
// Clean up if no connection is open anymore
96+
if (!websockets.length) {
97+
delete userMap[user.id];
98+
}
99+
}
100+
});
101+
});
102+
};

0 commit comments

Comments
 (0)