1 |
|
2 |
|
3 | const ChatServiceError = require('./ChatServiceError')
|
4 | const Promise = require('bluebird')
|
5 | const _ = require('lodash')
|
6 | const { checkNameSymbols, possiblyCallback } = require('./utils')
|
7 |
|
8 | /**
|
9 | * Server side operations.
|
10 | *
|
11 | * @mixin
|
12 | * @memberof chat-service
|
13 | * @see chat-service.ChatService
|
14 | */
|
15 | class ServiceAPI {
|
16 |
|
17 | constructor (state, makeUser, clusterBus) {
|
18 | this.state = state
|
19 | this.makeUser = makeUser
|
20 | this.clusterBus = clusterBus
|
21 | }
|
22 |
|
23 | /**
|
24 | * Executes {@link rpc.clientRequests} handlers.
|
25 | *
|
26 | * @param {string|boolean|Object} context Is a `userName` if
|
27 | * `string`, or a `bypassPermissions` if `boolean`, or an options
|
28 | * object if `Object`.
|
29 | * @param {string} command Command name.
|
30 | * @param {...*} args Command arguments.
|
31 | * @param {callback} [cb] Optional callback.
|
32 | *
|
33 | * @property {string} [context.userName] User name.
|
34 | * @property {string} [context.id] Socket id, it is required for
|
35 | * {@link rpc.clientRequests.roomJoin} and {@link
|
36 | * rpc.clientRequests.roomLeave} commands.
|
37 | * @property {boolean} [context.bypassHooks=false] If `false`
|
38 | * executes command without before and after hooks.
|
39 | * @property {boolean} [context.bypassPermissions=false] If `true`
|
40 | * executes command (except {@link rpc.clientRequests.roomJoin})
|
41 | * bypassing built-in permissions checking.
|
42 | *
|
43 | * @return {Promise<Array>} Array of command results.
|
44 | *
|
45 | * @see rpc.clientRequests
|
46 | */
|
47 | execUserCommand (context, command, ...args) {
|
48 | if (_.isObject(context)) {
|
49 | var { userName } = context
|
50 | context = _.clone(context)
|
51 | } else if (_.isBoolean(context)) {
|
52 | context = {bypassPermissions: context}
|
53 | } else {
|
54 | userName = context
|
55 | context = {}
|
56 | }
|
57 | context.isLocalCall = true
|
58 | let [nargs, cb] = possiblyCallback(args)
|
59 | return Promise.try(() => {
|
60 | if (userName) {
|
61 | return this.state.getUser(userName)
|
62 | } else {
|
63 | return this.makeUser()
|
64 | }
|
65 | }).then(user => user.exec(command, context, nargs))
|
66 | .asCallback(cb, { spread: true })
|
67 | }
|
68 |
|
69 | /**
|
70 | * Adds an user with a state.
|
71 | *
|
72 | * @param {string} userName User name.
|
73 | * @param {Object} [state] User state.
|
74 | * @param {callback} [cb] Optional callback.
|
75 | *
|
76 | * @property {Array<string>} [state.whitelist=[]] User direct messages
|
77 | * whitelist.
|
78 | * @property {Array<string>} [state.blacklist=[]] User direct messages
|
79 | * blacklist.
|
80 | * @property {boolean} [state.whitelistOnly=false] User direct
|
81 | * messages whitelistOnly mode.
|
82 | *
|
83 | * @return {Promise<undefined>} Promise that resolves without any data.
|
84 | */
|
85 | addUser (userName, state, cb) {
|
86 | return checkNameSymbols(userName)
|
87 | .then(() => this.state.addUser(userName, state))
|
88 | .return()
|
89 | .asCallback(cb)
|
90 | }
|
91 |
|
92 | /**
|
93 | * Deletes an offline user. Will raise an error if a user has online
|
94 | * sockets.
|
95 | *
|
96 | * @param {string} userName User name.
|
97 | * @param {callback} [cb] Optional callback.
|
98 | *
|
99 | * @return {Promise<undefined>} Promise that resolves without any data.
|
100 | */
|
101 | deleteUser (userName, cb) {
|
102 | return this.state.getUser(userName).then(user => {
|
103 | return user.listOwnSockets().then(sockets => {
|
104 | if (sockets && _.size(sockets) > 0) {
|
105 | return Promise.reject(new ChatServiceError('userOnline', userName))
|
106 | } else {
|
107 | return Promise.all([
|
108 | user.removeState(),
|
109 | this.state.removeUser(userName)
|
110 | ])
|
111 | }
|
112 | })
|
113 | }).return().asCallback(cb)
|
114 | }
|
115 |
|
116 | /**
|
117 | * Checks for an user existence.
|
118 | *
|
119 | * @param {string} userName User name.
|
120 | * @param {callback} [cb] Optional callback.
|
121 | *
|
122 | * @return {Promise<boolean>} Predicate result.
|
123 | */
|
124 | hasUser (userName, cb) {
|
125 | return this.state.getUser(userName, true)
|
126 | .then(user => Boolean(user))
|
127 | .asCallback(cb)
|
128 | }
|
129 |
|
130 | /**
|
131 | * Checks for a name existence in a direct messaging list.
|
132 | *
|
133 | * @param {string} userName User name.
|
134 | * @param {string} listName List name.
|
135 | * @param {string} item List element.
|
136 | * @param {callback} [cb] Optional callback.
|
137 | *
|
138 | * @return {Promise<boolean>} Predicate result.
|
139 | */
|
140 | userHasInList (userName, listName, item, cb) {
|
141 | return this.state.getUser(userName)
|
142 | .then(user => user.directMessaging.hasInList(listName, item))
|
143 | .asCallback(cb)
|
144 | }
|
145 |
|
146 | /**
|
147 | * Checks for a direct messaging permission.
|
148 | *
|
149 | * @param {string} recipient Recipient name.
|
150 | * @param {string} sender Sender name.
|
151 | * @param {callback} [cb] Optional callback.
|
152 | *
|
153 | * @return {Promise<boolean>} Predicate result.
|
154 | */
|
155 | hasDirectAccess (recipient, sender, cb) {
|
156 | return this.state.getUser(recipient)
|
157 | .then(user => user.directMessaging.checkAcess(sender))
|
158 | .return(true)
|
159 | .catchReturn(ChatServiceError, false)
|
160 | .asCallback(cb)
|
161 | }
|
162 |
|
163 | /**
|
164 | * Disconnects user's sockets for all service instances. Method is
|
165 | * asynchronous, returns without waiting for the completion.
|
166 | *
|
167 | * @param {string} userName User name.
|
168 | *
|
169 | * @return {undefined} Returns no data.
|
170 | */
|
171 | disconnectUserSockets (userName) {
|
172 | this.clusterBus.emit('disconnectUserSockets', userName)
|
173 | }
|
174 |
|
175 | /**
|
176 | * Adds a room with a state.
|
177 | *
|
178 | * @param {string} roomName Room name.
|
179 | * @param {Object} [state] Room state.
|
180 | * @param {callback} [cb] Optional callback.
|
181 | *
|
182 | * @property {Array<string>} [state.whitelist=[]] Room whitelist.
|
183 | * @property {Array<string>} [state.blacklist=[]] Room blacklist
|
184 | * @property {Array<string>} [state.adminlist=[]] Room adminlist.
|
185 | * @property {boolean} [state.whitelistOnly=false] Room
|
186 | * whitelistOnly mode.
|
187 | * @property {string} [state.owner] Room owner.
|
188 | * @property {number} [state.historyMaxSize] Room history maximum
|
189 | * size. Defalut value is {@link chat-service.config.options}
|
190 | * `historyMaxSize`.
|
191 | * @property {boolean} [state.enableAccessListsUpdates] Room enable
|
192 | * access lists updates. Defalut value is {@link
|
193 | * chat-service.config.options} `enableAccessListsUpdates`.
|
194 | * @property {boolean} [state.enableUserlistUpdates] Room enable
|
195 | * userlist updates. Defalut value is {@link
|
196 | * chat-service.config.options} `enableUserlistUpdates`.
|
197 | *
|
198 | * @return {Promise<undefined>} Promise that resolves without any data.
|
199 | */
|
200 | addRoom (roomName, state, cb) {
|
201 | return checkNameSymbols(roomName)
|
202 | .then(() => this.state.addRoom(roomName, state))
|
203 | .return()
|
204 | .asCallback(cb)
|
205 | }
|
206 |
|
207 | /**
|
208 | * Removes all joined users from the room and removes all room data.
|
209 | *
|
210 | * @param {string} roomName Room name.
|
211 | * @param {callback} [cb] Optional callback.
|
212 | *
|
213 | * @return {Promise<undefined>} Promise that resolves without any data.
|
214 | */
|
215 | deleteRoom (roomName, cb) {
|
216 | return this.execUserCommand(true, 'roomDelete', roomName)
|
217 | .return()
|
218 | .asCallback(cb)
|
219 | }
|
220 |
|
221 | /**
|
222 | * Checks for a room existence.
|
223 | *
|
224 | * @param {string} roomName Room name.
|
225 | * @param {callback} [cb] Optional callback.
|
226 | *
|
227 | * @return {Promise<boolean>} Predicate result.
|
228 | */
|
229 | hasRoom (roomName, cb) {
|
230 | return this.state.getRoom(roomName, true)
|
231 | .then(room => Boolean(room))
|
232 | .asCallback(cb)
|
233 | }
|
234 |
|
235 | /**
|
236 | * Checks for a name existence in a room list.
|
237 | *
|
238 | * @param {string} roomName Room name.
|
239 | * @param {string} listName List name.
|
240 | * @param {string} item List element.
|
241 | * @param {callback} [cb] Optional callback.
|
242 | *
|
243 | * @return {Promise<boolean>} Predicate result.
|
244 | */
|
245 | roomHasInList (roomName, listName, item, cb) {
|
246 | return this.state.getRoom(roomName)
|
247 | .then(room => room.roomState.hasInList(listName, item))
|
248 | .asCallback(cb)
|
249 | }
|
250 |
|
251 | /**
|
252 | * Checks for a room access permission.
|
253 | *
|
254 | * @param {string} roomName Room name.
|
255 | * @param {string} userName User name.
|
256 | * @param {callback} [cb] Optional callback.
|
257 | *
|
258 | * @return {Promise<boolean>} Predicate result.
|
259 | */
|
260 | hasRoomAccess (roomName, userName, cb) {
|
261 | return this.state.getRoom(roomName)
|
262 | .then(room => room.checkAcess(userName))
|
263 | .return(true)
|
264 | .catchReturn(ChatServiceError, false)
|
265 | .asCallback(cb)
|
266 | }
|
267 |
|
268 | /**
|
269 | * Changes the room owner.
|
270 | *
|
271 | * @param {string} roomName Room name.
|
272 | * @param {string} owner Owner user name.
|
273 | * @param {callback} [cb] Optional callback.
|
274 | *
|
275 | * @return {Promise<undefined>} Promise that resolves without any data.
|
276 | */
|
277 | changeRoomOwner (roomName, owner, cb) {
|
278 | return this.state.getRoom(roomName)
|
279 | .then(room => room.roomState.ownerSet(owner))
|
280 | .return()
|
281 | .asCallback(cb)
|
282 | }
|
283 |
|
284 | /**
|
285 | * Changes the room history size.
|
286 | *
|
287 | * @param {string} roomName Room name.
|
288 | * @param {number} size Room history size.
|
289 | * @param {callback} [cb] Optional callback.
|
290 | *
|
291 | * @return {Promise<undefined>} Promise that resolves without any data.
|
292 | */
|
293 | changeRoomHistoryMaxSize (roomName, size, cb) {
|
294 | return this.state.getRoom(roomName)
|
295 | .then(room => room.roomState.historyMaxSizeSet(size))
|
296 | .return()
|
297 | .asCallback(cb)
|
298 | }
|
299 |
|
300 | /**
|
301 | * Enables or disables access lists updates for the room.
|
302 | *
|
303 | * @param {string} roomName Room name.
|
304 | * @param {boolean} mode Enable or disable.
|
305 | * @param {callback} [cb] Optional callback.
|
306 | *
|
307 | * @return {Promise<undefined>} Promise that resolves without any data.
|
308 | *
|
309 | * @see rpc.serverNotifications.roomAccessListAdded
|
310 | * @see rpc.serverNotifications.roomAccessListRemoved
|
311 | * @see rpc.serverNotifications.roomModeChanged
|
312 | */
|
313 | changeAccessListsUpdates (roomName, mode, cb) {
|
314 | return this.state.getRoom(roomName)
|
315 | .then(room => room.roomState.accessListsUpdatesSet(mode))
|
316 | .return()
|
317 | .asCallback(cb)
|
318 | }
|
319 |
|
320 | /**
|
321 | * Enables or disables user list updates for the room.
|
322 | *
|
323 | * @param {string} roomName Room name.
|
324 | * @param {boolean} mode Enable or disable.
|
325 | * @param {callback} [cb] Optional callback.
|
326 | *
|
327 | * @return {Promise<undefined>} Promise that resolves without any data.
|
328 | *
|
329 | * @see rpc.serverNotifications.roomUserJoined
|
330 | * @see rpc.serverNotifications.roomUserLeft
|
331 | */
|
332 | changeUserlistUpdates (roomName, mode, cb) {
|
333 | return this.state.getRoom(roomName)
|
334 | .then(room => room.roomState.userlistUpdatesSet(mode))
|
335 | .return()
|
336 | .asCallback(cb)
|
337 | }
|
338 |
|
339 | }
|
340 |
|
341 | module.exports = ServiceAPI
|