kuzzle 2.14.10 → 2.14.14
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/.kuzzlerc.sample +6 -0
- package/lib/api/funnel.js +9 -5
- package/lib/cluster/node.js +5 -0
- package/lib/cluster/state.d.ts +2 -11
- package/lib/config/default.config.js +3 -1
- package/lib/config/index.js +6 -1
- package/lib/core/auth/tokenManager.d.ts +7 -0
- package/lib/core/auth/tokenManager.js +25 -8
- package/lib/core/network/router.js +0 -4
- package/lib/core/realtime/channel.d.ts +64 -0
- package/lib/core/realtime/channel.js +109 -0
- package/lib/core/realtime/connectionRooms.d.ts +28 -0
- package/lib/core/realtime/connectionRooms.js +68 -0
- package/lib/core/realtime/hotelClerk.d.ts +140 -0
- package/lib/core/realtime/hotelClerk.js +427 -630
- package/lib/core/realtime/index.js +1 -1
- package/lib/core/realtime/notifier.js +6 -6
- package/lib/core/realtime/room.d.ts +66 -0
- package/lib/core/realtime/room.js +103 -0
- package/lib/core/realtime/subscription.d.ts +25 -0
- package/lib/core/realtime/subscription.js +48 -0
- package/lib/core/statistics/statistics.js +43 -5
- package/lib/kerror/codes/1-services.json +11 -0
- package/lib/kerror/codes/2-api.json +1 -1
- package/lib/kuzzle/kuzzle.js +2 -2
- package/lib/types/KuzzleDocument.d.ts +5 -0
- package/lib/types/KuzzleDocument.js +3 -0
- package/lib/types/index.d.ts +4 -0
- package/lib/types/index.js +4 -0
- package/lib/types/realtime/RealtimeScope.d.ts +4 -0
- package/lib/types/realtime/RealtimeScope.js +3 -0
- package/lib/types/realtime/RealtimeUsers.d.ts +4 -0
- package/lib/types/realtime/RealtimeUsers.js +3 -0
- package/lib/types/realtime/RoomList.d.ts +20 -0
- package/lib/types/realtime/RoomList.js +3 -0
- package/package-lock.json +361 -152
- package/package.json +15 -15
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
"use strict";
|
|
1
2
|
/*
|
|
2
3
|
* Kuzzle, a backend software, self-hostable and ready to use
|
|
3
4
|
* to power modern apps
|
|
@@ -18,665 +19,461 @@
|
|
|
18
19
|
* See the License for the specific language governing permissions and
|
|
19
20
|
* limitations under the License.
|
|
20
21
|
*/
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
const
|
|
27
|
-
const
|
|
28
|
-
const
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
this.name = `${roomId}-${global.kuzzle.hash(this)}`;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
class Subscription {
|
|
57
|
-
constructor (index, collection, filters, roomId, connectionId, user) {
|
|
58
|
-
this.index = index;
|
|
59
|
-
this.collection = collection;
|
|
60
|
-
this.filters = filters;
|
|
61
|
-
this.roomId = roomId;
|
|
62
|
-
this.connectionId = connectionId;
|
|
63
|
-
this.kuid = user && user._id || null;
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
22
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
23
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.HotelClerk = void 0;
|
|
27
|
+
const bluebird_1 = __importDefault(require("bluebird"));
|
|
28
|
+
const request_1 = require("../../api/request");
|
|
29
|
+
const kerror_1 = __importDefault(require("../../kerror"));
|
|
30
|
+
const debug_1 = __importDefault(require("../../util/debug"));
|
|
31
|
+
const koncordeCompat_1 = require("../../util/koncordeCompat");
|
|
32
|
+
const channel_1 = require("./channel");
|
|
33
|
+
const connectionRooms_1 = require("./connectionRooms");
|
|
34
|
+
const room_1 = require("./room");
|
|
35
|
+
const subscription_1 = require("./subscription");
|
|
36
|
+
const realtimeError = kerror_1.default.wrap('core', 'realtime');
|
|
37
|
+
const debug = (0, debug_1.default)('kuzzle:realtime:hotelClerk');
|
|
38
|
+
/**
|
|
39
|
+
* The HotelClerk is responsible of keeping the list of rooms and subscriptions
|
|
40
|
+
* made to those rooms.
|
|
41
|
+
*
|
|
42
|
+
* When a subscription is made to a room, the HotelClerk link the connection
|
|
43
|
+
* to a channel of this room. Each channel represents a specific configuration
|
|
44
|
+
* about which kind of notification the subscriber should receive (e.g. scope in/out)
|
|
45
|
+
*
|
|
46
|
+
* When an user is subscribing, we send him back the channel he is subscribing to.
|
|
47
|
+
*
|
|
48
|
+
* Here stop the role of the HotelClerk, then the notifier will select the channels
|
|
49
|
+
* according to the notification and notify them.
|
|
50
|
+
*/
|
|
67
51
|
class HotelClerk {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
52
|
+
constructor(realtimeModule) {
|
|
53
|
+
/**
|
|
54
|
+
* Number of created rooms.
|
|
55
|
+
*
|
|
56
|
+
* Used with the "subscriptionRooms" configuration limit.
|
|
57
|
+
*/
|
|
58
|
+
this.roomsCount = 0;
|
|
59
|
+
/**
|
|
60
|
+
* Current realtime rooms.
|
|
61
|
+
*
|
|
62
|
+
* This object is used by the notifier to list wich channel has to be notified
|
|
63
|
+
* when a subscription scope is matching.
|
|
64
|
+
* It's also used to notify channels when an user join/exit a room.
|
|
65
|
+
*
|
|
66
|
+
* Map<roomId, Room>
|
|
67
|
+
*/
|
|
68
|
+
this.rooms = new Map();
|
|
69
|
+
/**
|
|
70
|
+
* Current subscribing connections handled by the HotelClerk.
|
|
71
|
+
*
|
|
72
|
+
* Each connection can subscribe to many rooms with different volatile data.
|
|
73
|
+
*
|
|
74
|
+
* This object is used to keep track of all subscriptions made by a connection
|
|
75
|
+
* to be able to unsubscribe when a connection is removed.
|
|
76
|
+
*
|
|
77
|
+
* Map<connectionId, ConnectionRooms>
|
|
78
|
+
*/
|
|
79
|
+
this.subscriptions = new Map();
|
|
80
|
+
this.module = realtimeModule;
|
|
81
|
+
this.koncorde = global.kuzzle.koncorde;
|
|
82
|
+
}
|
|
71
83
|
/**
|
|
72
|
-
*
|
|
73
|
-
* configuration limit
|
|
84
|
+
* Registers the ask events.
|
|
74
85
|
*/
|
|
75
|
-
|
|
76
|
-
|
|
86
|
+
async init() {
|
|
87
|
+
/**
|
|
88
|
+
* Create a new, empty room.
|
|
89
|
+
* @param {string} index
|
|
90
|
+
* @param {string} collection
|
|
91
|
+
* @param {string} roomId
|
|
92
|
+
* @returns {boolean} status indicating if the room was created or not
|
|
93
|
+
*/
|
|
94
|
+
global.kuzzle.onAsk('core:realtime:room:create', (index, collection, roomId) => this.newRoom(index, collection, roomId));
|
|
95
|
+
/**
|
|
96
|
+
* Joins an existing room.
|
|
97
|
+
* @param {Request} request
|
|
98
|
+
* @returns {Promise}
|
|
99
|
+
*/
|
|
100
|
+
global.kuzzle.onAsk('core:realtime:join', request => this.join(request));
|
|
101
|
+
/**
|
|
102
|
+
* Return the list of index, collection, rooms (+ their users count)
|
|
103
|
+
* on all index/collection pairs that the requesting user is allowed to
|
|
104
|
+
* subscribe
|
|
105
|
+
*
|
|
106
|
+
* @param {User} user
|
|
107
|
+
* @return {number}
|
|
108
|
+
* @throws {NotFoundError} If the roomId does not exist
|
|
109
|
+
*/
|
|
110
|
+
global.kuzzle.onAsk('core:realtime:list', user => this.list(user));
|
|
111
|
+
/**
|
|
112
|
+
* Given an index, returns the list of collections having subscriptions
|
|
113
|
+
* on them.
|
|
114
|
+
* @param {string} index
|
|
115
|
+
* @return {Array.<string>}
|
|
116
|
+
*/
|
|
117
|
+
global.kuzzle.onAsk('core:realtime:collections:get', index => this.listCollections(index));
|
|
118
|
+
/**
|
|
119
|
+
* Removes a user and all their subscriptions.
|
|
120
|
+
* @param {string} connectionId
|
|
121
|
+
*/
|
|
122
|
+
global.kuzzle.onAsk('core:realtime:connection:remove', connectionId => this.removeConnection(connectionId));
|
|
123
|
+
/**
|
|
124
|
+
* Adds a new user subscription
|
|
125
|
+
* @param {Request} request
|
|
126
|
+
* @return {Object|null}
|
|
127
|
+
*/
|
|
128
|
+
global.kuzzle.onAsk('core:realtime:subscribe', request => this.subscribe(request));
|
|
129
|
+
/**
|
|
130
|
+
* Unsubscribes a user from a room
|
|
131
|
+
* @param {string} connectionId
|
|
132
|
+
* @param {string} roomId
|
|
133
|
+
* @param {string} kuid
|
|
134
|
+
* @param {boolean} [notify]
|
|
135
|
+
*/
|
|
136
|
+
global.kuzzle.onAsk('core:realtime:unsubscribe', (connectionId, roomId, notify) => {
|
|
137
|
+
return this.unsubscribe(connectionId, roomId, notify);
|
|
138
|
+
});
|
|
139
|
+
/**
|
|
140
|
+
* Clear the hotel clerk and properly disconnect connections.
|
|
141
|
+
*/
|
|
142
|
+
global.kuzzle.on('kuzzle:shutdown', () => this.clearConnections());
|
|
143
|
+
/**
|
|
144
|
+
* Clear subscriptions when a connection is dropped
|
|
145
|
+
*/
|
|
146
|
+
global.kuzzle.on('connection:remove', connection => {
|
|
147
|
+
this.removeConnection(connection.id)
|
|
148
|
+
.catch(err => global.kuzzle.log.info(err));
|
|
149
|
+
});
|
|
150
|
+
}
|
|
77
151
|
/**
|
|
78
|
-
*
|
|
79
|
-
* users have subscribed to it
|
|
80
|
-
*
|
|
81
|
-
* Example: subscribing to a chat room where the subject is Kuzzle
|
|
82
|
-
* rooms = Map<roomId, room>
|
|
83
|
-
*
|
|
84
|
-
* Where:
|
|
85
|
-
* - the room ID is the filter ID (e.g. 'f45de4d8ef4f3ze4ffzer85d4fgkzm41')
|
|
86
|
-
* - room is an object with the following properties:
|
|
87
|
-
* {
|
|
88
|
-
* // list of users subscribing to this room
|
|
89
|
-
* customers: Set([ 'connectionId' ]),
|
|
90
|
-
*
|
|
91
|
-
* // room channels
|
|
92
|
-
* channels: {
|
|
93
|
-
*
|
|
94
|
-
* // channel ID
|
|
95
|
-
* 'roomId-<configurationHash>': {
|
|
96
|
-
*
|
|
97
|
-
* // request scope filter, default: 'all'
|
|
98
|
-
* scope: 'all|in|out|none',
|
|
152
|
+
* Subscribe a connection to a realtime room.
|
|
99
153
|
*
|
|
100
|
-
*
|
|
101
|
-
* users: 'all|in|out|none',
|
|
154
|
+
* The room will be created if it does not already exists.
|
|
102
155
|
*
|
|
103
|
-
*
|
|
104
|
-
* // (used for plugin subscriptions)
|
|
105
|
-
* cluster: true|false
|
|
106
|
-
* }
|
|
107
|
-
* },
|
|
108
|
-
* index: 'index',
|
|
109
|
-
* collection: 'collection',
|
|
110
|
-
* // the room unique identifier
|
|
111
|
-
* id: 'id',
|
|
112
|
-
* }
|
|
113
|
-
* }
|
|
114
|
-
*/
|
|
115
|
-
this.rooms = new Map();
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* In addition to this.rooms, this.customers allows managing users and their rooms
|
|
119
|
-
* Example for a customer who subscribes to the room 'chat-room-kuzzle'
|
|
120
|
-
* customers = Map.<connection id, customer rooms>
|
|
156
|
+
* Notify other subscribers on this room about this new subscription
|
|
121
157
|
*
|
|
122
|
-
*
|
|
123
|
-
*
|
|
158
|
+
* @throws Throws if the user has already subscribed to this room name
|
|
159
|
+
* (just for rooms with same name, there is no error if the room
|
|
160
|
+
* has a different name with same filter) or if there is an error
|
|
161
|
+
* during room creation
|
|
124
162
|
*/
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
163
|
+
async subscribe(request) {
|
|
164
|
+
const { index, collection } = request.input.resource;
|
|
165
|
+
if (!index) {
|
|
166
|
+
return kerror_1.default.reject('api', 'assert', 'missing_argument', 'index');
|
|
167
|
+
}
|
|
168
|
+
if (!collection) {
|
|
169
|
+
return kerror_1.default.reject('api', 'assert', 'missing_argument', 'collection');
|
|
170
|
+
}
|
|
171
|
+
/*
|
|
172
|
+
* /!\ This check is a duplicate to the one already made by the
|
|
173
|
+
* funnel controller. THIS IS INTENTIONAL.
|
|
174
|
+
*
|
|
175
|
+
* This is to prevent subscriptions to be made on dead
|
|
176
|
+
* connections. And between the funnel and here, there is
|
|
177
|
+
* time for a connection to drop, so while the check
|
|
178
|
+
* on the funnel is useful for many use cases, this one
|
|
179
|
+
* is made on the very last moment and is essential to ensure
|
|
180
|
+
* that no zombie subscription can be performed
|
|
181
|
+
*/
|
|
182
|
+
if (!global.kuzzle.router.isConnectionAlive(request.context)) {
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
185
|
+
let normalized;
|
|
186
|
+
try {
|
|
187
|
+
normalized = this.koncorde.normalize(request.input.body, (0, koncordeCompat_1.toKoncordeIndex)(index, collection));
|
|
188
|
+
}
|
|
189
|
+
catch (e) {
|
|
190
|
+
throw kerror_1.default.get('api', 'assert', 'koncorde_dsl_error', e.message);
|
|
191
|
+
}
|
|
192
|
+
this.createRoom(normalized);
|
|
193
|
+
const { channel, subscribed } = await this.subscribeToRoom(normalized.id, request);
|
|
194
|
+
if (subscribed) {
|
|
195
|
+
global.kuzzle.emit('core:realtime:subscribe:after', normalized.id);
|
|
196
|
+
// @deprecated -- to be removed in next major version
|
|
197
|
+
// we have to recreate the old "diff" object
|
|
198
|
+
await global.kuzzle.pipe('core:hotelClerk:addSubscription', {
|
|
199
|
+
changed: subscribed,
|
|
200
|
+
collection,
|
|
201
|
+
connectionId: request.context.connection.id,
|
|
202
|
+
filters: normalized.filter,
|
|
203
|
+
index,
|
|
204
|
+
roomId: normalized.id,
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
const subscription = new subscription_1.Subscription(index, collection, request.input.body, normalized.id, request.context.connection.id, request.context.user);
|
|
208
|
+
global.kuzzle.emit('core:realtime:user:subscribe:after', subscription);
|
|
209
|
+
return {
|
|
210
|
+
channel,
|
|
211
|
+
roomId: normalized.id,
|
|
212
|
+
};
|
|
213
|
+
}
|
|
129
214
|
/**
|
|
130
|
-
*
|
|
131
|
-
* @param {string} index
|
|
132
|
-
* @param {string} collection
|
|
133
|
-
* @param {string} roomId
|
|
134
|
-
* @returns {boolean} status indicating if the room was created or not
|
|
215
|
+
* Returns the list of collections of an index with realtime rooms.
|
|
135
216
|
*/
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
217
|
+
listCollections(index) {
|
|
218
|
+
return (0, koncordeCompat_1.getCollections)(this.koncorde, index);
|
|
219
|
+
}
|
|
140
220
|
/**
|
|
141
|
-
* Joins an existing room.
|
|
142
|
-
*
|
|
143
|
-
*
|
|
221
|
+
* Joins an existing realtime room.
|
|
222
|
+
*
|
|
223
|
+
* The room may exists on another cluster node, if it's the case, the normalized
|
|
224
|
+
* filters will be fetched from the cluster.
|
|
144
225
|
*/
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
226
|
+
async join(request) {
|
|
227
|
+
const roomId = request.input.body.roomId;
|
|
228
|
+
if (!this.rooms.has(roomId)) {
|
|
229
|
+
const normalized = await global.kuzzle.ask('cluster:realtime:filters:get', roomId);
|
|
230
|
+
if (!normalized) {
|
|
231
|
+
throw realtimeError.get('room_not_found', roomId);
|
|
232
|
+
}
|
|
233
|
+
this.createRoom(normalized);
|
|
234
|
+
}
|
|
235
|
+
const { channel, cluster, subscribed } = await this.subscribeToRoom(roomId, request);
|
|
236
|
+
if (cluster && subscribed) {
|
|
237
|
+
global.kuzzle.emit('core:realtime:subscribe:after', roomId);
|
|
238
|
+
}
|
|
239
|
+
return {
|
|
240
|
+
channel,
|
|
241
|
+
roomId,
|
|
242
|
+
};
|
|
243
|
+
}
|
|
148
244
|
/**
|
|
149
|
-
* Return the list of index, collection, rooms
|
|
245
|
+
* Return the list of index, collection, rooms and subscribing connections
|
|
150
246
|
* on all index/collection pairs that the requesting user is allowed to
|
|
151
|
-
* subscribe
|
|
152
|
-
*
|
|
153
|
-
* @param {User} user
|
|
154
|
-
* @return {number}
|
|
155
|
-
* @throws {NotFoundError} If the roomId does not exist
|
|
247
|
+
* subscribe.
|
|
156
248
|
*/
|
|
157
|
-
|
|
158
|
-
|
|
249
|
+
async list(user) {
|
|
250
|
+
// We need the room list from the cluster's full state, NOT the one stored
|
|
251
|
+
// in Koncorde: the latter also contains subscriptions created by the
|
|
252
|
+
// framework (or by plugins), and we don't want those to appear in the API
|
|
253
|
+
const fullStateRooms = await global.kuzzle.ask('cluster:realtime:room:list');
|
|
254
|
+
const isAllowedRequest = new request_1.KuzzleRequest({
|
|
255
|
+
action: 'subscribe',
|
|
256
|
+
controller: 'realtime',
|
|
257
|
+
}, {});
|
|
258
|
+
for (const [index, collections] of Object.entries(fullStateRooms)) {
|
|
259
|
+
isAllowedRequest.input.resource.index = index;
|
|
260
|
+
const toRemove = await bluebird_1.default.filter(Object.keys(collections), collection => {
|
|
261
|
+
isAllowedRequest.input.resource.collection = collection;
|
|
262
|
+
return !user.isActionAllowed(isAllowedRequest);
|
|
263
|
+
});
|
|
264
|
+
for (const collection of toRemove) {
|
|
265
|
+
delete fullStateRooms[index][collection];
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
return fullStateRooms;
|
|
269
|
+
}
|
|
159
270
|
/**
|
|
160
|
-
*
|
|
161
|
-
*
|
|
162
|
-
*
|
|
163
|
-
* @return {Array.<string>}
|
|
271
|
+
* Removes a connections and unsubscribe it from every subscribed rooms.
|
|
272
|
+
*
|
|
273
|
+
* Usually called when an user has been disconnected from Kuzzle.
|
|
164
274
|
*/
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
275
|
+
async removeConnection(connectionId, notify = true) {
|
|
276
|
+
const connectionRooms = this.subscriptions.get(connectionId);
|
|
277
|
+
if (!connectionRooms) {
|
|
278
|
+
// No need to raise an error if the connection does not have room subscriptions
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
await bluebird_1.default.map(connectionRooms.roomIds, (roomId) => (this.unsubscribe(connectionId, roomId, notify).catch(global.kuzzle.log.error)));
|
|
282
|
+
}
|
|
169
283
|
/**
|
|
170
|
-
*
|
|
171
|
-
*
|
|
284
|
+
* Clear all connections made to this node:
|
|
285
|
+
* - trigger appropriate core events
|
|
286
|
+
* - send user exit room notifications
|
|
172
287
|
*/
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
288
|
+
async clearConnections() {
|
|
289
|
+
await bluebird_1.default.map(this.subscriptions.keys(), (connectionId) => (this.removeConnection(connectionId, false)));
|
|
290
|
+
}
|
|
177
291
|
/**
|
|
178
|
-
*
|
|
179
|
-
*
|
|
180
|
-
*
|
|
292
|
+
* Register a new subscription
|
|
293
|
+
* - save the subscription on the provided room with volatile data
|
|
294
|
+
* - add the connection to the list of active connections of the room
|
|
181
295
|
*/
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
296
|
+
registerSubscription(connectionId, roomId, volatile) {
|
|
297
|
+
debug('Add room %s for connection %s', roomId, connectionId);
|
|
298
|
+
let connectionRooms = this.subscriptions.get(connectionId);
|
|
299
|
+
if (!connectionRooms) {
|
|
300
|
+
connectionRooms = new connectionRooms_1.ConnectionRooms();
|
|
301
|
+
this.subscriptions.set(connectionId, connectionRooms);
|
|
302
|
+
}
|
|
303
|
+
connectionRooms.addRoom(roomId, volatile);
|
|
304
|
+
this.rooms.get(roomId).addConnection(connectionId);
|
|
305
|
+
}
|
|
186
306
|
/**
|
|
187
|
-
*
|
|
188
|
-
*
|
|
189
|
-
* @
|
|
190
|
-
* @param {string} kuid
|
|
191
|
-
* @param {boolean} [notify]
|
|
307
|
+
* Create new room if needed
|
|
308
|
+
*
|
|
309
|
+
* @returns {void}
|
|
192
310
|
*/
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
311
|
+
createRoom(normalized) {
|
|
312
|
+
const { index: koncordeIndex, id: roomId } = normalized;
|
|
313
|
+
const { index, collection } = (0, koncordeCompat_1.fromKoncordeIndex)(koncordeIndex);
|
|
314
|
+
if (this.rooms.has(normalized.id)) {
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
const roomsLimit = global.kuzzle.config.limits.subscriptionRooms;
|
|
318
|
+
if (roomsLimit > 0 && this.roomsCount >= roomsLimit) {
|
|
319
|
+
throw realtimeError.get('too_many_rooms');
|
|
320
|
+
}
|
|
321
|
+
this.koncorde.store(normalized);
|
|
322
|
+
global.kuzzle.emit('core:realtime:room:create:after', normalized);
|
|
323
|
+
// @deprecated -- to be removed in the next major version of kuzzle
|
|
324
|
+
global.kuzzle.emit('room:new', { collection, index, roomId });
|
|
325
|
+
/*
|
|
326
|
+
In some very rare cases, the room may have been created between
|
|
327
|
+
the beginning of the function executed at the end of normalize,
|
|
328
|
+
and this one
|
|
329
|
+
|
|
330
|
+
Before incrementing the rooms count, we have to make sure this
|
|
331
|
+
is not the case to ensure our counter is right
|
|
332
|
+
*/
|
|
333
|
+
if (this.newRoom(index, collection, roomId)) {
|
|
334
|
+
this.roomsCount++;
|
|
335
|
+
}
|
|
217
336
|
}
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
/*
|
|
224
|
-
* /!\ This check is a duplicate to the one already made by the
|
|
225
|
-
* funnel controller. THIS IS INTENTIONAL.
|
|
337
|
+
/**
|
|
338
|
+
* Remove a connection from a room.
|
|
339
|
+
*
|
|
340
|
+
* Also delete the rooms if it was the last connection subscribing to it.
|
|
226
341
|
*
|
|
227
|
-
* This is to prevent subscriptions to be made on dead
|
|
228
|
-
* connections. And between the funnel and here, there is
|
|
229
|
-
* time for a connection to drop, so while the check
|
|
230
|
-
* on the funnel is useful for many use cases, this one
|
|
231
|
-
* is made on the very last moment and is essential to ensure
|
|
232
|
-
* that no zombie subscription can be performed
|
|
233
342
|
*/
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
async join (request) {
|
|
303
|
-
const roomId = request.input.body.roomId;
|
|
304
|
-
|
|
305
|
-
if (! this.rooms.has(roomId)) {
|
|
306
|
-
const normalized = await global.kuzzle.ask(
|
|
307
|
-
'cluster:realtime:filters:get',
|
|
308
|
-
roomId);
|
|
309
|
-
|
|
310
|
-
if (!normalized) {
|
|
311
|
-
throw realtimeError.get('room_not_found', roomId);
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
this._createRoom(normalized);
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
const response = await this._subscribeToRoom(roomId, request);
|
|
318
|
-
|
|
319
|
-
if (response.cluster && response.subscribed) {
|
|
320
|
-
global.kuzzle.emit('core:realtime:subscribe:after', roomId);
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
return {
|
|
324
|
-
channel: response.channel,
|
|
325
|
-
roomId,
|
|
326
|
-
};
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
/**
|
|
330
|
-
* Return the list of index, collection, rooms (+ their number of subscribers)
|
|
331
|
-
* on all index/collection pairs that the requesting user is allowed to
|
|
332
|
-
* subscribe
|
|
333
|
-
*
|
|
334
|
-
* Returned object looks like this:
|
|
335
|
-
* {
|
|
336
|
-
* <index>: {
|
|
337
|
-
* <collection>: {
|
|
338
|
-
* <roomId>: <number of subscribers>
|
|
339
|
-
* }
|
|
340
|
-
* }
|
|
341
|
-
* }
|
|
342
|
-
*
|
|
343
|
-
* @param {User} user
|
|
344
|
-
* @returns {Promise.<Object>} resolve an object listing all rooms subscribed
|
|
345
|
-
* by the connected user
|
|
346
|
-
*/
|
|
347
|
-
async list (user) {
|
|
348
|
-
// We need the room list from the cluster's full state, NOT the one stored
|
|
349
|
-
// in Koncorde: the latter also contains subscriptions created by the
|
|
350
|
-
// framework (or by plugins), and we don't want those to appear in the API
|
|
351
|
-
const list = await global.kuzzle.ask('cluster:realtime:room:list');
|
|
352
|
-
|
|
353
|
-
const isAllowedRequest = new Request({
|
|
354
|
-
action: 'subscribe',
|
|
355
|
-
controller: 'realtime',
|
|
356
|
-
});
|
|
357
|
-
|
|
358
|
-
for (const index of Object.keys(list)) {
|
|
359
|
-
isAllowedRequest.input.resource.index = index;
|
|
360
|
-
|
|
361
|
-
const toRemove = await Bluebird.filter(
|
|
362
|
-
Object.keys(list[index]),
|
|
363
|
-
collection => {
|
|
364
|
-
isAllowedRequest.input.resource.collection = collection;
|
|
365
|
-
|
|
366
|
-
return !user.isActionAllowed(isAllowedRequest);
|
|
343
|
+
async unsubscribe(connectionId, roomId, notify = true) {
|
|
344
|
+
const connectionRooms = this.subscriptions.get(connectionId);
|
|
345
|
+
const requestContext = new request_1.RequestContext({
|
|
346
|
+
connection: { id: connectionId }
|
|
347
|
+
});
|
|
348
|
+
if (!connectionRooms) {
|
|
349
|
+
throw realtimeError.get('not_subscribed', connectionId, roomId);
|
|
350
|
+
}
|
|
351
|
+
const volatile = connectionRooms.getVolatile(roomId);
|
|
352
|
+
if (volatile === undefined) {
|
|
353
|
+
throw realtimeError.get('not_subscribed', connectionId, roomId);
|
|
354
|
+
}
|
|
355
|
+
if (connectionRooms.count > 1) {
|
|
356
|
+
connectionRooms.removeRoom(roomId);
|
|
357
|
+
}
|
|
358
|
+
else {
|
|
359
|
+
this.subscriptions.delete(connectionId);
|
|
360
|
+
}
|
|
361
|
+
const room = this.rooms.get(roomId);
|
|
362
|
+
if (!room) {
|
|
363
|
+
global.kuzzle.log.error(`Cannot remove room "${roomId}": room not found`);
|
|
364
|
+
throw realtimeError.get('room_not_found', roomId);
|
|
365
|
+
}
|
|
366
|
+
for (const channel of Object.keys(room.channels)) {
|
|
367
|
+
global.kuzzle.entryPoint.leaveChannel(channel, connectionId);
|
|
368
|
+
}
|
|
369
|
+
room.removeConnection(connectionId);
|
|
370
|
+
if (room.size === 0) {
|
|
371
|
+
await this.removeRoom(roomId);
|
|
372
|
+
}
|
|
373
|
+
// even if the room is deleted for this node, another one may need the
|
|
374
|
+
// notification
|
|
375
|
+
const request = new request_1.Request({
|
|
376
|
+
action: 'unsubscribe',
|
|
377
|
+
collection: room.collection,
|
|
378
|
+
controller: 'realtime',
|
|
379
|
+
index: room.index,
|
|
380
|
+
volatile,
|
|
381
|
+
}, requestContext);
|
|
382
|
+
await this.module.notifier.notifyUser(roomId, request, 'out', { count: room.size });
|
|
383
|
+
// Do not send an unsubscription notification if the room has been destroyed
|
|
384
|
+
// @aschen Why ?
|
|
385
|
+
if (notify
|
|
386
|
+
&& this.rooms.has(roomId)
|
|
387
|
+
&& room.channels.size > 0) {
|
|
388
|
+
await global.kuzzle.pipe('core:realtime:unsubscribe:after', roomId);
|
|
389
|
+
// @deprecated -- to be removed in next major version
|
|
390
|
+
await global.kuzzle.pipe('core:hotelClerk:removeRoomForCustomer', {
|
|
391
|
+
requestContext,
|
|
392
|
+
room: {
|
|
393
|
+
collection: room.collection,
|
|
394
|
+
id: roomId,
|
|
395
|
+
index: room.index,
|
|
396
|
+
},
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
const kuid = global.kuzzle.tokenManager.getKuidFromConnection(connectionId);
|
|
400
|
+
const subscription = new subscription_1.Subscription(room.index, room.collection, undefined, roomId, connectionId, { _id: kuid });
|
|
401
|
+
global.kuzzle.emit('core:realtime:user:unsubscribe:after', {
|
|
402
|
+
/* @deprecated */
|
|
403
|
+
requestContext,
|
|
404
|
+
/* @deprecated */
|
|
405
|
+
room: {
|
|
406
|
+
collection: room.collection,
|
|
407
|
+
id: roomId,
|
|
408
|
+
index: room.index,
|
|
409
|
+
},
|
|
410
|
+
subscription,
|
|
367
411
|
});
|
|
368
|
-
|
|
369
|
-
for (const collection of toRemove) {
|
|
370
|
-
delete list[index][collection];
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
return list;
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
/**
|
|
378
|
-
* This function will delete a user from this.customers, and
|
|
379
|
-
* decrement the subscribers count in all rooms where he has subscribed to
|
|
380
|
-
* Usually called on a user disconnection event
|
|
381
|
-
*
|
|
382
|
-
* @param {string} connectionId
|
|
383
|
-
*/
|
|
384
|
-
async removeUser (connectionId) {
|
|
385
|
-
const customer = this.customers.get(connectionId);
|
|
386
|
-
|
|
387
|
-
if (!customer) {
|
|
388
|
-
// No need to raise an error if the connection has already been cleaned up
|
|
389
|
-
return;
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
await Bluebird.map(customer.keys(), roomId => {
|
|
393
|
-
return this.unsubscribe(connectionId, roomId)
|
|
394
|
-
.catch(err => global.kuzzle.log.error(err));
|
|
395
|
-
});
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
/**
|
|
399
|
-
* Associate the room to the connection id in this.clients
|
|
400
|
-
* Allow to manage later disconnection and delete socket/rooms/...
|
|
401
|
-
*
|
|
402
|
-
* @param {string} connectionId
|
|
403
|
-
* @param {string} roomId
|
|
404
|
-
* @param {object} volatile
|
|
405
|
-
*/
|
|
406
|
-
_addRoomForCustomer (connectionId, roomId, volatile) {
|
|
407
|
-
debug('Add room %s for customer %s', roomId, connectionId);
|
|
408
|
-
|
|
409
|
-
let customer = this.customers.get(connectionId);
|
|
410
|
-
|
|
411
|
-
if (! customer) {
|
|
412
|
-
customer = new Map();
|
|
413
|
-
this.customers.set(connectionId, customer);
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
this.rooms.get(roomId).customers.add(connectionId);
|
|
417
|
-
customer.set(roomId, volatile);
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
/**
|
|
421
|
-
* Create new room if needed
|
|
422
|
-
*
|
|
423
|
-
* @this HotelClerk
|
|
424
|
-
*
|
|
425
|
-
* @param {NormalizedFilter} normalized - Obtained with Koncorde.normalize
|
|
426
|
-
* @param {Object} [options]
|
|
427
|
-
*
|
|
428
|
-
* @returns {void}
|
|
429
|
-
*/
|
|
430
|
-
_createRoom (normalized) {
|
|
431
|
-
const { index: koncordeIndex, id: roomId } = normalized;
|
|
432
|
-
const [index, collection] = koncordeIndex.split('/');
|
|
433
|
-
|
|
434
|
-
if (this.rooms.has(normalized.id)) {
|
|
435
|
-
return;
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
const roomsLimit = global.kuzzle.config.limits.subscriptionRooms;
|
|
439
|
-
|
|
440
|
-
if ( roomsLimit > 0 && this.roomsCount >= roomsLimit ) {
|
|
441
|
-
throw realtimeError.get('too_many_rooms');
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
global.kuzzle.koncorde.store(normalized);
|
|
445
|
-
|
|
446
|
-
global.kuzzle.emit('core:realtime:room:create:after', normalized);
|
|
447
|
-
|
|
448
|
-
// @deprecated -- to be removed in the next major version of kuzzle
|
|
449
|
-
global.kuzzle.emit('room:new', { collection, index, roomId });
|
|
450
|
-
|
|
451
|
-
/*
|
|
452
|
-
In some very rare cases, the room may have been created between
|
|
453
|
-
the beginning of the function executed at the end of normalize,
|
|
454
|
-
and this one
|
|
455
|
-
|
|
456
|
-
Before incrementing the rooms count, we have to make sure this
|
|
457
|
-
is not the case to ensure our counter is right
|
|
458
|
-
*/
|
|
459
|
-
if (this.newRoom(index, collection, roomId)) {
|
|
460
|
-
this.roomsCount++;
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
/**
|
|
465
|
-
* Remove the room from subscribed room from the user
|
|
466
|
-
* Return the roomId in user mapping
|
|
467
|
-
*
|
|
468
|
-
* @this HotelClerk
|
|
469
|
-
* @param {string} connectionId
|
|
470
|
-
* @param {string} roomId
|
|
471
|
-
* @param {Boolean} [notify]
|
|
472
|
-
* @returns {Promise}
|
|
473
|
-
*/
|
|
474
|
-
async unsubscribe (connectionId, roomId, notify = true) {
|
|
475
|
-
const customer = this.customers.get(connectionId);
|
|
476
|
-
const requestContext = new RequestContext({
|
|
477
|
-
connection: { id: connectionId }
|
|
478
|
-
});
|
|
479
|
-
|
|
480
|
-
if (! customer) {
|
|
481
|
-
throw realtimeError.get('not_subscribed', connectionId, roomId);
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
const volatile = customer.get(roomId);
|
|
485
|
-
|
|
486
|
-
if (volatile === undefined) {
|
|
487
|
-
throw realtimeError.get('not_subscribed', connectionId, roomId);
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
if (customer.size > 1) {
|
|
491
|
-
customer.delete(roomId);
|
|
492
|
-
}
|
|
493
|
-
else {
|
|
494
|
-
this.customers.delete(connectionId);
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
const room = this.rooms.get(roomId);
|
|
498
|
-
|
|
499
|
-
if (! room) {
|
|
500
|
-
global.kuzzle.log.error(`[hotelClerk] Cannot remove room "${roomId}": room not found`);
|
|
501
|
-
throw realtimeError.get('room_not_found', roomId);
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
for (const channel of Object.keys(room.channels)) {
|
|
505
|
-
global.kuzzle.entryPoint.leaveChannel(channel, connectionId);
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
if (room.customers.size === 1) {
|
|
509
|
-
this.roomsCount--;
|
|
510
|
-
this.rooms.delete(roomId);
|
|
511
|
-
|
|
512
|
-
await this._removeRoomFromRealtimeEngine(roomId);
|
|
513
|
-
|
|
514
|
-
room.customers = new Set();
|
|
515
|
-
}
|
|
516
|
-
else {
|
|
517
|
-
room.customers.delete(connectionId);
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
// even if the room is deleted for this node, another one may need the
|
|
521
|
-
// notification
|
|
522
|
-
const request = new Request(
|
|
523
|
-
{
|
|
524
|
-
action: 'unsubscribe',
|
|
525
|
-
collection: room.collection,
|
|
526
|
-
controller: 'realtime',
|
|
527
|
-
index: room.index,
|
|
528
|
-
volatile,
|
|
529
|
-
},
|
|
530
|
-
requestContext);
|
|
531
|
-
|
|
532
|
-
await this.module.notifier.notifyUser(roomId, request, 'out', {
|
|
533
|
-
count: room.customers.size
|
|
534
|
-
});
|
|
535
|
-
|
|
536
|
-
// Do not send an unsubscription notification if the room has been destroyed
|
|
537
|
-
if ( notify
|
|
538
|
-
&& this.rooms.has(roomId)
|
|
539
|
-
&& Object.keys(room.channels).length > 0
|
|
540
|
-
) {
|
|
541
|
-
await global.kuzzle.pipe('core:realtime:unsubscribe:after', roomId);
|
|
542
|
-
|
|
543
|
-
// @deprecated -- to be removed in next major version
|
|
544
|
-
await global.kuzzle.pipe('core:hotelClerk:removeRoomForCustomer', {
|
|
545
|
-
requestContext,
|
|
546
|
-
room: {
|
|
547
|
-
collection: room.collection,
|
|
548
|
-
id: roomId,
|
|
549
|
-
index: room.index,
|
|
550
|
-
},
|
|
551
|
-
});
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
const kuid = global.kuzzle.tokenManager.getKuidFromConnection(connectionId);
|
|
555
|
-
|
|
556
|
-
const subscription = new Subscription(
|
|
557
|
-
room.index,
|
|
558
|
-
room.collection,
|
|
559
|
-
undefined,
|
|
560
|
-
roomId,
|
|
561
|
-
connectionId,
|
|
562
|
-
{ _id: kuid });
|
|
563
|
-
|
|
564
|
-
global.kuzzle.emit('core:realtime:user:unsubscribe:after', {
|
|
565
|
-
/* @deprecated */
|
|
566
|
-
requestContext,
|
|
567
|
-
/* @deprecated */
|
|
568
|
-
room: {
|
|
569
|
-
collection: room.collection,
|
|
570
|
-
id: roomId,
|
|
571
|
-
index: room.index,
|
|
572
|
-
},
|
|
573
|
-
subscription,
|
|
574
|
-
});
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
/**
|
|
578
|
-
* Deletes a room if no user has subscribed to it, and removes it also from the
|
|
579
|
-
* real-time engine
|
|
580
|
-
*
|
|
581
|
-
* @param {string} roomId
|
|
582
|
-
*/
|
|
583
|
-
async _removeRoomFromRealtimeEngine (roomId) {
|
|
584
|
-
// @deprecated -- to be removed in the next major version
|
|
585
|
-
try {
|
|
586
|
-
await global.kuzzle.pipe('room:remove', roomId);
|
|
587
|
-
}
|
|
588
|
-
catch (e) {
|
|
589
|
-
return;
|
|
590
|
-
}
|
|
591
|
-
|
|
592
|
-
// We have to ask the cluster to dispatch the room removal event.
|
|
593
|
-
// The cluster will also remove the room from Koncorde if no other node
|
|
594
|
-
// uses it.
|
|
595
|
-
// (this node may have no subscribers on it, but other nodes might)
|
|
596
|
-
await global.kuzzle.ask('cluster:realtime:room:remove', roomId);
|
|
597
|
-
}
|
|
598
|
-
|
|
599
|
-
/**
|
|
600
|
-
* Subscribes a user to an existing room.
|
|
601
|
-
*
|
|
602
|
-
* @param {string} roomId
|
|
603
|
-
* @param {Request} request
|
|
604
|
-
* @returns {Promise.<Object>}
|
|
605
|
-
*/
|
|
606
|
-
async _subscribeToRoom (roomId, request) {
|
|
607
|
-
let subscribed = false;
|
|
608
|
-
let notifyPromise;
|
|
609
|
-
const channel = new Channel(roomId, request.input.args);
|
|
610
|
-
const connectionId = request.context.connection.id;
|
|
611
|
-
const customer = this.customers.get(connectionId);
|
|
612
|
-
const room = this.rooms.get(roomId);
|
|
613
|
-
|
|
614
|
-
if ( !customer || !customer.has(roomId)) {
|
|
615
|
-
subscribed = true;
|
|
616
|
-
this._addRoomForCustomer(connectionId, roomId, request.input.volatile);
|
|
617
|
-
|
|
618
|
-
notifyPromise = this.module.notifier.notifyUser(
|
|
619
|
-
roomId,
|
|
620
|
-
request,
|
|
621
|
-
'in',
|
|
622
|
-
{ count: room.customers.size });
|
|
623
|
-
}
|
|
624
|
-
else {
|
|
625
|
-
notifyPromise = Bluebird.resolve();
|
|
626
412
|
}
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
413
|
+
/**
|
|
414
|
+
* Deletes a room if no user has subscribed to it, and removes it also from the
|
|
415
|
+
* real-time engine
|
|
416
|
+
*/
|
|
417
|
+
async removeRoom(roomId) {
|
|
418
|
+
this.roomsCount--;
|
|
419
|
+
this.rooms.delete(roomId);
|
|
420
|
+
// @deprecated -- to be removed in the next major version
|
|
421
|
+
try {
|
|
422
|
+
await global.kuzzle.pipe('room:remove', roomId);
|
|
423
|
+
}
|
|
424
|
+
catch (e) {
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
427
|
+
// We have to ask the cluster to dispatch the room removal event.
|
|
428
|
+
// The cluster will also remove the room from Koncorde if no other node
|
|
429
|
+
// uses it.
|
|
430
|
+
// (this node may have no subscribers on it, but other nodes might)
|
|
431
|
+
await global.kuzzle.ask('cluster:realtime:room:remove', roomId);
|
|
632
432
|
}
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
433
|
+
/**
|
|
434
|
+
* Subscribes a connection to an existing room.
|
|
435
|
+
*
|
|
436
|
+
* The subscription is made on a configuration channel who will be created
|
|
437
|
+
* on the room if it does not already exists.
|
|
438
|
+
*
|
|
439
|
+
*/
|
|
440
|
+
async subscribeToRoom(roomId, request) {
|
|
441
|
+
let subscribed = false;
|
|
442
|
+
let notifyPromise;
|
|
443
|
+
const { scope, users, propagate } = request.input.args;
|
|
444
|
+
const connectionId = request.context.connection.id;
|
|
445
|
+
const channel = new channel_1.Channel(roomId, { propagate, scope, users });
|
|
446
|
+
const connectionRooms = this.subscriptions.get(connectionId);
|
|
447
|
+
const room = this.rooms.get(roomId);
|
|
448
|
+
if (!connectionRooms || !connectionRooms.hasRoom(roomId)) {
|
|
449
|
+
subscribed = true;
|
|
450
|
+
this.registerSubscription(connectionId, roomId, request.input.volatile);
|
|
451
|
+
notifyPromise = this.module.notifier.notifyUser(roomId, request, 'in', { count: room.size });
|
|
452
|
+
}
|
|
453
|
+
else {
|
|
454
|
+
notifyPromise = bluebird_1.default.resolve();
|
|
455
|
+
}
|
|
456
|
+
global.kuzzle.entryPoint.joinChannel(channel.name, connectionId);
|
|
457
|
+
room.createChannel(channel);
|
|
458
|
+
await notifyPromise;
|
|
459
|
+
return {
|
|
460
|
+
channel: channel.name,
|
|
461
|
+
cluster: channel.cluster,
|
|
462
|
+
subscribed,
|
|
463
|
+
};
|
|
653
464
|
}
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
newRoom (index, collection, roomId) {
|
|
666
|
-
if (!this.rooms.has(roomId)) {
|
|
667
|
-
this.rooms.set(roomId, {
|
|
668
|
-
channels: {},
|
|
669
|
-
collection,
|
|
670
|
-
customers: new Set(),
|
|
671
|
-
id: roomId,
|
|
672
|
-
index,
|
|
673
|
-
});
|
|
674
|
-
|
|
675
|
-
return true;
|
|
465
|
+
/**
|
|
466
|
+
* Create an empty room in the RAM cache if it doesn't exists
|
|
467
|
+
*
|
|
468
|
+
* @returns True if a new room has been created
|
|
469
|
+
*/
|
|
470
|
+
newRoom(index, collection, roomId) {
|
|
471
|
+
if (!this.rooms.has(roomId)) {
|
|
472
|
+
this.rooms.set(roomId, new room_1.Room(roomId, index, collection));
|
|
473
|
+
return true;
|
|
474
|
+
}
|
|
475
|
+
return false;
|
|
676
476
|
}
|
|
677
|
-
|
|
678
|
-
return false;
|
|
679
|
-
}
|
|
680
477
|
}
|
|
681
|
-
|
|
682
|
-
|
|
478
|
+
exports.HotelClerk = HotelClerk;
|
|
479
|
+
//# sourceMappingURL=hotelClerk.js.map
|