Skip to content

Commit 968c12f

Browse files
committed
refactor(realtime: extract user event "delete" to SocketClient
Signed-off-by: BoHong Li <[email protected]>
1 parent 79666ae commit 968c12f

File tree

2 files changed

+201
-44
lines changed

2 files changed

+201
-44
lines changed

lib/realtime.js

Lines changed: 85 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,11 @@ function secure (socket, next) {
4949
handshakeData.cookie = cookie.parse(handshakeData.headers.cookie)
5050
handshakeData.sessionID = cookieParser.signedCookie(handshakeData.cookie[config.sessionName], config.sessionSecret)
5151
if (handshakeData.sessionID &&
52-
handshakeData.cookie[config.sessionName] &&
53-
handshakeData.cookie[config.sessionName] !== handshakeData.sessionID) {
54-
if (config.debug) { logger.info('AUTH success cookie: ' + handshakeData.sessionID) }
52+
handshakeData.cookie[config.sessionName] &&
53+
handshakeData.cookie[config.sessionName] !== handshakeData.sessionID) {
54+
if (config.debug) {
55+
logger.info('AUTH success cookie: ' + handshakeData.sessionID)
56+
}
5557
return next()
5658
} else {
5759
next(new Error('AUTH failed: Cookie is invalid.'))
@@ -184,7 +186,9 @@ setInterval(function () {
184186
var socket = realtime.io.sockets.connected[key]
185187
if ((!socket && users[key]) ||
186188
(socket && (!socket.rooms || socket.rooms.length <= 0))) {
187-
if (config.debug) { logger.info('cleaner found redundant user: ' + key) }
189+
if (config.debug) {
190+
logger.info('cleaner found redundant user: ' + key)
191+
}
188192
if (!socket) {
189193
socket = {
190194
id: key
@@ -337,7 +341,9 @@ function emitOnlineUsers (socket) {
337341
var users = []
338342
Object.keys(notes[noteId].users).forEach(function (key) {
339343
var user = notes[noteId].users[key]
340-
if (user) { users.push(buildUserOutData(user)) }
344+
if (user) {
345+
users.push(buildUserOutData(user))
346+
}
341347
})
342348
var out = {
343349
users: users
@@ -403,15 +409,27 @@ function connectNextSocket () {
403409
function interruptConnection (socket, noteId, socketId) {
404410
if (notes[noteId]) delete notes[noteId]
405411
if (users[socketId]) delete users[socketId]
406-
if (socket) { clearSocketQueue(connectionSocketQueue, socket) } else { connectionSocketQueue.shift() }
412+
if (socket) {
413+
clearSocketQueue(connectionSocketQueue, socket)
414+
} else {
415+
connectionSocketQueue.shift()
416+
}
407417
connectNextSocket()
408418
}
409419

410420
function checkViewPermission (req, note) {
411421
if (note.permission === 'private') {
412-
if (req.user && req.user.logged_in && req.user.id === note.owner) { return true } else { return false }
422+
if (req.user && req.user.logged_in && req.user.id === note.owner) {
423+
return true
424+
} else {
425+
return false
426+
}
413427
} else if (note.permission === 'limited' || note.permission === 'protected') {
414-
if (req.user && req.user.logged_in) { return true } else { return false }
428+
if (req.user && req.user.logged_in) {
429+
return true
430+
} else {
431+
return false
432+
}
415433
} else {
416434
return true
417435
}
@@ -620,7 +638,9 @@ function disconnect (socket) {
620638
clearSocketQueue(disconnectSocketQueue, socket)
621639
// seek for next socket
622640
isDisconnectBusy = false
623-
if (disconnectSocketQueue.length > 0) { disconnect(disconnectSocketQueue[0]) }
641+
if (disconnectSocketQueue.length > 0) {
642+
disconnect(disconnectSocketQueue[0])
643+
}
624644

625645
if (config.debug) {
626646
// logger.info(notes);
@@ -669,13 +689,20 @@ function ifMayEdit (socket, callback) {
669689
case 'freely':
670690
// not blocking anyone
671691
break
672-
case 'editable': case 'limited':
692+
case 'editable':
693+
case 'limited':
673694
// only login user can change
674-
if (!socket.request.user || !socket.request.user.logged_in) { mayEdit = false }
695+
if (!socket.request.user || !socket.request.user.logged_in) {
696+
mayEdit = false
697+
}
675698
break
676-
case 'locked': case 'private': case 'protected':
699+
case 'locked':
700+
case 'private':
701+
case 'protected':
677702
// only owner can change
678-
if (!note.owner || note.owner !== socket.request.user.id) { mayEdit = false }
703+
if (!note.owner || note.owner !== socket.request.user.id) {
704+
mayEdit = false
705+
}
679706
break
680707
}
681708
// if user may edit and this is a text operation
@@ -777,6 +804,51 @@ class SocketClient {
777804
this.socket.on('online users', this.onlineUsersEventHandler.bind(this))
778805
// reveiced when user logout or changed
779806
this.socket.on('user changed', this.userChangedEventHandler.bind(this))
807+
// delete a note
808+
this.socket.on('delete', this.deleteNote.bind(this))
809+
}
810+
811+
isUserLoggedIn () {
812+
return this.socket.request.user && this.socket.request.user.logged_in
813+
}
814+
815+
getCurrentLoggedInUserId () {
816+
return get(this.socket, 'request.user.id')
817+
}
818+
819+
disconnectSocketOnNote (note) {
820+
note.socks.forEach((sock) => {
821+
if (sock) {
822+
sock.emit('delete')
823+
setImmediate(() => {
824+
sock.disconnect(true)
825+
})
826+
}
827+
})
828+
}
829+
830+
async destroyNote (id) {
831+
return models.Note.destroy({
832+
where: { id: id }
833+
})
834+
}
835+
836+
deleteNote () {
837+
// need login to do more actions
838+
if (this.isUserLoggedIn() && this.isNoteAndUserExists()) {
839+
const note = this.getCurrentNote()
840+
// Only owner can delete note
841+
if (note.owner && note.owner === this.getCurrentLoggedInUserId()) {
842+
this.destroyNote(note.id)
843+
.then((successRows) => {
844+
if (!successRows) return
845+
this.disconnectSocketOnNote(note)
846+
})
847+
.catch(function (err) {
848+
return logger.error('delete note failed: ' + err)
849+
})
850+
}
851+
}
780852
}
781853

782854
userChangedEventHandler () {
@@ -983,37 +1055,6 @@ function connection (socket) {
9831055
}
9841056
}
9851057
})
986-
987-
// delete a note
988-
socket.on('delete', function () {
989-
// need login to do more actions
990-
if (socket.request.user && socket.request.user.logged_in) {
991-
var noteId = socket.noteId
992-
if (!noteId || !notes[noteId]) return
993-
var note = notes[noteId]
994-
// Only owner can delete note
995-
if (note.owner && note.owner === socket.request.user.id) {
996-
models.Note.destroy({
997-
where: {
998-
id: noteId
999-
}
1000-
}).then(function (count) {
1001-
if (!count) return
1002-
for (var i = 0, l = note.socks.length; i < l; i++) {
1003-
var sock = note.socks[i]
1004-
if (typeof sock !== 'undefined' && sock) {
1005-
sock.emit('delete')
1006-
setTimeout(function () {
1007-
sock.disconnect(true)
1008-
}, 0)
1009-
}
1010-
}
1011-
}).catch(function (err) {
1012-
return logger.error('delete note failed: ' + err)
1013-
})
1014-
}
1015-
}
1016-
})
10171058
}
10181059

10191060
exports = module.exports = realtime

test/realtime/socket-events.test.js

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,4 +267,120 @@ describe('realtime#socket event', function () {
267267
assert(emitOnlineUsersStub.called === false)
268268
})
269269
})
270+
271+
describe('delete', function () {
272+
it('should delete note when owner request', function (done) {
273+
const currentUserId = 'user1_id'
274+
const noteOwnerId = 'user1_id'
275+
const otherClient = makeMockSocket()
276+
clientSocket.request = {
277+
user: {
278+
logged_in: true,
279+
id: currentUserId
280+
}
281+
}
282+
realtime.notes[noteId] = {
283+
owner: noteOwnerId,
284+
socks: [clientSocket, undefined, otherClient]
285+
}
286+
const deleteFunc = eventFuncMap.get('delete')
287+
deleteFunc()
288+
setTimeout(() => {
289+
assert(otherClient.disconnect.calledOnce)
290+
assert(otherClient.emit.calledOnce)
291+
assert(otherClient.emit.lastCall.args[0] === 'delete')
292+
assert(clientSocket.disconnect.calledOnce)
293+
assert(clientSocket.emit.calledOnce)
294+
assert(clientSocket.emit.lastCall.args[0] === 'delete')
295+
assert(modelsMock.Note.destroy.calledOnce)
296+
done()
297+
}, 10)
298+
})
299+
300+
it('should not do anything when user not login', function (done) {
301+
const noteOwnerId = 'user1_id'
302+
clientSocket.request = {}
303+
realtime.notes[noteId] = {
304+
owner: noteOwnerId,
305+
socks: [clientSocket]
306+
}
307+
const deleteFunc = eventFuncMap.get('delete')
308+
deleteFunc()
309+
setTimeout(() => {
310+
assert(modelsMock.Note.destroy.called === false)
311+
assert(clientSocket.disconnect.called === false)
312+
done()
313+
}, 10)
314+
})
315+
316+
it('should not do anything when note not exists', function (done) {
317+
const currentUserId = 'user1_id'
318+
clientSocket.request = {
319+
user: {
320+
logged_in: true,
321+
id: currentUserId
322+
}
323+
}
324+
const deleteFunc = eventFuncMap.get('delete')
325+
deleteFunc()
326+
setTimeout(() => {
327+
assert(modelsMock.Note.destroy.called === false)
328+
assert(clientSocket.disconnect.called === false)
329+
done()
330+
}, 10)
331+
})
332+
333+
it('should not do anything when note owner is not me', function (done) {
334+
const currentUserId = 'user1_id'
335+
const noteOwnerId = 'user2_id'
336+
const otherClient = makeMockSocket()
337+
clientSocket.request = {
338+
user: {
339+
logged_in: true,
340+
id: currentUserId
341+
}
342+
}
343+
realtime.notes[noteId] = {
344+
owner: noteOwnerId,
345+
socks: [clientSocket, otherClient]
346+
}
347+
const deleteFunc = eventFuncMap.get('delete')
348+
deleteFunc()
349+
setTimeout(() => {
350+
assert(clientSocket.disconnect.called === false)
351+
assert(modelsMock.Note.destroy.called === false)
352+
done()
353+
}, 10)
354+
})
355+
356+
it('should not do anything when note destroy fail', function (done) {
357+
const currentUserId = 'user1_id'
358+
const noteOwnerId = 'user1_id'
359+
modelsMock.Note.destroy.withArgs({
360+
where: {
361+
id: noteId
362+
}
363+
}).returns(Promise.resolve(0))
364+
365+
const otherClient = makeMockSocket()
366+
clientSocket.request = {
367+
user: {
368+
logged_in: true,
369+
id: currentUserId
370+
}
371+
}
372+
realtime.notes[noteId] = {
373+
id: noteId,
374+
owner: noteOwnerId,
375+
socks: [clientSocket, otherClient]
376+
}
377+
const deleteFunc = eventFuncMap.get('delete')
378+
deleteFunc()
379+
setTimeout(() => {
380+
assert(modelsMock.Note.destroy.calledOnce)
381+
assert(clientSocket.disconnect.called === false)
382+
done()
383+
}, 10)
384+
})
385+
})
270386
})

0 commit comments

Comments
 (0)