@cocreate/socket-server 1.10.2 → 1.10.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/FUNDING.yml +3 -3
- package/.github/workflows/automated.yml +55 -55
- package/CHANGELOG.md +665 -656
- package/CONTRIBUTING.md +96 -96
- package/CoCreate.config.js +25 -25
- package/LICENSE +21 -21
- package/README.md +52 -52
- package/demo/server.js +19 -19
- package/docs/index.html +194 -64
- package/package.json +1 -1
- package/release.config.js +21 -21
- package/src/index.js +251 -251
package/src/index.js
CHANGED
|
@@ -1,251 +1,251 @@
|
|
|
1
|
-
const WebSocket = require('ws');
|
|
2
|
-
const url = require("url");
|
|
3
|
-
const EventEmitter = require("events").EventEmitter;
|
|
4
|
-
const uid = require('@cocreate/uuid')
|
|
5
|
-
|
|
6
|
-
class SocketServer extends EventEmitter {
|
|
7
|
-
constructor(prefix) {
|
|
8
|
-
super();
|
|
9
|
-
this.clients = new Map();
|
|
10
|
-
this.prefix = prefix || "crud";
|
|
11
|
-
this.wss = new WebSocket.Server({ noServer: true });
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
handleUpgrade(req, socket, head) {
|
|
15
|
-
const self = this;
|
|
16
|
-
const pathname = url.parse(req.url).pathname;
|
|
17
|
-
const config = this.getKeyFromUrl(pathname)
|
|
18
|
-
if (config.type == this.prefix) {
|
|
19
|
-
self.wss.handleUpgrade(req, socket, head, function (socket) {
|
|
20
|
-
socket.config = config
|
|
21
|
-
self.onWebSocket(req, socket);
|
|
22
|
-
})
|
|
23
|
-
return true;
|
|
24
|
-
}
|
|
25
|
-
return false;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
onWebSocket(req, socket) {
|
|
29
|
-
const self = this;
|
|
30
|
-
|
|
31
|
-
this.addClient(socket);
|
|
32
|
-
|
|
33
|
-
socket.on('message', async (message) => {
|
|
34
|
-
self.onMessage(req, socket, message);
|
|
35
|
-
})
|
|
36
|
-
|
|
37
|
-
socket.on('close', function () {
|
|
38
|
-
self.removeClient(socket)
|
|
39
|
-
})
|
|
40
|
-
|
|
41
|
-
socket.on("error", () => {
|
|
42
|
-
self.removeClient(socket)
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
this.send(socket, 'connect', socket.config.key);
|
|
46
|
-
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
addClient(socket) {
|
|
50
|
-
let organization_id = socket.config.organization_id
|
|
51
|
-
let user_id = socket.config.user_id
|
|
52
|
-
let key = socket.config.key
|
|
53
|
-
let room_clients = this.clients.get(key);
|
|
54
|
-
if (room_clients) {
|
|
55
|
-
room_clients.push(socket);
|
|
56
|
-
} else {
|
|
57
|
-
room_clients = [socket];
|
|
58
|
-
}
|
|
59
|
-
this.clients.set(key, room_clients);
|
|
60
|
-
|
|
61
|
-
if (user_id)
|
|
62
|
-
this.emit('userStatus', socket, { user_id, userStatus: 'on', organization_id });
|
|
63
|
-
|
|
64
|
-
//. add metrics
|
|
65
|
-
let total_cnt = 0;
|
|
66
|
-
this.clients.forEach((c) => total_cnt += c.length)
|
|
67
|
-
|
|
68
|
-
this.emit("createMetrics", {
|
|
69
|
-
organization_id,
|
|
70
|
-
client_cnt: room_clients.length,
|
|
71
|
-
total_cnt: total_cnt
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
removeClient(socket) {
|
|
76
|
-
let organization_id = socket.config.organization_id
|
|
77
|
-
let user_id = socket.config.user_id
|
|
78
|
-
let key = socket.config.key
|
|
79
|
-
let room_clients = this.clients.get(key)
|
|
80
|
-
const index = room_clients.indexOf(socket);
|
|
81
|
-
|
|
82
|
-
if (index > -1) {
|
|
83
|
-
room_clients.splice(index, 1);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
if (room_clients.length == 0) {
|
|
87
|
-
if (user_id)
|
|
88
|
-
this.emit('userStatus', socket, { user_id, status: 'off', organization_id });
|
|
89
|
-
|
|
90
|
-
// TODO: remove if no one else connected to organization
|
|
91
|
-
this.emit("deleteMetrics", { organization_id });
|
|
92
|
-
this.emit("deletePermissions", organization_id);
|
|
93
|
-
this.emit('disconnect', organization_id)
|
|
94
|
-
} else {
|
|
95
|
-
let total_cnt = 0;
|
|
96
|
-
this.clients.forEach((c) => total_cnt += c.length)
|
|
97
|
-
|
|
98
|
-
this.emit("changeCountMetrics", {
|
|
99
|
-
organization_id,
|
|
100
|
-
total_cnt,
|
|
101
|
-
client_cnt: room_clients.length
|
|
102
|
-
});
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
async onMessage(req, socket, message) {
|
|
108
|
-
try {
|
|
109
|
-
const organization_id = socket.config.organization_id
|
|
110
|
-
let { action, data } = JSON.parse(message)
|
|
111
|
-
|
|
112
|
-
if (action) {
|
|
113
|
-
this.emit("setBandwidth", {
|
|
114
|
-
type: 'in',
|
|
115
|
-
data: message,
|
|
116
|
-
organization_id
|
|
117
|
-
});
|
|
118
|
-
let user_id = null;
|
|
119
|
-
if (this.authenticate)
|
|
120
|
-
user_id = await this.authenticate.getUserId(req);
|
|
121
|
-
|
|
122
|
-
if (this.authorize) {
|
|
123
|
-
data.host = this.getHost(req)
|
|
124
|
-
const permission = await this.authorize.check(action, data, user_id)
|
|
125
|
-
if (permission.dbUrl === false) {
|
|
126
|
-
data.database = process.env.organization_id
|
|
127
|
-
data.organization_id = process.env.organization_id
|
|
128
|
-
|
|
129
|
-
const permission2 = await this.authorize.check(action, data, req, user_id)
|
|
130
|
-
if (!permission2 || permission2.error) {
|
|
131
|
-
return this.send(socket, 'Access Denied', { action, permission2, ...data })
|
|
132
|
-
}
|
|
133
|
-
} else if (!permission || permission.error) {
|
|
134
|
-
if (!user_id)
|
|
135
|
-
this.send(socket, 'updateUserStatus', { userStatus: 'off', clientId: data.clientId, organization_id })
|
|
136
|
-
|
|
137
|
-
return this.send(socket, 'Access Denied', { action, permission, ...data })
|
|
138
|
-
}
|
|
139
|
-
// dburl is true and db does not have 'keys' collection
|
|
140
|
-
// action: syncCollection data{collection: 'keys', document[]}
|
|
141
|
-
// actions: add keys as once keys are added admin user can do anything
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
// }
|
|
145
|
-
|
|
146
|
-
if (permission.authorized)
|
|
147
|
-
data = permission.authorized
|
|
148
|
-
|
|
149
|
-
if (user_id) {
|
|
150
|
-
if (!socket.config.user_id) {
|
|
151
|
-
socket.config.user_id = user_id
|
|
152
|
-
this.emit('userStatus', socket, { user_id, userStatus: 'on', organization_id });
|
|
153
|
-
}
|
|
154
|
-
} else {
|
|
155
|
-
this.send(socket, 'updateUserStatus', { userStatus: 'off', clientId: data.clientId, organization_id })
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
this.emit(action, socket, data);
|
|
159
|
-
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
} catch (e) {
|
|
163
|
-
console.log(e);
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
broadcast(socket, action, data) {
|
|
168
|
-
if (!data.uid)
|
|
169
|
-
data.uid = uid.generate()
|
|
170
|
-
|
|
171
|
-
let organization_id = socket.config.organization_id;
|
|
172
|
-
let url = `/${this.prefix}/${organization_id}`;
|
|
173
|
-
|
|
174
|
-
let namespace = data.namespace;
|
|
175
|
-
if (namespace) {
|
|
176
|
-
url += `/${namespace}`
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
let rooms = data.room;
|
|
181
|
-
if (rooms) {
|
|
182
|
-
if (!rooms.isArray())
|
|
183
|
-
rooms = [rooms]
|
|
184
|
-
for (let room of rooms) {
|
|
185
|
-
let url = url;
|
|
186
|
-
url += `/${room}`;
|
|
187
|
-
|
|
188
|
-
this.send(socket, action, data, url)
|
|
189
|
-
}
|
|
190
|
-
} else {
|
|
191
|
-
this.send(socket, action, data, url)
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
async send(socket, action, data, url) {
|
|
196
|
-
let clients
|
|
197
|
-
if (!url)
|
|
198
|
-
clients = [socket]
|
|
199
|
-
else
|
|
200
|
-
clients = this.clients.get(url);
|
|
201
|
-
|
|
202
|
-
if (clients) {
|
|
203
|
-
for (let client of clients) {
|
|
204
|
-
if (socket != client && data.broadcast != false || socket == client && data.broadcastSender != false) {
|
|
205
|
-
const permission = await this.authorize.check(action, data, socket.config.user_id)
|
|
206
|
-
if (permission && permission.authorized)
|
|
207
|
-
data = permission.authorized
|
|
208
|
-
|
|
209
|
-
let responseData = JSON.stringify({
|
|
210
|
-
action,
|
|
211
|
-
data
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
socket.send(responseData);
|
|
215
|
-
|
|
216
|
-
if (socket.config && socket.config.organization_id)
|
|
217
|
-
this.emit("setBandwidth", {
|
|
218
|
-
type: 'out',
|
|
219
|
-
data: responseData,
|
|
220
|
-
organization_id: socket.config.organization_id
|
|
221
|
-
})
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
getKeyFromUrl(pathname) {
|
|
229
|
-
var path = pathname.split("/");
|
|
230
|
-
var params = {
|
|
231
|
-
type: null,
|
|
232
|
-
key: pathname
|
|
233
|
-
}
|
|
234
|
-
if (path.length > 0) {
|
|
235
|
-
params.type = path[1];
|
|
236
|
-
params.organization_id = path[2]
|
|
237
|
-
}
|
|
238
|
-
return params
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
getHost(req) {
|
|
242
|
-
const headers = req['headers']
|
|
243
|
-
let origin = headers['origin'];
|
|
244
|
-
if (origin && origin !== 'null')
|
|
245
|
-
return url.parse(origin).hostname;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
module.exports = SocketServer
|
|
1
|
+
const WebSocket = require('ws');
|
|
2
|
+
const url = require("url");
|
|
3
|
+
const EventEmitter = require("events").EventEmitter;
|
|
4
|
+
const uid = require('@cocreate/uuid')
|
|
5
|
+
|
|
6
|
+
class SocketServer extends EventEmitter {
|
|
7
|
+
constructor(prefix) {
|
|
8
|
+
super();
|
|
9
|
+
this.clients = new Map();
|
|
10
|
+
this.prefix = prefix || "crud";
|
|
11
|
+
this.wss = new WebSocket.Server({ noServer: true });
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
handleUpgrade(req, socket, head) {
|
|
15
|
+
const self = this;
|
|
16
|
+
const pathname = url.parse(req.url).pathname;
|
|
17
|
+
const config = this.getKeyFromUrl(pathname)
|
|
18
|
+
if (config.type == this.prefix) {
|
|
19
|
+
self.wss.handleUpgrade(req, socket, head, function (socket) {
|
|
20
|
+
socket.config = config
|
|
21
|
+
self.onWebSocket(req, socket);
|
|
22
|
+
})
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
onWebSocket(req, socket) {
|
|
29
|
+
const self = this;
|
|
30
|
+
|
|
31
|
+
this.addClient(socket);
|
|
32
|
+
|
|
33
|
+
socket.on('message', async (message) => {
|
|
34
|
+
self.onMessage(req, socket, message);
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
socket.on('close', function () {
|
|
38
|
+
self.removeClient(socket)
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
socket.on("error", () => {
|
|
42
|
+
self.removeClient(socket)
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
this.send(socket, 'connect', socket.config.key);
|
|
46
|
+
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
addClient(socket) {
|
|
50
|
+
let organization_id = socket.config.organization_id
|
|
51
|
+
let user_id = socket.config.user_id
|
|
52
|
+
let key = socket.config.key
|
|
53
|
+
let room_clients = this.clients.get(key);
|
|
54
|
+
if (room_clients) {
|
|
55
|
+
room_clients.push(socket);
|
|
56
|
+
} else {
|
|
57
|
+
room_clients = [socket];
|
|
58
|
+
}
|
|
59
|
+
this.clients.set(key, room_clients);
|
|
60
|
+
|
|
61
|
+
if (user_id)
|
|
62
|
+
this.emit('userStatus', socket, { user_id, userStatus: 'on', organization_id });
|
|
63
|
+
|
|
64
|
+
//. add metrics
|
|
65
|
+
let total_cnt = 0;
|
|
66
|
+
this.clients.forEach((c) => total_cnt += c.length)
|
|
67
|
+
|
|
68
|
+
this.emit("createMetrics", {
|
|
69
|
+
organization_id,
|
|
70
|
+
client_cnt: room_clients.length,
|
|
71
|
+
total_cnt: total_cnt
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
removeClient(socket) {
|
|
76
|
+
let organization_id = socket.config.organization_id
|
|
77
|
+
let user_id = socket.config.user_id
|
|
78
|
+
let key = socket.config.key
|
|
79
|
+
let room_clients = this.clients.get(key)
|
|
80
|
+
const index = room_clients.indexOf(socket);
|
|
81
|
+
|
|
82
|
+
if (index > -1) {
|
|
83
|
+
room_clients.splice(index, 1);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (room_clients.length == 0) {
|
|
87
|
+
if (user_id)
|
|
88
|
+
this.emit('userStatus', socket, { user_id, status: 'off', organization_id });
|
|
89
|
+
|
|
90
|
+
// TODO: remove if no one else connected to organization
|
|
91
|
+
this.emit("deleteMetrics", { organization_id });
|
|
92
|
+
this.emit("deletePermissions", organization_id);
|
|
93
|
+
this.emit('disconnect', organization_id)
|
|
94
|
+
} else {
|
|
95
|
+
let total_cnt = 0;
|
|
96
|
+
this.clients.forEach((c) => total_cnt += c.length)
|
|
97
|
+
|
|
98
|
+
this.emit("changeCountMetrics", {
|
|
99
|
+
organization_id,
|
|
100
|
+
total_cnt,
|
|
101
|
+
client_cnt: room_clients.length
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
async onMessage(req, socket, message) {
|
|
108
|
+
try {
|
|
109
|
+
const organization_id = socket.config.organization_id
|
|
110
|
+
let { action, data } = JSON.parse(message)
|
|
111
|
+
|
|
112
|
+
if (action) {
|
|
113
|
+
this.emit("setBandwidth", {
|
|
114
|
+
type: 'in',
|
|
115
|
+
data: message,
|
|
116
|
+
organization_id
|
|
117
|
+
});
|
|
118
|
+
let user_id = null;
|
|
119
|
+
if (this.authenticate)
|
|
120
|
+
user_id = await this.authenticate.getUserId(req);
|
|
121
|
+
|
|
122
|
+
if (this.authorize) {
|
|
123
|
+
data.host = this.getHost(req)
|
|
124
|
+
const permission = await this.authorize.check(action, data, user_id)
|
|
125
|
+
if (permission.dbUrl === false) {
|
|
126
|
+
data.database = process.env.organization_id
|
|
127
|
+
data.organization_id = process.env.organization_id
|
|
128
|
+
|
|
129
|
+
const permission2 = await this.authorize.check(action, data, req, user_id)
|
|
130
|
+
if (!permission2 || permission2.error) {
|
|
131
|
+
return this.send(socket, 'Access Denied', { action, permission2, ...data })
|
|
132
|
+
}
|
|
133
|
+
} else if (!permission || permission.error) {
|
|
134
|
+
if (!user_id)
|
|
135
|
+
this.send(socket, 'updateUserStatus', { userStatus: 'off', clientId: data.clientId, organization_id })
|
|
136
|
+
|
|
137
|
+
return this.send(socket, 'Access Denied', { action, permission, ...data })
|
|
138
|
+
}
|
|
139
|
+
// dburl is true and db does not have 'keys' collection
|
|
140
|
+
// action: syncCollection data{collection: 'keys', document[]}
|
|
141
|
+
// actions: add keys as once keys are added admin user can do anything
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
// }
|
|
145
|
+
|
|
146
|
+
if (permission.authorized)
|
|
147
|
+
data = permission.authorized
|
|
148
|
+
|
|
149
|
+
if (user_id) {
|
|
150
|
+
if (!socket.config.user_id) {
|
|
151
|
+
socket.config.user_id = user_id
|
|
152
|
+
this.emit('userStatus', socket, { user_id, userStatus: 'on', organization_id });
|
|
153
|
+
}
|
|
154
|
+
} else {
|
|
155
|
+
this.send(socket, 'updateUserStatus', { userStatus: 'off', clientId: data.clientId, organization_id })
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
this.emit(action, socket, data);
|
|
159
|
+
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
} catch (e) {
|
|
163
|
+
console.log(e);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
broadcast(socket, action, data) {
|
|
168
|
+
if (!data.uid)
|
|
169
|
+
data.uid = uid.generate()
|
|
170
|
+
|
|
171
|
+
let organization_id = socket.config.organization_id;
|
|
172
|
+
let url = `/${this.prefix}/${organization_id}`;
|
|
173
|
+
|
|
174
|
+
let namespace = data.namespace;
|
|
175
|
+
if (namespace) {
|
|
176
|
+
url += `/${namespace}`
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
let rooms = data.room;
|
|
181
|
+
if (rooms) {
|
|
182
|
+
if (!rooms.isArray())
|
|
183
|
+
rooms = [rooms]
|
|
184
|
+
for (let room of rooms) {
|
|
185
|
+
let url = url;
|
|
186
|
+
url += `/${room}`;
|
|
187
|
+
|
|
188
|
+
this.send(socket, action, data, url)
|
|
189
|
+
}
|
|
190
|
+
} else {
|
|
191
|
+
this.send(socket, action, data, url)
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
async send(socket, action, data, url) {
|
|
196
|
+
let clients
|
|
197
|
+
if (!url)
|
|
198
|
+
clients = [socket]
|
|
199
|
+
else
|
|
200
|
+
clients = this.clients.get(url);
|
|
201
|
+
|
|
202
|
+
if (clients) {
|
|
203
|
+
for (let client of clients) {
|
|
204
|
+
if (socket != client && data.broadcast != false || socket == client && data.broadcastSender != false) {
|
|
205
|
+
const permission = await this.authorize.check(action, data, socket.config.user_id)
|
|
206
|
+
if (permission && permission.authorized)
|
|
207
|
+
data = permission.authorized
|
|
208
|
+
|
|
209
|
+
let responseData = JSON.stringify({
|
|
210
|
+
action,
|
|
211
|
+
data
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
socket.send(responseData);
|
|
215
|
+
|
|
216
|
+
if (socket.config && socket.config.organization_id)
|
|
217
|
+
this.emit("setBandwidth", {
|
|
218
|
+
type: 'out',
|
|
219
|
+
data: responseData,
|
|
220
|
+
organization_id: socket.config.organization_id
|
|
221
|
+
})
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
getKeyFromUrl(pathname) {
|
|
229
|
+
var path = pathname.split("/");
|
|
230
|
+
var params = {
|
|
231
|
+
type: null,
|
|
232
|
+
key: pathname
|
|
233
|
+
}
|
|
234
|
+
if (path.length > 0) {
|
|
235
|
+
params.type = path[1];
|
|
236
|
+
params.organization_id = path[2]
|
|
237
|
+
}
|
|
238
|
+
return params
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
getHost(req) {
|
|
242
|
+
const headers = req['headers']
|
|
243
|
+
let origin = headers['origin'];
|
|
244
|
+
if (origin && origin !== 'null')
|
|
245
|
+
return url.parse(origin).hostname;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
module.exports = SocketServer
|