diff --git a/Common/sources/constants.js b/Common/sources/constants.js index 91c93d29..4f57f00e 100644 --- a/Common/sources/constants.js +++ b/Common/sources/constants.js @@ -59,10 +59,12 @@ exports.LICENSE_RESULT = { UnknownUser : 4, Connections : 5, ExpiredTrial: 6, - SuccessLimit: 7 + SuccessLimit: 7, + UsersCount : 8 }; exports.LICENSE_CONNECTIONS = 20; +exports.LICENSE_EXPIRE_USERS_ONE_DAY = 24 * 60 * 60; // day in seconds exports.AVS_OFFICESTUDIO_FILE_UNKNOWN = 0x0000; exports.AVS_OFFICESTUDIO_FILE_DOCUMENT = 0x0040; @@ -184,6 +186,7 @@ exports.REDIS_KEY_PUBSUB = 'pubsub'; exports.REDIS_KEY_SAVE_LOCK = 'savelock:'; exports.REDIS_KEY_PRESENCE_HASH = 'presence:hash:'; exports.REDIS_KEY_PRESENCE_SET = 'presence:set:'; +exports.REDIS_KEY_PRESENCE_UNIQUE_USERS = 'presence:unique:users'; exports.REDIS_KEY_LOCKS = 'locks:'; exports.REDIS_KEY_CHANGES_INDEX = 'changesindex:'; exports.REDIS_KEY_LOCK_DOCUMENT = 'lockdocument:'; diff --git a/Common/sources/license.js b/Common/sources/license.js index d3a44730..391e6989 100644 --- a/Common/sources/license.js +++ b/Common/sources/license.js @@ -53,7 +53,7 @@ const redisKeyLicense = cfgRedisPrefix + ((constants.PACKAGE_TYPE_OS === oPackag exports.readLicense = function*() { const c_LR = constants.LICENSE_RESULT; const c_LM = constants.LICENSE_MODE; - const resMax = {count: 999999, type: c_LR.Success, mode: c_LM.None, connections: 999999999}; + const resMax = {count: 999999, type: c_LR.Success, mode: c_LM.None, connections: 999999999, users: 999999999}; const res = { count: 1, type: c_LR.Error, @@ -61,7 +61,9 @@ exports.readLicense = function*() { packageType: oPackageType, mode: c_LM.None, branding: false, - connections: constants.LICENSE_CONNECTIONS + connections: constants.LICENSE_CONNECTIONS, + usersCount: 0, + usersExpire: constants.LICENSE_EXPIRE_USERS_ONE_DAY }; let checkFile = false; try { @@ -92,6 +94,13 @@ exports.readLicense = function*() { if (oLicense.hasOwnProperty('connections')) { res.connections = oLicense['connections'] >> 0; } + if (oLicense.hasOwnProperty('users_count')) { + res.usersCount = oLicense['users_count'] >> 0; + } + if (oLicense.hasOwnProperty('users_expire')) { + res.usersExpire = Math.max(constants.LICENSE_EXPIRE_USERS_ONE_DAY, (oLicense['users_expire'] >> 0) * + constants.LICENSE_EXPIRE_USERS_ONE_DAY); + } } else { throw 'verify'; } diff --git a/DocService/sources/DocsCoServer.js b/DocService/sources/DocsCoServer.js index 1be8e805..def5f931 100644 --- a/DocService/sources/DocsCoServer.js +++ b/DocService/sources/DocsCoServer.js @@ -152,6 +152,7 @@ const redisKeyForceSave = cfgRedisPrefix + constants.REDIS_KEY_FORCE_SAVE; const redisKeyForceSaveTimer = cfgRedisPrefix + constants.REDIS_KEY_FORCE_SAVE_TIMER; const redisKeyForceSaveTimerLock = cfgRedisPrefix + constants.REDIS_KEY_FORCE_SAVE_TIMER_LOCK; const redisKeySaved = cfgRedisPrefix + constants.REDIS_KEY_SAVED; +const redisKeyPresenceUniqueUsers = cfgRedisPrefix + constants.REDIS_KEY_PRESENCE_UNIQUE_USERS; const EditorTypes = { document : 0, @@ -471,7 +472,7 @@ function getConnectionInfo(conn) { return JSON.stringify(data); } function updatePresenceCommandsToArray(outCommands, docId, userId, userInfo) { - var expireAt = new Date().getTime() + cfgExpPresence * 1000; + const expireAt = new Date().getTime() + cfgExpPresence * 1000; outCommands.push( ['zadd', redisKeyPresenceSet + docId, expireAt, userId], ['hset', redisKeyPresenceHash + docId, userId, userInfo], @@ -480,13 +481,20 @@ function updatePresenceCommandsToArray(outCommands, docId, userId, userInfo) { ); } function* updatePresence(docId, userId, connInfo) { - var multi = redisClient.multi(getUpdatePresenceCommands(docId, userId, connInfo)); + const multi = redisClient.multi(getUpdatePresenceCommands(docId, userId, connInfo)); yield utils.promiseRedis(multi, multi.exec); } +function* updateEditUsers(userId) { + if (!licenseInfo.usersCount) { + return; + } + const expireAt = new Date().getTime() + licenseInfo.usersExpire * 1000; + yield utils.promiseRedis(redisClient, redisClient.zadd, redisKeyPresenceUniqueUsers, expireAt, userId); +} function getUpdatePresenceCommands(docId, userId, connInfo) { - let commands = []; + const commands = []; updatePresenceCommandsToArray(commands, docId, userId, connInfo); - var expireAt = new Date().getTime() + cfgExpPresence * 1000; + const expireAt = new Date().getTime() + cfgExpPresence * 1000; commands.push(['zadd', redisKeyDocuments, expireAt, docId]); return commands; } @@ -1882,6 +1890,15 @@ exports.install = function(server, callbackFunction) { conn.sessionTimeLastAction = new Date().getTime() - data.sessionTimeIdle; } + if (!conn.user.view) { + let res = yield* _checkLicenseAuth(conn.user.idOriginal); + if (c_LR.Success !== res && c_LR.SuccessLimit !== res) { + conn.user.view = true; + } else { + yield* updateEditUsers(conn.user.idOriginal); + } + } + // Ситуация, когда пользователь уже отключен от совместного редактирования if (bIsRestore && data.isCloseCoAuthoring) { conn.sessionId = data.sessionId;//restore old @@ -2512,89 +2529,110 @@ exports.install = function(server, callbackFunction) { return {res: !isLock, documentLocks: documentLocks}; } - function _checkLicense(conn) { - return co(function* () { - try { - const c_LR = constants.LICENSE_RESULT; - let licenseType = licenseInfo.type; - // Warning. Cluster version or if workers > 1 will work with increasing numbers. - let connectionsCount = 0; - if (constants.PACKAGE_TYPE_OS === licenseInfo.packageType && c_LR.Error === licenseType) { - connectionsCount = constants.LICENSE_CONNECTIONS; - } else if (c_LR.Success === licenseType) { - connectionsCount = licenseInfo.connections; - } - if (connectionsCount) { - const editConnectionsCount = (_.filter(connections, function (el) { - return true !== el.isCloseCoAuthoring && el.user.view !== true; - })).length; - licenseType = (connectionsCount > editConnectionsCount) ? c_LR.Success : c_LR.Connections; + function _checkLicense(conn) { + return co(function* () { + try { + const c_LR = constants.LICENSE_RESULT; + let licenseType = licenseInfo.type; + if (constants.PACKAGE_TYPE_OS === licenseInfo.packageType && c_LR.Error === licenseType) { + licenseType = c_LR.Success; + } + let rights = constants.RIGHTS.Edit; + if (config.get('server.edit_singleton')) { + // ToDo docId from url ? + const docIdParsed = urlParse.exec(conn.url); + if (docIdParsed && 1 < docIdParsed.length) { + const participantsMap = yield* getParticipantMap(docIdParsed[1]); + for (let i = 0; i < participantsMap.length; ++i) { + const elem = participantsMap[i]; + if (!elem.view) { + rights = constants.RIGHTS.View; + break; + } + } + } + } + + sendData(conn, { + type: 'license', license: { + type: licenseType, + light: licenseInfo.light, + mode: licenseInfo.mode, + rights: rights, + buildVersion: commonDefines.buildVersion, + buildNumber: commonDefines.buildNumber, + branding: licenseInfo.branding + } + }); + } catch (err) { + logger.error('_checkLicense error:\r\n%s', err.stack); + } + }); + } + + function* _checkLicenseAuth(userId) { + const c_LR = constants.LICENSE_RESULT; + let licenseType = licenseInfo.type; + if (licenseInfo.usersCount) { + if (c_LR.Success === licenseType) { + const usersCount = yield utils.promiseRedis(redisClient, redisClient.zcount, + redisKeyPresenceUniqueUsers, '-inf', '+inf'); + if (licenseInfo.usersCount > usersCount) { + licenseType = c_LR.Success; + } else { + let rank = yield utils.promiseRedis(redisClient, redisClient.zrank, redisKeyPresenceUniqueUsers, + userId); + licenseType = null !== rank ? c_LR.Success : c_LR.UsersCount; + } + } + } else { + // Warning. Cluster version or if workers > 1 will work with increasing numbers. + let connectionsCount = 0; + if (constants.PACKAGE_TYPE_OS === licenseInfo.packageType && c_LR.Error === licenseType) { + connectionsCount = constants.LICENSE_CONNECTIONS; + } else if (c_LR.Success === licenseType) { + connectionsCount = licenseInfo.connections; + } + if (connectionsCount) { + const editConnectionsCount = (_.filter(connections, function (el) { + return true !== el.isCloseCoAuthoring && el.user.view !== true; + })).length; + licenseType = (connectionsCount > editConnectionsCount) ? c_LR.Success : c_LR.Connections; + } + /*if (constants.PACKAGE_TYPE_OS === licenseInfo.packageType && c_LR.Error === licenseType) { + licenseType = c_LR.SuccessLimit; + + const count = constants.LICENSE_CONNECTIONS; + let cursor = '0', sum = 0, scanRes, tmp, length, i, users; + while (true) { + scanRes = yield utils.promiseRedis(redisClient, redisClient.scan, cursor, 'MATCH', redisKeyPresenceHash + '*'); + tmp = scanRes[1]; + sum += (length = tmp.length); + + for (i = 0; i < length; ++i) { + if (sum >= count) { + licenseType = c_LR.Connections; + break; + } + + users = yield utils.promiseRedis(redisClient, redisClient.hlen, tmp[i]); + sum += users - (0 !== users ? 1 : 0); + } + + if (sum >= count) { + licenseType = c_LR.Connections; + break; + } + + cursor = scanRes[0]; + if ('0' === cursor) { + break; + } + } + }*/ } - /*if (constants.PACKAGE_TYPE_OS === licenseInfo.packageType && c_LR.Error === licenseType) { - licenseType = c_LR.SuccessLimit; - - const count = constants.LICENSE_CONNECTIONS; - let cursor = '0', sum = 0, scanRes, tmp, length, i, users; - while (true) { - scanRes = yield utils.promiseRedis(redisClient, redisClient.scan, cursor, 'MATCH', redisKeyPresenceHash + '*'); - tmp = scanRes[1]; - sum += (length = tmp.length); - - for (i = 0; i < length; ++i) { - if (sum >= count) { - licenseType = c_LR.Connections; - break; - } - - users = yield utils.promiseRedis(redisClient, redisClient.hlen, tmp[i]); - sum += users - (0 !== users ? 1 : 0); - } - - if (sum >= count) { - licenseType = c_LR.Connections; - break; - } - - cursor = scanRes[0]; - if ('0' === cursor) { - break; - } - } - }*/ - - let rights = constants.RIGHTS.Edit; - if (config.get('server.edit_singleton')) { - // ToDo docId from url ? - const docIdParsed = urlParse.exec(conn.url); - if (docIdParsed && 1 < docIdParsed.length) { - const participantsMap = yield* getParticipantMap(docIdParsed[1]); - for (let i = 0; i < participantsMap.length; ++i) { - const elem = participantsMap[i]; - if (!elem.view) { - rights = constants.RIGHTS.View; - break; - } - } - } - } - - sendData(conn, { - type: 'license', - license: { - type: licenseType, - light: licenseInfo.light, - mode: licenseInfo.mode, - rights: rights, - buildVersion: commonDefines.buildVersion, - buildNumber: commonDefines.buildNumber, - branding: licenseInfo.branding - } - }); - } catch (err) { - logger.error('_checkLicense error:\r\n%s', err.stack); - } - }); - } + return licenseType; + } sockjs_echo.installHandlers(server, {prefix: '/doc/['+constants.DOC_ID_PATTERN+']*/c', log: function(severity, message) { //TODO: handle severity