diff options
Diffstat (limited to 'lib/realtime.js')
-rw-r--r-- | lib/realtime.js | 328 |
1 files changed, 234 insertions, 94 deletions
diff --git a/lib/realtime.js b/lib/realtime.js index 1a57c77e..57038b72 100644 --- a/lib/realtime.js +++ b/lib/realtime.js @@ -9,9 +9,12 @@ var shortId = require('shortid'); var randomcolor = require("randomcolor"); var Chance = require('chance'), chance = new Chance(); +var md5 = require("blueimp-md5").md5; +var moment = require('moment'); //core var config = require("../config.js"); +var logger = require("./logger.js"); //others var db = require("./db.js"); @@ -49,7 +52,7 @@ function secure(socket, next) { next(new Error('AUTH failed: No cookie transmitted.')); } if (config.debug) - console.log("AUTH success cookie: " + handshakeData.sessionID); + logger.info("AUTH success cookie: " + handshakeData.sessionID); next(); } catch (ex) { @@ -65,9 +68,10 @@ var updater = setInterval(function () { var note = notes[key]; if (note.isDirty) { if (config.debug) - console.log("updater found dirty note: " + key); + logger.info("updater found dirty note: " + key); var body = LZString.decompressFromUTF16(note.body); var title = Note.getNoteTitle(body); + title = LZString.compressToBase64(title); body = LZString.compressToBase64(body); db.saveToDB(key, title, body, function (err, result) {}); @@ -75,36 +79,45 @@ var updater = setInterval(function () { } callback(); }, function (err) { - if (err) return console.error('updater error', err); + if (err) return logger.error('updater error', err); }); }, 5000); function getStatus(callback) { db.countFromDB(function (err, data) { - if (err) return console.log(err); - var regusers = 0; - var distinctregusers = 0; + if (err) return logger.info(err); var distinctaddresses = []; + var regaddresses = []; + var distinctregaddresses = []; Object.keys(users).forEach(function (key) { - var value = users[key]; - if (value.login) - regusers++; + var user = users[key]; var found = false; for (var i = 0; i < distinctaddresses.length; i++) { - if (value.address == distinctaddresses[i]) { + if (user.address == distinctaddresses[i]) { found = true; break; } } if (!found) { - distinctaddresses.push(value.address); - if (value.login) - distinctregusers++; + distinctaddresses.push(user.address); + } + if (user.login) { + regaddresses.push(user.address); + var found = false; + for (var i = 0; i < distinctregaddresses.length; i++) { + if (user.address == distinctregaddresses[i]) { + found = true; + break; + } + } + if (!found) { + distinctregaddresses.push(user.address); + } } }); User.getUserCount(function (err, regcount) { if (err) { - console.log('get status failed: ' + err); + logger.error('get status failed: ' + err); return; } if (callback) @@ -114,8 +127,8 @@ function getStatus(callback) { distinctOnlineUsers: distinctaddresses.length, notesCount: data.rows[0].count, registeredUsers: regcount, - onlineRegisteredUsers: regusers, - distinctOnlineRegisteredUsers: distinctregusers + onlineRegisteredUsers: regaddresses.length, + distinctOnlineRegisteredUsers: distinctregaddresses.length }); }); }); @@ -146,23 +159,40 @@ function emitOnlineUsers(socket) { if (user) users.push(buildUserOutData(user)); }); - notes[notename].socks.forEach(function (sock) { - var out = { - users: users - }; - out = LZString.compressToUTF16(JSON.stringify(out)); - sock.emit('online users', out); - }); + var out = { + users: users + }; + out = LZString.compressToUTF16(JSON.stringify(out)); + for (var i = 0, l = notes[notename].socks.length; i < l; i++) { + var sock = notes[notename].socks[i]; + if (sock && out) + sock.emit('online users', out); + }; } function emitUserStatus(socket) { var notename = getNotenameFromSocket(socket); if (!notename || !notes[notename]) return; - notes[notename].socks.forEach(function (sock) { + var out = buildUserOutData(users[socket.id]); + for (var i = 0, l = notes[notename].socks.length; i < l; i++) { + var sock = notes[notename].socks[i]; if (sock != socket) { - var out = buildUserOutData(users[socket.id]); sock.emit('user status', out); } + }; +} + +function emitRefresh(socket) { + var notename = getNotenameFromSocket(socket); + if (!notename || !notes[notename]) return; + var note = notes[notename]; + socket.emit('refresh', { + owner: note.owner, + permission: note.permission, + body: note.body, + otk: note.otk, + hash: note.hash, + updatetime: note.updatetime }); } @@ -175,9 +205,7 @@ function finishConnection(socket, notename) { notes[notename].users[socket.id] = users[socket.id]; notes[notename].socks.push(socket); emitOnlineUsers(socket); - socket.emit('refresh', { - body: notes[notename].body - }); + emitRefresh(socket); //clear finished socket in queue for (var i = 0; i < connectionSocketQueue.length; i++) { @@ -190,11 +218,11 @@ function finishConnection(socket, notename) { startConnection(connectionSocketQueue[0]); if (config.debug) { - console.log('SERVER connected a client to [' + notename + ']:'); - console.log(JSON.stringify(users[socket.id])); - //console.log(notes); + logger.info('SERVER connected a client to [' + notename + ']:'); + logger.info(JSON.stringify(users[socket.id])); + //logger.info(notes); getStatus(function (data) { - console.log(JSON.stringify(data)); + logger.info(JSON.stringify(data)); }); } } @@ -219,17 +247,34 @@ function startConnection(socket) { connectionSocketQueue.splice(i, 1); } isConnectionBusy = false; - return console.error(err); + return logger.error(err); } - var body = LZString.decompressFromBase64(data.rows[0].content); - body = LZString.compressToUTF16(body); - notes[notename] = { - socks: [], - body: body, - isDirty: false, - users: {} - }; - finishConnection(socket, notename); + var owner = data.rows[0].owner; + var permission = "freely"; + if (owner && owner != "null") { + permission = "editable"; + } + Note.findOrNewNote(notename, permission, function (err, note) { + if (err) { + responseError(res, "404", "Not Found", "oops."); + return; + } + var body = LZString.decompressFromBase64(data.rows[0].content); + body = LZString.compressToUTF16(body); + var updatetime = data.rows[0].update_time; + notes[notename] = { + owner: owner, + permission: note.permission, + socks: [], + body: body, + isDirty: false, + users: {}, + otk: shortId.generate(), + hash: md5(body), + updatetime: moment(updatetime).valueOf() + }; + finishConnection(socket, notename); + }); }); } else { finishConnection(socket, notename); @@ -241,8 +286,8 @@ function disconnect(socket) { isDisconnectBusy = true; if (config.debug) { - console.log("SERVER disconnected a client"); - console.log(JSON.stringify(users[socket.id])); + logger.info("SERVER disconnected a client"); + logger.info(JSON.stringify(users[socket.id])); } var notename = getNotenameFromSocket(socket); if (!notename) return; @@ -251,24 +296,31 @@ function disconnect(socket) { } if (notes[notename]) { delete notes[notename].users[socket.id]; - var index = notes[notename].socks.indexOf(socket); - if (index > -1) { - notes[notename].socks.splice(index, 1); - } + do { + var index = notes[notename].socks.indexOf(socket); + if (index != -1) { + notes[notename].socks.splice(index, 1); + } + } while (index != -1); if (Object.keys(notes[notename].users).length <= 0) { - var body = LZString.decompressFromUTF16(notes[notename].body); - var title = Note.getNoteTitle(body); - body = LZString.compressToBase64(body); - db.saveToDB(notename, title, body, - function (err, result) { - delete notes[notename]; - if (config.debug) { - //console.log(notes); - getStatus(function (data) { - console.log(JSON.stringify(data)); - }); - } - }); + if (notes[notename].isDirty) { + var body = LZString.decompressFromUTF16(notes[notename].body); + var title = Note.getNoteTitle(body); + title = LZString.compressToBase64(title); + body = LZString.compressToBase64(body); + db.saveToDB(notename, title, body, + function (err, result) { + delete notes[notename]; + if (config.debug) { + //logger.info(notes); + getStatus(function (data) { + logger.info(JSON.stringify(data)); + }); + } + }); + } else { + delete notes[notename]; + } } } emitOnlineUsers(socket); @@ -284,9 +336,9 @@ function disconnect(socket) { disconnect(disconnectSocketQueue[0]); if (config.debug) { - //console.log(notes); + //logger.info(notes); getStatus(function (data) { - console.log(JSON.stringify(data)); + logger.info(JSON.stringify(data)); }); } } @@ -309,6 +361,24 @@ function updateUserData(socket, user) { //retrieve user data from passport if (socket.request.user && socket.request.user.logged_in) { var profile = JSON.parse(socket.request.user.profile); + /* + var photo = null; + switch(profile.provider) { + case "facebook": + console.log(profile); + break; + case "twitter": + photo = profile.photos[0]; + break; + case "github": + photo = profile.avatar_url; + break; + case "dropbox": + //not image api provided + break; + } + user.photo = photo; + */ user.name = profile.displayName || profile.username; user.userid = socket.request.user._id; user.login = true; @@ -353,7 +423,6 @@ function connection(socket) { id: socket.id, address: socket.handshake.address, 'user-agent': socket.handshake.headers['user-agent'], - otk: shortId.generate(), color: color, cursor: null, login: false, @@ -368,24 +437,41 @@ function connection(socket) { connectionSocketQueue.push(socket); startConnection(socket); - //when a new client coming or received a client refresh request - socket.on('refresh', function (body_) { + //received client refresh request + socket.on('refresh', function () { + emitRefresh(socket); + }); + + //received client data updated + socket.on('update', function (body_) { var notename = getNotenameFromSocket(socket); - if (!notename) return; + if (!notename || !notes[notename]) return; if (config.debug) - console.log('SERVER received [' + notename + '] data updated: ' + socket.id); - if (notes[notename].body != body_) { - notes[notename].body = body_; - notes[notename].isDirty = true; + logger.info('SERVER received [' + notename + '] data updated: ' + socket.id); + var note = notes[notename]; + if (note.body != body_) { + note.body = body_; + note.hash = md5(body_); + note.updatetime = Date.now(); + note.isDirty = true; } + var out = { + id: socket.id, + hash: note.hash, + updatetime: note.updatetime + }; + for (var i = 0, l = note.socks.length; i < l; i++) { + var sock = note.socks[i]; + sock.emit('check', out); + }; }); //received user status socket.on('user status', function (data) { var notename = getNotenameFromSocket(socket); - if (!notename) return; + if (!notename || !notes[notename]) return; if (config.debug) - console.log('SERVER received [' + notename + '] user status from [' + socket.id + ']: ' + JSON.stringify(data)); + logger.info('SERVER received [' + notename + '] user status from [' + socket.id + ']: ' + JSON.stringify(data)); if (data) { var user = users[socket.id]; user.idle = data.idle; @@ -394,9 +480,40 @@ function connection(socket) { emitUserStatus(socket); }); + //received note permission change request + socket.on('permission', function (permission) { + //need login to do more actions + if (socket.request.user && socket.request.user.logged_in) { + var notename = getNotenameFromSocket(socket); + if (!notename || !notes[notename]) return; + var note = notes[notename]; + //Only owner can change permission + if (note.owner == socket.request.user._id) { + note.permission = permission; + Note.findNote(notename, function (err, _note) { + if (err || !_note) { + return; + } + Note.updatePermission(_note, permission, function (err, _note) { + if (err || !_note) { + return; + } + var out = { + permission: permission + }; + for (var i = 0, l = note.socks.length; i < l; i++) { + var sock = note.socks[i]; + sock.emit('permission', out); + }; + }); + }); + } + } + }); + //reveiced when user logout or changed socket.on('user changed', function () { - console.log('user changed'); + logger.info('user changed'); var notename = getNotenameFromSocket(socket); if (!notename || !notes[notename]) return; updateUserData(socket, notes[notename].users[socket.id]); @@ -431,11 +548,12 @@ function connection(socket) { if (!notename || !notes[notename]) return; users[socket.id].cursor = data; var out = buildUserOutData(users[socket.id]); - notes[notename].socks.forEach(function (sock) { + for (var i = 0, l = notes[notename].socks.length; i < l; i++) { + var sock = notes[notename].socks[i]; if (sock != socket) { sock.emit('cursor focus', out); } - }); + }; }); //received cursor activity @@ -444,11 +562,12 @@ function connection(socket) { if (!notename || !notes[notename]) return; users[socket.id].cursor = data; var out = buildUserOutData(users[socket.id]); - notes[notename].socks.forEach(function (sock) { + for (var i = 0, l = notes[notename].socks.length; i < l; i++) { + var sock = notes[notename].socks[i]; if (sock != socket) { sock.emit('cursor activity', out); } - }); + }; }); //received cursor blur @@ -459,13 +578,12 @@ function connection(socket) { var out = { id: socket.id }; - notes[notename].socks.forEach(function (sock) { + for (var i = 0, l = notes[notename].socks.length; i < l; i++) { + var sock = notes[notename].socks[i]; if (sock != socket) { - if (sock != socket) { - sock.emit('cursor blur', out); - } + sock.emit('cursor blur', out); } - }); + }; }); //when a new client disconnect @@ -477,12 +595,30 @@ function connection(socket) { //when received client change data request socket.on('change', function (op) { var notename = getNotenameFromSocket(socket); - if (!notename) return; + if (!notename || !notes[notename]) return; + var note = notes[notename]; + switch (note.permission) { + case "freely": + //not blocking anyone + break; + case "editable": + //only login user can change + if (!socket.request.user || !socket.request.user.logged_in) + return; + break; + case "locked": + //only owner can change + if (note.owner != socket.request.user._id) + return; + break; + } op = LZString.decompressFromUTF16(op); if (op) op = JSON.parse(op); + else + return; if (config.debug) - console.log('SERVER received [' + notename + '] data changed: ' + socket.id + ', op:' + JSON.stringify(op)); + logger.info('SERVER received [' + notename + '] data changed: ' + socket.id + ', op:' + JSON.stringify(op)); switch (op.origin) { case '+input': case '+delete': @@ -499,16 +635,20 @@ function connection(socket) { case '+joinLines': case '+duplicateLine': case '+sortLines': - notes[notename].socks.forEach(function (sock) { - if (sock != socket) { - if (config.debug) - console.log('SERVER emit sync data out [' + notename + ']: ' + sock.id + ', op:' + JSON.stringify(op)); - sock.emit('change', LZString.compressToUTF16(JSON.stringify(op))); - } - }); + op.id = socket.id; + op.otk = note.otk; + op.nextotk = note.otk = shortId.generate(); + var stringop = JSON.stringify(op); + var compressstringop = LZString.compressToUTF16(stringop); + for (var i = 0, l = note.socks.length; i < l; i++) { + var sock = note.socks[i]; + if (config.debug) + logger.info('SERVER emit sync data out [' + notename + ']: ' + sock.id + ', op:' + stringop); + sock.emit('change', compressstringop); + }; break; - default: - console.log('SERVER received uncaught [' + notename + '] data changed: ' + socket.id + ', op:' + JSON.stringify(op)); + default: + logger.info('SERVER received uncaught [' + notename + '] data changed: ' + socket.id + ', op:' + JSON.stringify(op)); } }); } |