Feature/multi tenant (#377)

* [feature] Add tenantManager

* [fix] Fix export

* [schema] Change schema for tenants

* [de] For auth

* [config] Remove unused secret.browser param to unify with multitenancy

* [feature] Add OperationContext to store state of request

* [log] Remove docId and userId from log message

* [feature] Add OperationContext class

* [feature] For logging

* [feature] Add content to some methods

* [feature] For multitenancy

* [feature] For multitenancy

* [feature] For multitenancy

* [feature] For multitenancy

* [feature] For multitenancy

* [feature] For multitenancy

* [feature] Move all tenant logic to tenantManager

* [feature] Fix reading of tenant license

* [feature] Move tenant logic to EditorData interface

* [feature] Use context in SQL queries

* [feature] Refactoring

* [feature] Fix editorDataMemory

* [feature] Fix before merge
This commit is contained in:
Sergey Konovalov
2022-08-02 17:34:06 +03:00
committed by GitHub
parent 0c215cefa9
commit 41399fa3d4
32 changed files with 2159 additions and 1661 deletions

View File

@ -97,6 +97,13 @@
"privateKeyOld": "MIIEowIBAAKCAQEAqnro3nUUjvZK1i7UqeOlXmCrVPiDtHlRgIPReAjt2nKL1GG3SBXO6N0aPbiM5rtK0XRPUoLmKu2rYvSJ/Kmkdp14a/3uiEl788VVn0hb/l9OuQtH3HBjmM0/LKRgJQuU3LgHI67uRVZYtSJ/n9fYdZqnLfveLsrgZpgRCoabrp+H5Uem9N+x0OJR3LpToVRZhzSkYQrxnERJmF3bhR5yF8Zn+3BoSiUpVOCAvJRAYl8cAIs3BwQcTEyXJjnt+wW5Q1VyKr+bXp/39+tnugQeTe1jjdPy6rOTftQwzjro81oZpOMazwwR1aeQuQWCrmHQZqyV3Rvo6X3xYlOQnlo1/wIDAQABAoIBAQCKtUSBs8tNYrGTQTlBHXrwpkDg+u7WSZt5sEcfnkxA39BLtlHU8gGO0E9Ihr8GAL+oWjUsEltJ9GTtN8CJ9lFdPVS8sTiCZR/YQOggmFRZTJyVzMrkXgF7Uwwiu3+KxLiTOZx9eRhfDBlTD8W9fXaegX2i2Xp2ohUhBHthEBLdaZTWFi5Sid/Y0dDzBeP6UIJorZ5D+1ybaeIVHjndpwNsIEUGUxPFLrkeiU8Rm4MJ9ahxfywcP7DjQoPGY9Ge5cBhpxfzERWf732wUD6o3+L9tvOBU00CLVjULbGZKTVE2FJMyXK9jr6Zor9Mkhomp6/8Agkr9rp+TPyelFGYEz8hAoGBAOEc09CrL3eYBkhNEcaMQzxBLvOGpg8kaDX5SaArHfl9+U9yzRqss4ARECanp9HuHfjMQo7iejao0ngDjL7BNMSaH74QlSsPOY2iOm8Qvx8/zb7g4h9r1zLjFZb3mpSA4snRZvvdiZ9ugbuVPmhXnDzRRMg45MibJeeOTJNylofRAoGBAMHfF/WutqKDoX25qZo9m74W4bttOj6oIDk1N4/c6M1Z1v/aptYSE06bkWngj9P46kqjaay4hgMtzyGruc5aojPx5MHHf5bo14+Jv4NzYtR2llrUxO+UJX7aCfUYXI7RC93GUmhpeQ414j7SNAXec58d7e+ETw+6cHiAWO4uOSTPAoGATPq5qDLR4Zi4FUNdn8LZPyKfNqHF6YmupT5hIgd8kZO1jKiaYNPL8jBjkIRmjBBcaXcYD5p85nImvumf2J9jNxPpZOpwyC/Fo5xlVROp97qu1eY7DTmodntXJ6/2SXAlnZQhHmHsrPtyG752f+HtyJJbbgiem8cKWDu+DfHybfECgYBbSLo1WiBwgN4nHqZ3E48jgA6le5azLeKOTTpuKKwNFMIhEkj//t7MYn+jhLL0Mf3PSwZU50Vidc1To1IHkbFSGBGIFHFFEzl8QnXEZS4hr/y3o/teezj0c6HAn8nlDRUzRVBEDXWMdV6kCcGpCccTIrqHzpqTY0vV0UkOTQFnDQKBgAxSEhm/gtCYJIMCBe+KBJT9uECV5xDQopTTjsGOkd4306EN2dyPOIlAfwM6K/0qWisa0Ei5i8TbRRuBeTTdLEYLqXCJ7fj5tdD1begBdSVtHQ2WHqzPJSuImTkFi9NXxd1XUyZFM3y6YQvlssSuL7QSxUIEtZHnrJTt3QDd10dj", "privateKeyOld": "MIIEowIBAAKCAQEAqnro3nUUjvZK1i7UqeOlXmCrVPiDtHlRgIPReAjt2nKL1GG3SBXO6N0aPbiM5rtK0XRPUoLmKu2rYvSJ/Kmkdp14a/3uiEl788VVn0hb/l9OuQtH3HBjmM0/LKRgJQuU3LgHI67uRVZYtSJ/n9fYdZqnLfveLsrgZpgRCoabrp+H5Uem9N+x0OJR3LpToVRZhzSkYQrxnERJmF3bhR5yF8Zn+3BoSiUpVOCAvJRAYl8cAIs3BwQcTEyXJjnt+wW5Q1VyKr+bXp/39+tnugQeTe1jjdPy6rOTftQwzjro81oZpOMazwwR1aeQuQWCrmHQZqyV3Rvo6X3xYlOQnlo1/wIDAQABAoIBAQCKtUSBs8tNYrGTQTlBHXrwpkDg+u7WSZt5sEcfnkxA39BLtlHU8gGO0E9Ihr8GAL+oWjUsEltJ9GTtN8CJ9lFdPVS8sTiCZR/YQOggmFRZTJyVzMrkXgF7Uwwiu3+KxLiTOZx9eRhfDBlTD8W9fXaegX2i2Xp2ohUhBHthEBLdaZTWFi5Sid/Y0dDzBeP6UIJorZ5D+1ybaeIVHjndpwNsIEUGUxPFLrkeiU8Rm4MJ9ahxfywcP7DjQoPGY9Ge5cBhpxfzERWf732wUD6o3+L9tvOBU00CLVjULbGZKTVE2FJMyXK9jr6Zor9Mkhomp6/8Agkr9rp+TPyelFGYEz8hAoGBAOEc09CrL3eYBkhNEcaMQzxBLvOGpg8kaDX5SaArHfl9+U9yzRqss4ARECanp9HuHfjMQo7iejao0ngDjL7BNMSaH74QlSsPOY2iOm8Qvx8/zb7g4h9r1zLjFZb3mpSA4snRZvvdiZ9ugbuVPmhXnDzRRMg45MibJeeOTJNylofRAoGBAMHfF/WutqKDoX25qZo9m74W4bttOj6oIDk1N4/c6M1Z1v/aptYSE06bkWngj9P46kqjaay4hgMtzyGruc5aojPx5MHHf5bo14+Jv4NzYtR2llrUxO+UJX7aCfUYXI7RC93GUmhpeQ414j7SNAXec58d7e+ETw+6cHiAWO4uOSTPAoGATPq5qDLR4Zi4FUNdn8LZPyKfNqHF6YmupT5hIgd8kZO1jKiaYNPL8jBjkIRmjBBcaXcYD5p85nImvumf2J9jNxPpZOpwyC/Fo5xlVROp97qu1eY7DTmodntXJ6/2SXAlnZQhHmHsrPtyG752f+HtyJJbbgiem8cKWDu+DfHybfECgYBbSLo1WiBwgN4nHqZ3E48jgA6le5azLeKOTTpuKKwNFMIhEkj//t7MYn+jhLL0Mf3PSwZU50Vidc1To1IHkbFSGBGIFHFFEzl8QnXEZS4hr/y3o/teezj0c6HAn8nlDRUzRVBEDXWMdV6kCcGpCccTIrqHzpqTY0vV0UkOTQFnDQKBgAxSEhm/gtCYJIMCBe+KBJT9uECV5xDQopTTjsGOkd4306EN2dyPOIlAfwM6K/0qWisa0Ei5i8TbRRuBeTTdLEYLqXCJ7fj5tdD1begBdSVtHQ2WHqzPJSuImTkFi9NXxd1XUyZFM3y6YQvlssSuL7QSxUIEtZHnrJTt3QDd10dj",
"refreshLockInterval": "10m" "refreshLockInterval": "10m"
}, },
"tenants": {
"baseDir": "",
"baseDomain": "",
"filenameSecret": "secret.key",
"filenameLicense": "license.lic",
"defaultTetant": "tetant"
},
"services": { "services": {
"CoAuthoring": { "CoAuthoring": {
"server": { "server": {
@ -200,8 +207,7 @@
"allowMetaIPAddress": true "allowMetaIPAddress": true
}, },
"secret": { "secret": {
"browser": {"string": "secret", "file": "", "tenants": {}}, "inbox": {"string": "secret", "file": ""},
"inbox": {"string": "secret", "file": "", "tenants": {}},
"outbox": {"string": "secret", "file": ""}, "outbox": {"string": "secret", "file": ""},
"session": {"string": "secret", "file": ""} "session": {"string": "secret", "file": ""}
}, },
@ -213,9 +219,6 @@
"outbox": false "outbox": false
} }
}, },
"browser": {
"secretFromInbox": true
},
"inbox": { "inbox": {
"header": "Authorization", "header": "Authorization",
"prefix": "Bearer ", "prefix": "Bearer ",

View File

@ -4,7 +4,7 @@
"type": "console", "type": "console",
"layout": { "layout": {
"type": "pattern", "type": "pattern",
"pattern": "%[[%d] [%p] %c -%] %.10000m" "pattern": "%[[%d] [%p] [%X{TENANT}] [%X{DOCID}] [%X{USERID}] %c -%] %.10000m"
} }
} }
}, },

View File

@ -4,7 +4,7 @@
"type": "console", "type": "console",
"layout": { "layout": {
"type": "pattern", "type": "pattern",
"pattern": "[%d] [%p] %c - %.10000m" "pattern": "[%d] [%p] [%X{TENANT}] [%X{DOCID}] [%X{USERID}] %c - %.10000m"
} }
} }
}, },

View File

@ -34,6 +34,7 @@
var config = require('config'); var config = require('config');
var container = require('rhea'); var container = require('rhea');
var logger = require('./logger'); var logger = require('./logger');
const operationContext = require('./operationContext');
const cfgRabbitSocketOptions = config.get('activemq.connectOptions'); const cfgRabbitSocketOptions = config.get('activemq.connectOptions');
@ -45,20 +46,20 @@ function connetPromise(reconnectOnConnectionError, closeCallback) {
let conn = container.create_container().connect(cfgRabbitSocketOptions); let conn = container.create_container().connect(cfgRabbitSocketOptions);
let isConnected = false; let isConnected = false;
conn.on('connection_open', function(context) { conn.on('connection_open', function(context) {
logger.debug('[AMQP] connected'); operationContext.global.logger.debug('[AMQP] connected');
isConnected = true; isConnected = true;
resolve(conn); resolve(conn);
}); });
conn.on('connection_error', function(context) { conn.on('connection_error', function(context) {
//todo //todo
logger.debug('[AMQP] connection_error %s', context.error); operationContext.global.logger.debug('[AMQP] connection_error %s', context.error);
}); });
conn.on('connection_close', function() { conn.on('connection_close', function() {
//todo //todo
logger.debug('[AMQP] conn close'); operationContext.global.logger.debug('[AMQP] conn close');
}); });
conn.on('disconnected', function(context) { conn.on('disconnected', function(context) {
logger.error('[AMQP] disconnected %s', context.error && context.error.stack); operationContext.global.logger.error('[AMQP] disconnected %s', context.error && context.error.stack);
if (isConnected) { if (isConnected) {
closeCallback(); closeCallback();
} else { } else {

View File

@ -708,6 +708,7 @@ CMailMergeSendData.prototype.setIsJsonKey = function(v) {
}; };
function TaskQueueData(data) { function TaskQueueData(data) {
if (data) { if (data) {
this['ctx'] = data['ctx'];
this['cmd'] = new InputCommand(data['cmd'], true); this['cmd'] = new InputCommand(data['cmd'], true);
this['toFile'] = data['toFile']; this['toFile'] = data['toFile'];
this['fromOrigin'] = data['fromOrigin']; this['fromOrigin'] = data['fromOrigin'];
@ -718,6 +719,7 @@ function TaskQueueData(data) {
this['dataKey'] = data['dataKey']; this['dataKey'] = data['dataKey'];
this['visibilityTimeout'] = data['visibilityTimeout']; this['visibilityTimeout'] = data['visibilityTimeout'];
} else { } else {
this['ctx'] = undefined;
this['cmd'] = undefined; this['cmd'] = undefined;
this['toFile'] = undefined; this['toFile'] = undefined;
this['fromOrigin'] = undefined; this['fromOrigin'] = undefined;
@ -730,6 +732,12 @@ function TaskQueueData(data) {
} }
} }
TaskQueueData.prototype = { TaskQueueData.prototype = {
getCtx : function() {
return this['ctx'];
},
setCtx : function(data) {
return this['ctx'] = data;
},
getCmd : function() { getCmd : function() {
return this['cmd']; return this['cmd'];
}, },

View File

@ -35,6 +35,7 @@
exports.DOC_ID_PATTERN = '0-9-.a-zA-Z_='; exports.DOC_ID_PATTERN = '0-9-.a-zA-Z_=';
exports.DOC_ID_REGEX = new RegExp("^[" + exports.DOC_ID_PATTERN + "]*$", 'i'); exports.DOC_ID_REGEX = new RegExp("^[" + exports.DOC_ID_PATTERN + "]*$", 'i');
exports.DOC_ID_REPLACE_REGEX = new RegExp("[^" + exports.DOC_ID_PATTERN + "]", 'g'); exports.DOC_ID_REPLACE_REGEX = new RegExp("[^" + exports.DOC_ID_PATTERN + "]", 'g');
exports.DOC_ID_SOCKET_PATTERN = new RegExp("^/doc/([" + exports.DOC_ID_PATTERN + "]*)/c.+", 'i');
exports.DOC_ID_MAX_LENGTH = 240; exports.DOC_ID_MAX_LENGTH = 240;
exports.USER_ID_MAX_LENGTH = 240;//255-240=15 symbols to make user id unique exports.USER_ID_MAX_LENGTH = 240;//255-240=15 symbols to make user id unique
exports.USER_NAME_MAX_LENGTH = 255; exports.USER_NAME_MAX_LENGTH = 255;
@ -46,6 +47,8 @@ exports.ONLY_OFFICE_URL_PARAM = 'ooname';
exports.DISPLAY_PREFIX = 'display'; exports.DISPLAY_PREFIX = 'display';
exports.CHANGES_NAME = 'changes'; exports.CHANGES_NAME = 'changes';
exports.VIEWER_ONLY = /^(?:(pdf|djvu|xps|oxps))$/; exports.VIEWER_ONLY = /^(?:(pdf|djvu|xps|oxps))$/;
exports.DEFAULT_DOC_ID = 'docId';
exports.DEFAULT_USER_ID = 'userId';
exports.RIGHTS = { exports.RIGHTS = {
None : 0, None : 0,

View File

@ -34,7 +34,6 @@
var path = require('path'); var path = require('path');
var constants = require('./constants'); var constants = require('./constants');
var logger = require('./logger');
function getImageFormatBySignature(buffer) { function getImageFormatBySignature(buffer) {
var length = buffer.length; var length = buffer.length;
@ -500,7 +499,7 @@ exports.getStringFromFormat = function(format) {
return ''; return '';
} }
}; };
exports.getImageFormat = function(buffer, optExt) { exports.getImageFormat = function(ctx, buffer, optExt) {
var format = constants.AVS_OFFICESTUDIO_FILE_UNKNOWN; var format = constants.AVS_OFFICESTUDIO_FILE_UNKNOWN;
try { try {
//signature //signature
@ -519,8 +518,7 @@ exports.getImageFormat = function(buffer, optExt) {
} }
} }
catch (e) { catch (e) {
logger.error(optExt); ctx.logger.error('error getImageFormat ext=%s: %s', optExt, e.stack);
logger.error('error getImageFormat:\r\n%s', e.stack);
} }
return format; return format;
}; };
@ -540,7 +538,6 @@ exports.isPresentationFormat = function(format) {
format === constants.AVS_OFFICESTUDIO_FILE_TEAMLAB_PPTY; format === constants.AVS_OFFICESTUDIO_FILE_TEAMLAB_PPTY;
}; };
exports.isOOXFormat = function(format) { exports.isOOXFormat = function(format) {
console.log('isOOXFormat'+format);
return constants.AVS_OFFICESTUDIO_FILE_DOCUMENT_DOCX === format return constants.AVS_OFFICESTUDIO_FILE_DOCUMENT_DOCX === format
|| constants.AVS_OFFICESTUDIO_FILE_DOCUMENT_DOCM === format || constants.AVS_OFFICESTUDIO_FILE_DOCUMENT_DOCM === format
|| constants.AVS_OFFICESTUDIO_FILE_DOCUMENT_DOTX === format || constants.AVS_OFFICESTUDIO_FILE_DOCUMENT_DOTX === format

View File

@ -73,7 +73,9 @@ if (config.get('log.options.replaceConsole')) {
console.error = logger.error.bind(logger); console.error = logger.error.bind(logger);
console.debug = logger.debug.bind(logger); console.debug = logger.debug.bind(logger);
} }
exports.getLogger = function (){
return log4js.getLogger.apply(log4js, Array.prototype.slice.call(arguments));
};
exports.trace = function (){ exports.trace = function (){
return logger.trace.apply(logger, Array.prototype.slice.call(arguments)); return logger.trace.apply(logger, Array.prototype.slice.call(arguments));
}; };

View File

@ -0,0 +1,91 @@
/*
* (c) Copyright Ascensio System SIA 2010-2019
*
* This program is a free software product. You can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License (AGPL)
* version 3 as published by the Free Software Foundation. In accordance with
* Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect
* that Ascensio System SIA expressly excludes the warranty of non-infringement
* of any third-party rights.
*
* This program is distributed WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For
* details, see the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
*
* You can contact Ascensio System SIA at 20A-12 Ernesta Birznieka-Upisha
* street, Riga, Latvia, EU, LV-1050.
*
* The interactive user interfaces in modified source and object code versions
* of the Program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU AGPL version 3.
*
* Pursuant to Section 7(b) of the License you must retain the original Product
* logo when distributing the program. Pursuant to Section 7(e) we decline to
* grant you any rights under trademark law for use of our trademarks.
*
* All the Product's GUI elements, including illustrations and icon sets, as
* well as technical writing content are licensed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International. See the License
* terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
*
*/
'use strict';
const utils = require('./utils');
const logger = require('./logger');
const constants = require('./constants');
const tenantManager = require('./tenantManager');
function Context(){
this.logger = logger.getLogger('nodeJS');
this.initDefault();
}
Context.prototype.init = function(tenant, docId, userId) {
this.setTenant(tenant);
this.setDocId(docId);
this.setUserId(userId);
};
Context.prototype.initDefault = function() {
this.init(tenantManager.getDefautTenant(), constants.DEFAULT_DOC_ID, constants.DEFAULT_USER_ID);
};
Context.prototype.initFromConnection = function(conn) {
let tenant = tenantManager.getTenantByConnection(this, conn);
let docId = conn.docid;
if (!docId) {
const docIdParsed = constants.DOC_ID_SOCKET_PATTERN.exec(conn.url);
if (docIdParsed && 1 < docIdParsed.length) {
docId = docIdParsed[1];
}
}
let userId = conn.user?.id;
this.init(tenant, docId || this.docId, userId || this.userId);
};
Context.prototype.initFromRequest = function(req) {
let tenant = tenantManager.getTenantByRequest(this, req);
this.init(tenant, this.docId, this.userId);
};
Context.prototype.initFromTaskQueueData = function(task) {
let ctx = task.getCtx();
this.init(ctx.tenant, ctx.docId, ctx.userId);
};
Context.prototype.initFromPubSub = function(data) {
let ctx = data.ctx;
this.init(ctx.tenant, ctx.docId, ctx.userId);
};
Context.prototype.setTenant = function(tenant) {
this.tenant = tenant;
this.logger.addContext('TENANT', tenant);
};
Context.prototype.setDocId = function(docId) {
this.docId = docId;
this.logger.addContext('DOCID', docId);
};
Context.prototype.setUserId = function(userId) {
this.userId = userId;
this.logger.addContext('USERID', userId);
};
exports.Context = Context;
exports.global = new Context();

View File

@ -34,6 +34,7 @@
var config = require('config'); var config = require('config');
var amqp = require('amqplib/callback_api'); var amqp = require('amqplib/callback_api');
var logger = require('./logger'); var logger = require('./logger');
const operationContext = require('./operationContext');
var cfgRabbitUrl = config.get('rabbitmq.url'); var cfgRabbitUrl = config.get('rabbitmq.url');
var cfgRabbitSocketOptions = config.get('rabbitmq.socketOptions'); var cfgRabbitSocketOptions = config.get('rabbitmq.socketOptions');
@ -45,7 +46,7 @@ function connetPromise(reconnectOnConnectionError, closeCallback) {
function startConnect() { function startConnect() {
amqp.connect(cfgRabbitUrl, cfgRabbitSocketOptions, function(err, conn) { amqp.connect(cfgRabbitUrl, cfgRabbitSocketOptions, function(err, conn) {
if (null != err) { if (null != err) {
logger.error('[AMQP] %s', err.stack); operationContext.global.logger.error('[AMQP] %s', err.stack);
if (reconnectOnConnectionError) { if (reconnectOnConnectionError) {
setTimeout(startConnect, RECONNECT_TIMEOUT); setTimeout(startConnect, RECONNECT_TIMEOUT);
} else { } else {
@ -53,16 +54,16 @@ function connetPromise(reconnectOnConnectionError, closeCallback) {
} }
} else { } else {
conn.on('error', function(err) { conn.on('error', function(err) {
logger.error('[AMQP] conn error', err.stack); operationContext.global.logger.error('[AMQP] conn error', err.stack);
}); });
var closeEventCallback = function() { var closeEventCallback = function() {
//in some case receive multiple close events //in some case receive multiple close events
conn.removeListener('close', closeEventCallback); conn.removeListener('close', closeEventCallback);
logger.debug('[AMQP] conn close'); operationContext.global.logger.debug('[AMQP] conn close');
closeCallback(); closeCallback();
}; };
conn.on('close', closeEventCallback); conn.on('close', closeEventCallback);
logger.debug('[AMQP] connected'); operationContext.global.logger.debug('[AMQP] connected');
resolve(conn); resolve(conn);
} }
}); });

View File

@ -33,84 +33,99 @@
'use strict'; 'use strict';
var config = require('config'); var config = require('config');
var utils = require('./utils'); var utils = require('./utils');
var logger = require('./logger');
var storage = require('./' + config.get('storage.name')); var storage = require('./' + config.get('storage.name'));
function getStoragePath(strPath) { var tenantManager = require('./tenantManager');
return strPath.replace(/\\/g, '/');
function getStoragePath(ctx, strPath) {
return tenantManager.getTenantPathPrefix(ctx) + strPath.replace(/\\/g, '/')
} }
exports.headObject = function(strPath) {
return storage.headObject(getStoragePath(strPath)); exports.headObject = function(ctx, strPath) {
return storage.headObject(getStoragePath(ctx, strPath));
}; };
exports.getObject = function(strPath) { exports.getObject = function(ctx, strPath) {
return storage.getObject(getStoragePath(strPath)); return storage.getObject(getStoragePath(ctx, strPath));
}; };
exports.createReadStream = function(strPath) { exports.createReadStream = function(ctx, strPath) {
return storage.createReadStream(getStoragePath(strPath)); return storage.createReadStream(getStoragePath(ctx, strPath));
}; };
exports.putObject = function(strPath, buffer, contentLength) { exports.putObject = function(ctx, strPath, buffer, contentLength) {
return storage.putObject(getStoragePath(strPath), buffer, contentLength); return storage.putObject(getStoragePath(ctx, strPath), buffer, contentLength);
}; };
exports.uploadObject = function(strPath, filePath) { exports.uploadObject = function(ctx, strPath, filePath) {
return storage.uploadObject(strPath, filePath); return storage.uploadObject(getStoragePath(ctx, strPath), filePath);
}; };
exports.copyObject = function(sourceKey, destinationKey) { exports.copyObject = function(ctx, sourceKey, destinationKey) {
return storage.copyObject(sourceKey, destinationKey); let storageSrc = getStoragePath(ctx, sourceKey);
let storageDst = getStoragePath(ctx, destinationKey);
return storage.copyObject(storageSrc, storageDst);
}; };
exports.copyPath = function(sourcePath, destinationPath) { exports.copyPath = function(ctx, sourcePath, destinationPath) {
return exports.listObjects(getStoragePath(sourcePath)).then(function(list) { let storageSrc = getStoragePath(ctx, sourcePath);
let storageDst = getStoragePath(ctx, destinationPath);
return storage.listObjects(storageSrc).then(function(list) {
return Promise.all(list.map(function(curValue) { return Promise.all(list.map(function(curValue) {
return exports.copyObject(curValue, destinationPath + '/' + exports.getRelativePath(sourcePath, curValue)); return storage.copyObject(curValue, storageDst + '/' + exports.getRelativePath(storageSrc, curValue));
})); }));
}); });
}; };
exports.listObjects = function(strPath) { exports.listObjects = function(ctx, strPath) {
return storage.listObjects(getStoragePath(strPath)).catch(function(e) { let prefix = getStoragePath(ctx, "");
logger.error('storage.listObjects:\r\n%s', e.stack); return storage.listObjects(getStoragePath(ctx, strPath)).then(function(list) {
return list.map((currentValue) => {
return currentValue.substring(prefix.length);
});
}).catch(function(e) {
ctx.logger.error('storage.listObjects: %s', e.stack);
return []; return [];
}); });
}; };
exports.deleteObject = function(strPath) { exports.deleteObject = function(ctx, strPath) {
return storage.deleteObject(getStoragePath(strPath)); return storage.deleteObject(getStoragePath(ctx, strPath));
}; };
exports.deleteObjects = function(strPaths) { exports.deleteObjects = function(ctx, strPaths) {
var StoragePaths = strPaths.map(function(curValue) { var StoragePaths = strPaths.map(function(curValue) {
return getStoragePath(curValue); return getStoragePath(ctx, curValue);
}); });
return storage.deleteObjects(StoragePaths); return storage.deleteObjects(StoragePaths);
}; };
exports.deletePath = function(strPath) { exports.deletePath = function(ctx, strPath) {
return exports.listObjects(getStoragePath(strPath)).then(function(list) { let storageSrc = getStoragePath(ctx, strPath);
return exports.deleteObjects(list); return storage.listObjects(storageSrc).then(function(list) {
return storage.deleteObjects(list);
}); });
}; };
exports.getSignedUrl = function(baseUrl, strPath, urlType, optFilename, opt_creationDate) { exports.getSignedUrl = function(ctx, baseUrl, strPath, urlType, optFilename, opt_creationDate) {
return storage.getSignedUrl(baseUrl, getStoragePath(strPath), urlType, optFilename, opt_creationDate); return storage.getSignedUrl(baseUrl, getStoragePath(ctx, strPath), urlType, optFilename, opt_creationDate);
}; };
exports.getSignedUrls = function(baseUrl, strPath, urlType, opt_creationDate) { exports.getSignedUrls = function(ctx, baseUrl, strPath, urlType, opt_creationDate) {
return exports.listObjects(getStoragePath(strPath)).then(function(list) { let storageSrc = getStoragePath(ctx, strPath);
return storage.listObjects(storageSrc).then(function(list) {
return Promise.all(list.map(function(curValue) { return Promise.all(list.map(function(curValue) {
return exports.getSignedUrl(baseUrl, curValue, urlType, undefined, opt_creationDate); return storage.getSignedUrl(baseUrl, curValue, urlType, undefined, opt_creationDate);
})).then(function(urls) { })).then(function(urls) {
var outputMap = {}; var outputMap = {};
for (var i = 0; i < list.length && i < urls.length; ++i) { for (var i = 0; i < list.length && i < urls.length; ++i) {
outputMap[exports.getRelativePath(strPath, list[i])] = urls[i]; outputMap[exports.getRelativePath(storageSrc, list[i])] = urls[i];
} }
return outputMap; return outputMap;
}); });
}); });
}; };
exports.getSignedUrlsArrayByArray = function(baseUrl, list, urlType) { exports.getSignedUrlsArrayByArray = function(ctx, baseUrl, list, urlType) {
return Promise.all(list.map(function(curValue) { return Promise.all(list.map(function(curValue) {
return exports.getSignedUrl(baseUrl, curValue, urlType, undefined); let storageSrc = getStoragePath(ctx, curValue);
return storage.getSignedUrl(baseUrl, storageSrc, urlType, undefined);
})); }));
}; };
exports.getSignedUrlsByArray = function(baseUrl, list, optPath, urlType) { exports.getSignedUrlsByArray = function(ctx, baseUrl, list, optPath, urlType) {
return exports.getSignedUrlsArrayByArray(baseUrl, list, urlType).then(function(urls) { return exports.getSignedUrlsArrayByArray(ctx, baseUrl, list, urlType).then(function(urls) {
var outputMap = {}; var outputMap = {};
for (var i = 0; i < list.length && i < urls.length; ++i) { for (var i = 0; i < list.length && i < urls.length; ++i) {
if (optPath) { if (optPath) {
outputMap[exports.getRelativePath(optPath, list[i])] = urls[i]; let storageSrc = getStoragePath(ctx, optPath);
outputMap[exports.getRelativePath(storageSrc, list[i])] = urls[i];
} else { } else {
outputMap[list[i]] = urls[i]; outputMap[list[i]] = urls[i];
} }

View File

@ -41,6 +41,7 @@ var rabbitMQCore = require('./rabbitMQCore');
var activeMQCore = require('./activeMQCore'); var activeMQCore = require('./activeMQCore');
const logger = require('./logger'); const logger = require('./logger');
const commonDefines = require('./commondefines'); const commonDefines = require('./commondefines');
const operationContext = require('./operationContext');
const cfgMaxRedeliveredCount = config.get('FileConverter.converter.maxRedeliveredCount'); const cfgMaxRedeliveredCount = config.get('FileConverter.converter.maxRedeliveredCount');
const cfgQueueType = config.get('queue.type'); const cfgQueueType = config.get('queue.type');
@ -253,7 +254,7 @@ function clear(taskqueue) {
function* pushBackRedeliveredRabbit(taskqueue, message, ack) { function* pushBackRedeliveredRabbit(taskqueue, message, ack) {
if (message?.fields?.redelivered) { if (message?.fields?.redelivered) {
try { try {
logger.warn('checkRedelivered redelivered data=%j', message); operationContext.global.logger.warn('checkRedelivered redelivered data=%j', message);
//remove current task and add new into tail of queue to remove redelivered flag //remove current task and add new into tail of queue to remove redelivered flag
let data = message.content.toString(); let data = message.content.toString();
let redeliveredCount = message.properties.headers['x-redelivered-count']; let redeliveredCount = message.properties.headers['x-redelivered-count'];
@ -264,7 +265,7 @@ function* pushBackRedeliveredRabbit(taskqueue, message, ack) {
yield taskqueue.addResponse(taskqueue.simulateErrorResponse(data)); yield taskqueue.addResponse(taskqueue.simulateErrorResponse(data));
} }
} catch (err) { } catch (err) {
logger.error('checkRedelivered error: %s', err.stack); operationContext.global.logger.error('checkRedelivered error: %s', err.stack);
} finally{ } finally{
ack(); ack();
} }
@ -274,14 +275,14 @@ function* pushBackRedeliveredRabbit(taskqueue, message, ack) {
} }
function* pushBackRedeliveredActive(taskqueue, context, ack) { function* pushBackRedeliveredActive(taskqueue, context, ack) {
if (undefined !== context.message.delivery_count) { if (undefined !== context.message.delivery_count) {
logger.warn('checkRedelivered redelivered data=%j', context.message); operationContext.global.logger.warn('checkRedelivered redelivered data=%j', context.message);
if (context.message.delivery_count > cfgMaxRedeliveredCount) { if (context.message.delivery_count > cfgMaxRedeliveredCount) {
try { try {
if (taskqueue.simulateErrorResponse) { if (taskqueue.simulateErrorResponse) {
yield taskqueue.addResponse(taskqueue.simulateErrorResponse(context.message.body)); yield taskqueue.addResponse(taskqueue.simulateErrorResponse(context.message.body));
} }
} catch (err) { } catch (err) {
logger.error('checkRedelivered error: %s', err.stack); operationContext.global.logger.error('checkRedelivered error: %s', err.stack);
} finally { } finally {
ack(); ack();
} }

View File

@ -0,0 +1,138 @@
/*
* (c) Copyright Ascensio System SIA 2010-2019
*
* This program is a free software product. You can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License (AGPL)
* version 3 as published by the Free Software Foundation. In accordance with
* Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect
* that Ascensio System SIA expressly excludes the warranty of non-infringement
* of any third-party rights.
*
* This program is distributed WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For
* details, see the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
*
* You can contact Ascensio System SIA at 20A-12 Ernesta Birznieka-Upisha
* street, Riga, Latvia, EU, LV-1050.
*
* The interactive user interfaces in modified source and object code versions
* of the Program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU AGPL version 3.
*
* Pursuant to Section 7(b) of the License you must retain the original Product
* logo when distributing the program. Pursuant to Section 7(e) we decline to
* grant you any rights under trademark law for use of our trademarks.
*
* All the Product's GUI elements, including illustrations and icon sets, as
* well as technical writing content are licensed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International. See the License
* terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
*
*/
'use strict';
const config = require('config');
const co = require('co');
const license = require('./../../Common/sources/license');
const constants = require('./../../Common/sources/constants');
const commonDefines = require('./../../Common/sources/commondefines');
const utils = require('./../../Common/sources/utils');
const { readFile } = require('fs/promises');
const path = require('path');
const oPackageType = config.get('license.packageType');
const cfgTenantsBaseDomain = config.get('tenants.baseDomain');
const cfgTenantsBaseDir = config.get('tenants.baseDir');
const cfgTenantsFilenameSecret = config.get('tenants.filenameSecret');
const cfgTenantsFilenameLicense = config.get('tenants.filenameLicense');
const cfgTenantsDefaultTetant = config.get('tenants.defaultTetant');
const cfgSecretInbox = config.get('services.CoAuthoring.secret.inbox');
const cfgSecretOutbox = config.get('services.CoAuthoring.secret.outbox');
const cfgSecretSession = config.get('services.CoAuthoring.secret.session');
let licenseInfo;
let licenseOriginal;
function getDefautTenant() {
return cfgTenantsDefaultTetant;
}
function getTenant(ctx, domain) {
let tenant = getDefautTenant();
if (domain) {
//remove port
domain = domain.substring(0, domain.indexOf(':'));
let index = domain.indexOf('.' + cfgTenantsBaseDomain);
if (-1 !== index) {
tenant = domain.substring(0, index);
} else {
ctx.logger.warn('getTenant invalid domain=%s', domain);
}
}
return tenant;
}
function getTenantByConnection(ctx, conn) {
return isMultitenantMode() ? getTenant(ctx, utils.getDomainByConnection(ctx, conn)) : getDefautTenant();
}
function getTenantByRequest(ctx, req) {
return isMultitenantMode() ? getTenant(ctx, utils.getDomainByRequest(ctx, req)) : getDefautTenant();
}
function getTenantPathPrefix(ctx) {
return isMultitenantMode() ? utils.removeIllegalCharacters(ctx.tenant) + '/' : '';
}
function getTenantSecret(ctx, type) {
return co(function*() {
let res = undefined;
if (isMultitenantMode()) {
let tenantPath = utils.removeIllegalCharacters(ctx.tenant);
let secretPath = path.join(cfgTenantsBaseDir, tenantPath, cfgTenantsFilenameSecret);
ctx.logger.debug('getTenantSecret path=%s', secretPath);
res = yield readFile(secretPath, {encoding: 'utf8'});
} else {
switch (type) {
case commonDefines.c_oAscSecretType.Browser:
case commonDefines.c_oAscSecretType.Inbox:
res = utils.getSecretByElem(cfgSecretInbox);
break;
case commonDefines.c_oAscSecretType.Outbox:
res = utils.getSecretByElem(cfgSecretOutbox);
break;
case commonDefines.c_oAscSecretType.Session:
res = utils.getSecretByElem(cfgSecretSession);
break;
}
}
return res;
});
}
function setDefLicense(data, original) {
licenseInfo = data;
licenseOriginal = original;
}
function getTenantLicense(ctx) {
return co(function*() {
let res = undefined;
if (isMultitenantMode()) {
let tenantPath = utils.removeIllegalCharacters(ctx.tenant);
let licensePath = path.join(cfgTenantsBaseDir, tenantPath, cfgTenantsFilenameLicense);
ctx.logger.debug('getTenantLicense path=%s', licensePath);
[res] = yield* license.readLicense(licensePath);
} else {
res = licenseInfo;
}
return res;
});
}
function isMultitenantMode() {
return !!cfgTenantsBaseDir && !!cfgTenantsBaseDomain;
}
exports.getDefautTenant = getDefautTenant;
exports.getTenantByConnection = getTenantByConnection;
exports.getTenantByRequest = getTenantByRequest;
exports.getTenantPathPrefix = getTenantPathPrefix;
exports.getTenantSecret = getTenantSecret;
exports.getTenantLicense = getTenantLicense;
exports.setDefLicense = setDefLicense;
exports.isMultitenantMode = isMultitenantMode;

View File

@ -72,7 +72,6 @@ var cfgTokenOutboxHeader = config.get('services.CoAuthoring.token.outbox.header'
var cfgTokenOutboxPrefix = config.get('services.CoAuthoring.token.outbox.prefix'); var cfgTokenOutboxPrefix = config.get('services.CoAuthoring.token.outbox.prefix');
var cfgTokenOutboxAlgorithm = config.get('services.CoAuthoring.token.outbox.algorithm'); var cfgTokenOutboxAlgorithm = config.get('services.CoAuthoring.token.outbox.algorithm');
var cfgTokenOutboxExpires = config.get('services.CoAuthoring.token.outbox.expires'); var cfgTokenOutboxExpires = config.get('services.CoAuthoring.token.outbox.expires');
var cfgSignatureSecretOutbox = config.get('services.CoAuthoring.secret.outbox');
var cfgVisibilityTimeout = config.get('queue.visibilityTimeout'); var cfgVisibilityTimeout = config.get('queue.visibilityTimeout');
var cfgQueueRetentionPeriod = config.get('queue.retentionPeriod'); var cfgQueueRetentionPeriod = config.get('queue.retentionPeriod');
var cfgRequestDefaults = config.get('services.CoAuthoring.requestDefaults'); var cfgRequestDefaults = config.get('services.CoAuthoring.requestDefaults');
@ -261,13 +260,13 @@ function raiseErrorObj(ro, error) {
function isRedirectResponse(response) { function isRedirectResponse(response) {
return response && response.statusCode >= 300 && response.statusCode < 400 && response.caseless.has('location'); return response && response.statusCode >= 300 && response.statusCode < 400 && response.caseless.has('location');
} }
function downloadUrlPromise(uri, optTimeout, optLimit, opt_Authorization, opt_filterPrivate, opt_headers, opt_streamWriter) { function downloadUrlPromise(ctx, uri, optTimeout, optLimit, opt_Authorization, opt_filterPrivate, opt_headers, opt_streamWriter) {
//todo replace deprecated request module //todo replace deprecated request module
const maxRedirects = (undefined !== cfgRequestDefaults.maxRedirects) ? cfgRequestDefaults.maxRedirects : 10; const maxRedirects = (undefined !== cfgRequestDefaults.maxRedirects) ? cfgRequestDefaults.maxRedirects : 10;
const followRedirect = (undefined !== cfgRequestDefaults.followRedirect) ? cfgRequestDefaults.followRedirect : true; const followRedirect = (undefined !== cfgRequestDefaults.followRedirect) ? cfgRequestDefaults.followRedirect : true;
var redirectsFollowed = 0; var redirectsFollowed = 0;
let doRequest = function(curUrl) { let doRequest = function(curUrl) {
return downloadUrlPromiseWithoutRedirect(curUrl, optTimeout, optLimit, opt_Authorization, opt_filterPrivate, opt_headers, opt_streamWriter) return downloadUrlPromiseWithoutRedirect(ctx, curUrl, optTimeout, optLimit, opt_Authorization, opt_filterPrivate, opt_headers, opt_streamWriter)
.catch(function(err) { .catch(function(err) {
let response = err.response; let response = err.response;
if (isRedirectResponse(response)) { if (isRedirectResponse(response)) {
@ -277,7 +276,7 @@ function downloadUrlPromise(uri, optTimeout, optLimit, opt_Authorization, opt_fi
redirectTo = url.resolve(err.request.uri.href, redirectTo) redirectTo = url.resolve(err.request.uri.href, redirectTo)
} }
logger.debug('downloadUrlPromise redirectsFollowed:%d redirectTo: %s', redirectsFollowed, redirectTo); ctx.logger.debug('downloadUrlPromise redirectsFollowed:%d redirectTo: %s', redirectsFollowed, redirectTo);
redirectsFollowed++; redirectsFollowed++;
return doRequest(redirectTo); return doRequest(redirectTo);
} }
@ -287,7 +286,7 @@ function downloadUrlPromise(uri, optTimeout, optLimit, opt_Authorization, opt_fi
}; };
return doRequest(uri); return doRequest(uri);
} }
function downloadUrlPromiseWithoutRedirect(uri, optTimeout, optLimit, opt_Authorization, opt_filterPrivate, opt_headers, opt_streamWriter) { function downloadUrlPromiseWithoutRedirect(ctx, uri, optTimeout, optLimit, opt_Authorization, opt_filterPrivate, opt_headers, opt_streamWriter) {
return new Promise(function (resolve, reject) { return new Promise(function (resolve, reject) {
//IRI to URI //IRI to URI
uri = URI.serialize(URI.parse(uri)); uri = URI.serialize(URI.parse(uri));
@ -323,7 +322,7 @@ function downloadUrlPromiseWithoutRedirect(uri, optTimeout, optLimit, opt_Author
} else { } else {
var contentLength = response.caseless.get('content-length'); var contentLength = response.caseless.get('content-length');
if (contentLength && body.length !== (contentLength - 0)) { if (contentLength && body.length !== (contentLength - 0)) {
logger.warn('downloadUrlPromise body size mismatch: uri=%s; content-length=%s; body.length=%d', uri, contentLength, body.length); ctx.logger.warn('downloadUrlPromise body size mismatch: uri=%s; content-length=%s; body.length=%d', uri, contentLength, body.length);
} }
resolve({response: response, body: body}); resolve({response: response, body: body});
} }
@ -654,6 +653,9 @@ function containsAllAsciiNP(str) {
return /^[\040-\176]*$/.test(str);//non-printing characters return /^[\040-\176]*$/.test(str);//non-printing characters
} }
exports.containsAllAsciiNP = containsAllAsciiNP; exports.containsAllAsciiNP = containsAllAsciiNP;
function getDomain(hostHeader, forwardedHostHeader) {
return forwardedHostHeader || hostHeader || 'localhost';
};
function getBaseUrl(protocol, hostHeader, forwardedProtoHeader, forwardedHostHeader, forwardedPrefixHeader) { function getBaseUrl(protocol, hostHeader, forwardedProtoHeader, forwardedHostHeader, forwardedPrefixHeader) {
var url = ''; var url = '';
if (forwardedProtoHeader) { if (forwardedProtoHeader) {
@ -664,13 +666,7 @@ function getBaseUrl(protocol, hostHeader, forwardedProtoHeader, forwardedHostHea
url += 'http'; url += 'http';
} }
url += '://'; url += '://';
if (forwardedHostHeader) { url += getDomain(hostHeader, forwardedHostHeader);
url += forwardedHostHeader;
} else if (hostHeader) {
url += hostHeader;
} else {
url += 'localhost';
}
if (forwardedPrefixHeader) { if (forwardedPrefixHeader) {
url += forwardedPrefixHeader; url += forwardedPrefixHeader;
} }
@ -684,6 +680,20 @@ function getBaseUrlByRequest(req) {
} }
exports.getBaseUrlByConnection = getBaseUrlByConnection; exports.getBaseUrlByConnection = getBaseUrlByConnection;
exports.getBaseUrlByRequest = getBaseUrlByRequest; exports.getBaseUrlByRequest = getBaseUrlByRequest;
function getDomainByConnection(ctx, conn) {
let host = conn.headers['host'];
let forwardedHost = conn.headers['x-forwarded-host'];
ctx.logger.debug("getDomainByConnection headers['host']=%s headers['x-forwarded-host']=%s", host, forwardedHost);
return getDomain(host, forwardedHost);
}
function getDomainByRequest(ctx, req) {
let host = req.get('host');
let forwardedHost = req.get('x-forwarded-host');
ctx.logger.debug("getDomainByRequest headers['host']=%s headers['x-forwarded-host']=%s", host, forwardedHost);
return getDomain(req.get('host'), req.get('x-forwarded-host'));
}
exports.getDomainByConnection = getDomainByConnection;
exports.getDomainByRequest = getDomainByRequest;
function stream2Buffer(stream) { function stream2Buffer(stream) {
return new Promise(function(resolve, reject) { return new Promise(function(resolve, reject) {
if (!stream.readable) { if (!stream.readable) {
@ -761,14 +771,14 @@ function checkIpFilter(ipString, opt_hostname) {
return status; return status;
} }
exports.checkIpFilter = checkIpFilter; exports.checkIpFilter = checkIpFilter;
function* checkHostFilter(hostname) { function* checkHostFilter(ctx, hostname) {
let status = 0; let status = 0;
let hostIp; let hostIp;
try { try {
hostIp = yield dnsLookup(hostname); hostIp = yield dnsLookup(hostname);
} catch (e) { } catch (e) {
status = cfgIpFilterErrorCode; status = cfgIpFilterErrorCode;
logger.error('dnsLookup error: hostname = %s\r\n%s', hostname, e.stack); ctx.logger.error('dnsLookup error: hostname = %s %s', hostname, e.stack);
} }
if (0 === status) { if (0 === status) {
status = checkIpFilter(hostIp, hostname); status = checkIpFilter(hostIp, hostname);
@ -832,29 +842,7 @@ function getSecretByElem(secretElem) {
return secret; return secret;
} }
exports.getSecretByElem = getSecretByElem; exports.getSecretByElem = getSecretByElem;
function getSecret(docId, secretElem, opt_iss, opt_token) { function fillJwtForRequest(payload, secret, opt_inBody) {
if (!isEmptyObject(secretElem.tenants)) {
var iss;
if (opt_token) {
//look for issuer
var decodedTemp = jwt.decode(opt_token);
if (decodedTemp && decodedTemp.iss) {
iss = decodedTemp.iss;
}
} else {
iss = opt_iss;
}
if (iss) {
secretElem = secretElem.tenants[iss];
if (!secretElem) {
logger.error('getSecret unknown issuer: docId = %s iss = %s', docId, iss);
}
}
}
return getSecretByElem(secretElem);
}
exports.getSecret = getSecret;
function fillJwtForRequest(payload, opt_inBody) {
//todo refuse prototypes in payload(they are simple getter/setter). //todo refuse prototypes in payload(they are simple getter/setter).
//JSON.parse/stringify is more universal but Object.assign is enough for our inputs //JSON.parse/stringify is more universal but Object.assign is enough for our inputs
payload = Object.assign(Object.create(null), payload); payload = Object.assign(Object.create(null), payload);
@ -866,7 +854,6 @@ function fillJwtForRequest(payload, opt_inBody) {
} }
let options = {algorithm: cfgTokenOutboxAlgorithm, expiresIn: cfgTokenOutboxExpires}; let options = {algorithm: cfgTokenOutboxAlgorithm, expiresIn: cfgTokenOutboxExpires};
let secret = getSecretByElem(cfgSignatureSecretOutbox);
return jwt.sign(data, secret, options); return jwt.sign(data, secret, options);
} }
exports.fillJwtForRequest = fillJwtForRequest; exports.fillJwtForRequest = fillJwtForRequest;
@ -874,13 +861,13 @@ exports.forwarded = forwarded;
exports.getIndexFromUserId = function(userId, userIdOriginal){ exports.getIndexFromUserId = function(userId, userIdOriginal){
return parseInt(userId.substring(userIdOriginal.length)); return parseInt(userId.substring(userIdOriginal.length));
}; };
exports.checkPathTraversal = function(docId, rootDirectory, filename) { exports.checkPathTraversal = function(ctx, docId, rootDirectory, filename) {
if (filename.indexOf('\0') !== -1) { if (filename.indexOf('\0') !== -1) {
logger.warn('checkPathTraversal Poison Null Bytes docId=%s filename=%s', docId, filename); ctx.logger.warn('checkPathTraversal Poison Null Bytes filename=%s', filename);
return false; return false;
} }
if (!filename.startsWith(rootDirectory)) { if (!filename.startsWith(rootDirectory)) {
logger.warn('checkPathTraversal Path Traversal docId=%s filename=%s', docId, filename); ctx.logger.warn('checkPathTraversal Path Traversal filename=%s', filename);
return false; return false;
} }
return true; return true;
@ -905,14 +892,14 @@ exports.getConnectionInfoStr = function(conn){
exports.isLiveViewer = function(conn){ exports.isLiveViewer = function(conn){
return conn.user?.view && "fast" === conn.coEditingMode; return conn.user?.view && "fast" === conn.coEditingMode;
}; };
exports.canIncludeOutboxAuthorization = function (url) { exports.canIncludeOutboxAuthorization = function (ctx, url) {
if (cfgTokenEnableRequestOutbox) { if (cfgTokenEnableRequestOutbox) {
if (!outboxUrlExclusionRegex) { if (!outboxUrlExclusionRegex) {
return true; return true;
} else if (!outboxUrlExclusionRegex.test(url)) { } else if (!outboxUrlExclusionRegex.test(url)) {
return true; return true;
} else { } else {
logger.debug('canIncludeOutboxAuthorization excluded by token.outbox.urlExclusionRegex url=%s', url); ctx.logger.debug('canIncludeOutboxAuthorization excluded by token.outbox.urlExclusionRegex url=%s', url);
} }
} }
return false; return false;
@ -981,8 +968,7 @@ exports.checkBaseUrl = function(baseUrl) {
}; };
exports.resolvePath = function(object, path, defaultValue) { exports.resolvePath = function(object, path, defaultValue) {
return path.split('.').reduce((o, p) => o ? o[p] : defaultValue, object); return path.split('.').reduce((o, p) => o ? o[p] : defaultValue, object);
} };
Date.isLeapYear = function (year) { Date.isLeapYear = function (year) {
return (((year % 4 === 0) && (year % 100 !== 0)) || (year % 400 === 0)); return (((year % 4 === 0) && (year % 100 !== 0)) || (year % 400 === 0));
}; };
@ -1023,3 +1009,15 @@ exports.getLicensePeriod = function(startDate, now) {
startDate.setUTCHours(0,0,0,0); startDate.setUTCHours(0,0,0,0);
return startDate.getTime(); return startDate.getTime();
}; };
exports.removeIllegalCharacters = function(filename) {
return filename?.replace(/[/\\?%*:|"<>]/g, '-') || filename;
}
exports.getFunctionArguments = function(func) {
return func.toString().
replace(/[\r\n\s]+/g, ' ').
match(/(?:function\s*\w*)?\s*(?:\((.*?)\)|([^\s]+))/).
slice(1, 3).
join('').
split(/\s*,\s*/);
};

File diff suppressed because it is too large Load Diff

View File

@ -40,7 +40,6 @@ var sqlDataBaseType = {
var config = require('config').get('services.CoAuthoring.sql'); var config = require('config').get('services.CoAuthoring.sql');
var baseConnector = (sqlDataBaseType.mySql === config.get('type') || sqlDataBaseType.mariaDB === config.get('type')) ? require('./mySqlBaseConnector') : require('./postgreSqlBaseConnector'); var baseConnector = (sqlDataBaseType.mySql === config.get('type') || sqlDataBaseType.mariaDB === config.get('type')) ? require('./mySqlBaseConnector') : require('./postgreSqlBaseConnector');
var logger = require('./../../Common/sources/logger');
let constants = require('./../../Common/sources/constants'); let constants = require('./../../Common/sources/constants');
const tableChanges = config.get('tableChanges'), const tableChanges = config.get('tableChanges'),
@ -52,9 +51,9 @@ let addSqlParam = baseConnector.addSqlParameter;
var maxPacketSize = config.get('max_allowed_packet'); // Размер по умолчанию для запроса в базу данных 1Mb - 1 (т.к. он не пишет 1048575, а пишет 1048574) var maxPacketSize = config.get('max_allowed_packet'); // Размер по умолчанию для запроса в базу данных 1Mb - 1 (т.к. он не пишет 1048575, а пишет 1048574)
exports.baseConnector = baseConnector; exports.baseConnector = baseConnector;
exports.insertChangesPromiseCompatibility = function (objChanges, docId, index, user) { exports.insertChangesPromiseCompatibility = function (ctx, objChanges, docId, index, user) {
return new Promise(function(resolve, reject) { return new Promise(function(resolve, reject) {
_insertChangesCallback(0, objChanges, docId, index, user, function(error, result) { _insertChangesCallback(ctx, 0, objChanges, docId, index, user, function(error, result) {
if (error) { if (error) {
reject(error); reject(error);
} else { } else {
@ -63,13 +62,13 @@ exports.insertChangesPromiseCompatibility = function (objChanges, docId, index,
}); });
}); });
}; };
exports.insertChangesPromiseFast = function (objChanges, docId, index, user) { exports.insertChangesPromiseFast = function (ctx, objChanges, docId, index, user) {
return new Promise(function(resolve, reject) { return new Promise(function(resolve, reject) {
baseConnector.insertChanges(tableChanges, 0, objChanges, docId, index, user, function(error, result, isSupported) { baseConnector.insertChanges(ctx, tableChanges, 0, objChanges, docId, index, user, function(error, result, isSupported) {
isSupportFastInsert = isSupported; isSupportFastInsert = isSupported;
if (error) { if (error) {
if (!isSupportFastInsert) { if (!isSupportFastInsert) {
resolve(exports.insertChangesPromiseCompatibility(objChanges, docId, index, user)); resolve(exports.insertChangesPromiseCompatibility(ctx, objChanges, docId, index, user));
} else { } else {
reject(error); reject(error);
} }
@ -79,11 +78,11 @@ exports.insertChangesPromiseFast = function (objChanges, docId, index, user) {
}); });
}); });
}; };
exports.insertChangesPromise = function (objChanges, docId, index, user) { exports.insertChangesPromise = function (ctx, objChanges, docId, index, user) {
if (isSupportFastInsert) { if (isSupportFastInsert) {
return exports.insertChangesPromiseFast(objChanges, docId, index, user); return exports.insertChangesPromiseFast(ctx, objChanges, docId, index, user);
} else { } else {
return exports.insertChangesPromiseCompatibility(objChanges, docId, index, user); return exports.insertChangesPromiseCompatibility(ctx, objChanges, docId, index, user);
} }
}; };
@ -93,7 +92,7 @@ function _getDateTime2(oDate) {
exports.getDateTime = _getDateTime2; exports.getDateTime = _getDateTime2;
function _insertChangesCallback (startIndex, objChanges, docId, index, user, callback) { function _insertChangesCallback (ctx, startIndex, objChanges, docId, index, user, callback) {
var sqlCommand = `INSERT INTO ${tableChanges} VALUES`; var sqlCommand = `INSERT INTO ${tableChanges} VALUES`;
var i = startIndex, l = objChanges.length, lengthUtf8Current = sqlCommand.length, lengthUtf8Row = 0, values = []; var i = startIndex, l = objChanges.length, lengthUtf8Current = sqlCommand.length, lengthUtf8Row = 0, values = [];
if (i === l) if (i === l)
@ -106,13 +105,14 @@ function _insertChangesCallback (startIndex, objChanges, docId, index, user, cal
if (lengthUtf8Row + lengthUtf8Current >= maxPacketSize && i > startIndex) { if (lengthUtf8Row + lengthUtf8Current >= maxPacketSize && i > startIndex) {
sqlCommand += ';'; sqlCommand += ';';
(function(tmpStart, tmpIndex) { (function(tmpStart, tmpIndex) {
baseConnector.sqlQuery(sqlCommand, function() { baseConnector.sqlQuery(ctx, sqlCommand, function() {
// lock не снимаем, а продолжаем добавлять // lock не снимаем, а продолжаем добавлять
_insertChangesCallback(tmpStart, objChanges, docId, tmpIndex, user, callback); _insertChangesCallback(ctx, tmpStart, objChanges, docId, tmpIndex, user, callback);
}, undefined, undefined, values); }, undefined, undefined, values);
})(i, index); })(i, index);
return; return;
} }
let p0 = addSqlParam(ctx.tenant, values);
let p1 = addSqlParam(docId, values); let p1 = addSqlParam(docId, values);
let p2 = addSqlParam(index, values); let p2 = addSqlParam(index, values);
let p3 = addSqlParam(user.id, values); let p3 = addSqlParam(user.id, values);
@ -123,27 +123,28 @@ function _insertChangesCallback (startIndex, objChanges, docId, index, user, cal
if (i > startIndex) { if (i > startIndex) {
sqlCommand += ','; sqlCommand += ',';
} }
sqlCommand += `(${p1},${p2},${p3},${p4},${p5},${p6},${p7})`; sqlCommand += `(${p0},${p1},${p2},${p3},${p4},${p5},${p6},${p7})`;
lengthUtf8Current += lengthUtf8Row; lengthUtf8Current += lengthUtf8Row;
} }
sqlCommand += ';'; sqlCommand += ';';
baseConnector.sqlQuery(sqlCommand, callback, undefined, undefined, values); baseConnector.sqlQuery(ctx, sqlCommand, callback, undefined, undefined, values);
} }
exports.deleteChangesCallback = function(docId, deleteIndex, callback) { exports.deleteChangesCallback = function(ctx, docId, deleteIndex, callback) {
let sqlCommand, values = []; let sqlCommand, values = [];
let sqlParam1 = addSqlParam(docId, values); let p1 = addSqlParam(ctx.tenant, values);
let p2 = addSqlParam(docId, values);
if (null !== deleteIndex) { if (null !== deleteIndex) {
let sqlParam2 = addSqlParam(deleteIndex, values); let sqlParam2 = addSqlParam(deleteIndex, values);
sqlCommand = `DELETE FROM ${tableChanges} WHERE id=${sqlParam1} AND change_id >= ${sqlParam2};`; sqlCommand = `DELETE FROM ${tableChanges} WHERE tenant=${p1} AND id=${p2} AND change_id >= ${sqlParam2};`;
} else { } else {
sqlCommand = `DELETE FROM ${tableChanges} WHERE id=${sqlParam1};`; sqlCommand = `DELETE FROM ${tableChanges} WHERE tenant=${p1} AND id=${p2};`;
} }
baseConnector.sqlQuery(sqlCommand, callback, undefined, undefined, values); baseConnector.sqlQuery(ctx, sqlCommand, callback, undefined, undefined, values);
}; };
exports.deleteChangesPromise = function (docId, deleteIndex) { exports.deleteChangesPromise = function (ctx, docId, deleteIndex) {
return new Promise(function(resolve, reject) { return new Promise(function(resolve, reject) {
exports.deleteChangesCallback(docId, deleteIndex, function(error, result) { exports.deleteChangesCallback(ctx, docId, deleteIndex, function(error, result) {
if (error) { if (error) {
reject(error); reject(error);
} else { } else {
@ -152,21 +153,22 @@ exports.deleteChangesPromise = function (docId, deleteIndex) {
}); });
}); });
}; };
exports.deleteChanges = function (docId, deleteIndex) { exports.deleteChanges = function (ctx, docId, deleteIndex) {
lockCriticalSection(docId, function () {_deleteChanges(docId, deleteIndex);}); lockCriticalSection(docId, function () {_deleteChanges(ctx, docId, deleteIndex);});
}; };
function _deleteChanges (docId, deleteIndex) { function _deleteChanges (ctx, docId, deleteIndex) {
exports.deleteChangesCallback(docId, deleteIndex, function () {unLockCriticalSection(docId);}); exports.deleteChangesCallback(ctx, docId, deleteIndex, function () {unLockCriticalSection(docId);});
} }
exports.getChangesIndex = function(docId, callback) { exports.getChangesIndex = function(ctx, docId, callback) {
let values = []; let values = [];
let sqlParam = addSqlParam(docId, values); let p1 = addSqlParam(ctx.tenant, values);
var sqlCommand = `SELECT MAX(change_id) as change_id FROM ${tableChanges} WHERE id=${sqlParam};`; let p2 = addSqlParam(docId, values);
baseConnector.sqlQuery(sqlCommand, callback, undefined, undefined, values); var sqlCommand = `SELECT MAX(change_id) as change_id FROM ${tableChanges} WHERE tenant=${p1} AND id=${p2};`;
baseConnector.sqlQuery(ctx, sqlCommand, callback, undefined, undefined, values);
}; };
exports.getChangesIndexPromise = function(docId) { exports.getChangesIndexPromise = function(ctx, docId) {
return new Promise(function(resolve, reject) { return new Promise(function(resolve, reject) {
exports.getChangesIndex(docId, function(error, result) { exports.getChangesIndex(ctx, docId, function(error, result) {
if (error) { if (error) {
reject(error); reject(error);
} else { } else {
@ -175,11 +177,13 @@ exports.getChangesIndexPromise = function(docId) {
}); });
}); });
}; };
exports.getChangesPromise = function (docId, optStartIndex, optEndIndex, opt_time) { exports.getChangesPromise = function (ctx, docId, optStartIndex, optEndIndex, opt_time) {
return new Promise(function(resolve, reject) { return new Promise(function(resolve, reject) {
let values = []; let values = [];
let sqlParam = addSqlParam(docId, values); let sqlParam = addSqlParam(ctx.tenant, values);
let sqlWhere = `id=${sqlParam}`; let sqlWhere = `tenant=${sqlParam}`;
sqlParam = addSqlParam(docId, values);
sqlWhere += ` AND id=${sqlParam}`;
if (null != optStartIndex) { if (null != optStartIndex) {
sqlParam = addSqlParam(optStartIndex, values); sqlParam = addSqlParam(optStartIndex, values);
sqlWhere += ` AND change_id>=${sqlParam}`; sqlWhere += ` AND change_id>=${sqlParam}`;
@ -198,7 +202,7 @@ exports.getChangesPromise = function (docId, optStartIndex, optEndIndex, opt_tim
sqlWhere += ' ORDER BY change_id ASC'; sqlWhere += ' ORDER BY change_id ASC';
var sqlCommand = `SELECT * FROM ${tableChanges} WHERE ${sqlWhere};`; var sqlCommand = `SELECT * FROM ${tableChanges} WHERE ${sqlWhere};`;
baseConnector.sqlQuery(sqlCommand, function(error, result) { baseConnector.sqlQuery(ctx, sqlCommand, function(error, result) {
if (error) { if (error) {
reject(error); reject(error);
} else { } else {
@ -207,23 +211,6 @@ exports.getChangesPromise = function (docId, optStartIndex, optEndIndex, opt_tim
}, undefined, undefined, values); }, undefined, undefined, values);
}); });
}; };
exports.checkStatusFile = function (docId, callbackFunction) {
let values = [];
let sqlParam = addSqlParam(docId, values);
var sqlCommand = `SELECT status, status_info FROM ${tableResult} WHERE id=${sqlParam};`;
baseConnector.sqlQuery(sqlCommand, callbackFunction, undefined, undefined, values);
};
exports.checkStatusFilePromise = function (docId) {
return new Promise(function(resolve, reject) {
exports.checkStatusFile(docId, function(error, result) {
if (error) {
reject(error);
} else {
resolve(result);
}
});
});
};
exports.isLockCriticalSection = function (id) { exports.isLockCriticalSection = function (id) {
return !!(g_oCriticalSection[id]); return !!(g_oCriticalSection[id]);
@ -249,11 +236,11 @@ function unLockCriticalSection (id) {
else else
delete g_oCriticalSection[id]; delete g_oCriticalSection[id];
} }
exports.healthCheck = function () { exports.healthCheck = function (ctx) {
return new Promise(function(resolve, reject) { return new Promise(function(resolve, reject) {
//SELECT 1; usefull for H2, MySQL, Microsoft SQL Server, PostgreSQL, SQLite //SELECT 1; usefull for H2, MySQL, Microsoft SQL Server, PostgreSQL, SQLite
//http://stackoverflow.com/questions/3668506/efficient-sql-test-query-or-validation-query-that-will-work-across-all-or-most //http://stackoverflow.com/questions/3668506/efficient-sql-test-query-or-validation-query-that-will-work-across-all-or-most
baseConnector.sqlQuery('SELECT 1;', function(error, result) { baseConnector.sqlQuery(ctx, 'SELECT 1;', function(error, result) {
if (error) { if (error) {
reject(error); reject(error);
} else { } else {
@ -263,10 +250,22 @@ exports.healthCheck = function () {
}); });
}; };
exports.getEmptyCallbacks = function() { exports.getEmptyCallbacks = function(ctx) {
return new Promise(function(resolve, reject) { return new Promise(function(resolve, reject) {
const sqlCommand = "SELECT DISTINCT t1.id FROM doc_changes t1 LEFT JOIN task_result t2 ON t2.id = t1.id WHERE t2.callback = '';"; const sqlCommand = "SELECT DISTINCT t1.tenant, t1.id FROM doc_changes t1 LEFT JOIN task_result t2 ON t2.tenant = t1.tenant AND t2.id = t1.id WHERE t2.callback = '';";
baseConnector.sqlQuery(sqlCommand, function(error, result) { baseConnector.sqlQuery(ctx, sqlCommand, function(error, result) {
if (error) {
reject(error);
} else {
resolve(result);
}
});
});
};
exports.getTableColumns = function(ctx, tableName) {
return new Promise(function(resolve, reject) {
const sqlCommand = `SELECT column_name FROM information_schema.COLUMNS WHERE TABLE_NAME = '${tableName}';`;
baseConnector.sqlQuery(ctx, sqlCommand, function(error, result) {
if (error) { if (error) {
reject(error); reject(error);
} else { } else {
@ -291,8 +290,8 @@ UserCallback.prototype.delimiter = constants.CHAR_DELIMITER;
UserCallback.prototype.toSQLInsert = function(){ UserCallback.prototype.toSQLInsert = function(){
return this.delimiter + JSON.stringify(this); return this.delimiter + JSON.stringify(this);
}; };
UserCallback.prototype.getCallbackByUserIndex = function(docId, callbacksStr, opt_userIndex) { UserCallback.prototype.getCallbackByUserIndex = function(ctx, callbacksStr, opt_userIndex) {
logger.debug("getCallbackByUserIndex: docId = %s userIndex = %s callbacks = %s", docId, opt_userIndex, callbacksStr); ctx.logger.debug("getCallbackByUserIndex: userIndex = %s callbacks = %s", opt_userIndex, callbacksStr);
if (!callbacksStr || !callbacksStr.startsWith(UserCallback.prototype.delimiter)) { if (!callbacksStr || !callbacksStr.startsWith(UserCallback.prototype.delimiter)) {
let index = callbacksStr.indexOf(UserCallback.prototype.delimiter); let index = callbacksStr.indexOf(UserCallback.prototype.delimiter);
if (-1 === index) { if (-1 === index) {
@ -314,8 +313,8 @@ UserCallback.prototype.getCallbackByUserIndex = function(docId, callbacksStr, op
} }
return callbackUrl; return callbackUrl;
}; };
UserCallback.prototype.getCallbacks = function(docId, callbacksStr) { UserCallback.prototype.getCallbacks = function(ctx, callbacksStr) {
logger.debug("getCallbacks: docId = %s callbacks = %s", docId, callbacksStr); ctx.logger.debug("getCallbacks: callbacks = %s", callbacksStr);
if (!callbacksStr || !callbacksStr.startsWith(UserCallback.prototype.delimiter)) { if (!callbacksStr || !callbacksStr.startsWith(UserCallback.prototype.delimiter)) {
let index = callbacksStr.indexOf(UserCallback.prototype.delimiter); let index = callbacksStr.indexOf(UserCallback.prototype.delimiter);
if (-1 === index) { if (-1 === index) {
@ -359,10 +358,10 @@ DocumentPassword.prototype.toSQLInsert = function(){
DocumentPassword.prototype.isInitial = function(){ DocumentPassword.prototype.isInitial = function(){
return !this.change; return !this.change;
}; };
DocumentPassword.prototype.getDocPassword = function(docId, docPasswordStr) { DocumentPassword.prototype.getDocPassword = function(ctx, docPasswordStr) {
let res = {initial: undefined, current: undefined, change: undefined}; let res = {initial: undefined, current: undefined, change: undefined};
if (docPasswordStr) { if (docPasswordStr) {
logger.debug("getDocPassword: docId = %s passwords = %s", docId, docPasswordStr); ctx.logger.debug("getDocPassword: passwords = %s", docPasswordStr);
let passwords = docPasswordStr.split(UserCallback.prototype.delimiter); let passwords = docPasswordStr.split(UserCallback.prototype.delimiter);
for (let i = 1; i < passwords.length; ++i) { for (let i = 1; i < passwords.length; ++i) {
@ -378,12 +377,12 @@ DocumentPassword.prototype.getDocPassword = function(docId, docPasswordStr) {
} }
return res; return res;
}; };
DocumentPassword.prototype.getCurPassword = function(docId, docPasswordStr) { DocumentPassword.prototype.getCurPassword = function(ctx, docPasswordStr) {
let docPassword = this.getDocPassword(docId, docPasswordStr); let docPassword = this.getDocPassword(ctx, docPasswordStr);
return docPassword.current; return docPassword.current;
}; };
DocumentPassword.prototype.hasPasswordChanges = function(docId, docPasswordStr) { DocumentPassword.prototype.hasPasswordChanges = function(ctx, docPasswordStr) {
let docPassword = this.getDocPassword(docId, docPasswordStr); let docPassword = this.getDocPassword(ctx, docPasswordStr);
return docPassword.initial !== docPassword.current; return docPassword.initial !== docPassword.current;
}; };
exports.DocumentPassword = DocumentPassword; exports.DocumentPassword = DocumentPassword;

File diff suppressed because it is too large Load Diff

View File

@ -37,7 +37,6 @@ var config = require('config');
var co = require('co'); var co = require('co');
const locale = require('windows-locale'); const locale = require('windows-locale');
var taskResult = require('./taskresult'); var taskResult = require('./taskresult');
var logger = require('./../../Common/sources/logger');
var utils = require('./../../Common/sources/utils'); var utils = require('./../../Common/sources/utils');
var constants = require('./../../Common/sources/constants'); var constants = require('./../../Common/sources/constants');
var commonDefines = require('./../../Common/sources/commondefines'); var commonDefines = require('./../../Common/sources/commondefines');
@ -47,18 +46,19 @@ var storage = require('./../../Common/sources/storage-base');
var formatChecker = require('./../../Common/sources/formatchecker'); var formatChecker = require('./../../Common/sources/formatchecker');
var statsDClient = require('./../../Common/sources/statsdclient'); var statsDClient = require('./../../Common/sources/statsdclient');
var storageBase = require('./../../Common/sources/storage-base'); var storageBase = require('./../../Common/sources/storage-base');
var operationContext = require('./../../Common/sources/operationContext');
const sqlBase = require('./baseConnector'); const sqlBase = require('./baseConnector');
var CONVERT_ASYNC_DELAY = 1000; var CONVERT_ASYNC_DELAY = 1000;
var clientStatsD = statsDClient.getClient(); var clientStatsD = statsDClient.getClient();
function* getConvertStatus(cmd, selectRes, opt_checkPassword) { function* getConvertStatus(ctx, cmd, selectRes, opt_checkPassword) {
var status = new commonDefines.ConvertStatus(constants.NO_ERROR); var status = new commonDefines.ConvertStatus(constants.NO_ERROR);
if (selectRes.length > 0) { if (selectRes.length > 0) {
var docId = cmd.getDocId(); var docId = cmd.getDocId();
var row = selectRes[0]; var row = selectRes[0];
let password = opt_checkPassword && sqlBase.DocumentPassword.prototype.getCurPassword(docId, row.password); let password = opt_checkPassword && sqlBase.DocumentPassword.prototype.getCurPassword(ctx, row.password);
switch (row.status) { switch (row.status) {
case taskResult.FileStatus.Ok: case taskResult.FileStatus.Ok:
if (password) { if (password) {
@ -70,10 +70,10 @@ function* getConvertStatus(cmd, selectRes, opt_checkPassword) {
isCorrectPassword = decryptedPassword === userPassword; isCorrectPassword = decryptedPassword === userPassword;
} }
if (isCorrectPassword) { if (isCorrectPassword) {
logger.debug("getConvertStatus password match: docId = %s", docId); ctx.logger.debug("getConvertStatus password match");
status.end = true; status.end = true;
} else { } else {
logger.debug("getConvertStatus password mismatch: docId = %s", docId); ctx.logger.debug("getConvertStatus password mismatch");
status.err = constants.CONVERT_PASSWORD; status.err = constants.CONVERT_PASSWORD;
} }
} else { } else {
@ -85,7 +85,7 @@ function* getConvertStatus(cmd, selectRes, opt_checkPassword) {
case taskResult.FileStatus.NeedPassword: case taskResult.FileStatus.NeedPassword:
status.err = row.status_info; status.err = row.status_info;
if (taskResult.FileStatus.ErrToReload == row.status || taskResult.FileStatus.NeedPassword == row.status) { if (taskResult.FileStatus.ErrToReload == row.status || taskResult.FileStatus.NeedPassword == row.status) {
yield canvasService.cleanupCache(docId); yield canvasService.cleanupCache(ctx);
} }
break; break;
case taskResult.FileStatus.NeedParams: case taskResult.FileStatus.NeedParams:
@ -103,9 +103,9 @@ function* getConvertStatus(cmd, selectRes, opt_checkPassword) {
} }
return status; return status;
} }
function* getConvertPath(docId, fileTo, formatTo) { function* getConvertPath(ctx, docId, fileTo, formatTo) {
if (constants.AVS_OFFICESTUDIO_FILE_OTHER_OOXML === formatTo || constants.AVS_OFFICESTUDIO_FILE_OTHER_ODF === formatTo) { if (constants.AVS_OFFICESTUDIO_FILE_OTHER_OOXML === formatTo || constants.AVS_OFFICESTUDIO_FILE_OTHER_ODF === formatTo) {
let list = yield storage.listObjects(docId); let list = yield storage.listObjects(ctx, docId);
let baseName = path.basename(fileTo, path.extname(fileTo)); let baseName = path.basename(fileTo, path.extname(fileTo));
for (let i = 0; i < list.length; ++i) { for (let i = 0; i < list.length; ++i) {
if (path.basename(list[i], path.extname(list[i])) === baseName) { if (path.basename(list[i], path.extname(list[i])) === baseName) {
@ -115,28 +115,29 @@ function* getConvertPath(docId, fileTo, formatTo) {
} }
return docId + '/' + fileTo; return docId + '/' + fileTo;
} }
function* getConvertUrl(baseUrl, fileToPath, title) { function* getConvertUrl(ctx, baseUrl, fileToPath, title) {
if (title) { if (title) {
title = path.basename(title, path.extname(title)) + path.extname(fileToPath); title = path.basename(title, path.extname(title)) + path.extname(fileToPath);
} }
return yield storage.getSignedUrl(baseUrl, fileToPath, commonDefines.c_oAscUrlTypes.Temporary, title); return yield storage.getSignedUrl(ctx, baseUrl, fileToPath, commonDefines.c_oAscUrlTypes.Temporary, title);
} }
function* convertByCmd(cmd, async, opt_fileTo, opt_taskExist, opt_priority, opt_expiration, opt_queue, opt_checkPassword) { function* convertByCmd(ctx, cmd, async, opt_fileTo, opt_taskExist, opt_priority, opt_expiration, opt_queue, opt_checkPassword) {
var docId = cmd.getDocId(); var docId = cmd.getDocId();
var startDate = null; var startDate = null;
if (clientStatsD) { if (clientStatsD) {
startDate = new Date(); startDate = new Date();
} }
logger.debug('Start convert request docId = %s', docId); ctx.logger.debug('Start convert request');
let bCreate = false; let bCreate = false;
if (!opt_taskExist) { if (!opt_taskExist) {
let task = new taskResult.TaskResultData(); let task = new taskResult.TaskResultData();
task.tenant = ctx.tenant;
task.key = docId; task.key = docId;
task.status = taskResult.FileStatus.WaitQueue; task.status = taskResult.FileStatus.WaitQueue;
task.statusInfo = constants.NO_ERROR; task.statusInfo = constants.NO_ERROR;
let upsertRes = yield taskResult.upsert(task); let upsertRes = yield taskResult.upsert(ctx, task);
//if CLIENT_FOUND_ROWS don't specify 1 row is inserted , 2 row is updated, and 0 row is set to its current values //if CLIENT_FOUND_ROWS don't specify 1 row is inserted , 2 row is updated, and 0 row is set to its current values
//http://dev.mysql.com/doc/refman/5.7/en/insert-on-duplicate.html //http://dev.mysql.com/doc/refman/5.7/en/insert-on-duplicate.html
bCreate = upsertRes.affectedRows == 1; bCreate = upsertRes.affectedRows == 1;
@ -144,10 +145,11 @@ function* convertByCmd(cmd, async, opt_fileTo, opt_taskExist, opt_priority, opt_
var selectRes; var selectRes;
var status; var status;
if (!bCreate) { if (!bCreate) {
selectRes = yield taskResult.select(docId); selectRes = yield taskResult.select(ctx, docId);
status = yield* getConvertStatus(cmd, selectRes, opt_checkPassword); status = yield* getConvertStatus(ctx, cmd, selectRes, opt_checkPassword);
} else { } else {
var queueData = new commonDefines.TaskQueueData(); var queueData = new commonDefines.TaskQueueData();
queueData.setCtx(ctx);
queueData.setCmd(cmd); queueData.setCmd(cmd);
if (opt_fileTo) { if (opt_fileTo) {
queueData.setToFile(opt_fileTo); queueData.setToFile(opt_fileTo);
@ -165,22 +167,22 @@ function* convertByCmd(cmd, async, opt_fileTo, opt_taskExist, opt_priority, opt_
break; break;
} }
yield utils.sleep(CONVERT_ASYNC_DELAY); yield utils.sleep(CONVERT_ASYNC_DELAY);
selectRes = yield taskResult.select(docId); selectRes = yield taskResult.select(ctx, docId);
status = yield* getConvertStatus(cmd, selectRes, opt_checkPassword); status = yield* getConvertStatus(ctx, cmd, selectRes, opt_checkPassword);
waitTime += CONVERT_ASYNC_DELAY; waitTime += CONVERT_ASYNC_DELAY;
if (waitTime > utils.CONVERTION_TIMEOUT) { if (waitTime > utils.CONVERTION_TIMEOUT) {
status.err = constants.CONVERT_TIMEOUT; status.err = constants.CONVERT_TIMEOUT;
} }
} }
} }
logger.debug('End convert request end %s status %s docId = %s', status.end, status.err, docId); ctx.logger.debug('End convert request end %s status %s', status.end, status.err);
if (clientStatsD) { if (clientStatsD) {
clientStatsD.timing('coauth.convertservice', new Date() - startDate); clientStatsD.timing('coauth.convertservice', new Date() - startDate);
} }
return status; return status;
} }
function* convertFromChanges(docId, baseUrl, forceSave, externalChangeInfo, opt_userdata, opt_userConnectionId, let convertFromChanges = co.wrap(function*(ctx, docId, baseUrl, forceSave, externalChangeInfo, opt_userdata, opt_userConnectionId,
opt_responseKey, opt_priority, opt_expiration, opt_queue, opt_redisKey) { opt_responseKey, opt_priority, opt_expiration, opt_queue, opt_redisKey) {
var cmd = new commonDefines.InputCommand(); var cmd = new commonDefines.InputCommand();
cmd.setCommand('sfcm'); cmd.setCommand('sfcm');
@ -204,50 +206,56 @@ function* convertFromChanges(docId, baseUrl, forceSave, externalChangeInfo, opt_
cmd.setRedisKey(opt_redisKey); cmd.setRedisKey(opt_redisKey);
} }
yield* canvasService.commandSfctByCmd(cmd, opt_priority, opt_expiration, opt_queue); yield canvasService.commandSfctByCmd(ctx, cmd, opt_priority, opt_expiration, opt_queue);
var fileTo = constants.OUTPUT_NAME; var fileTo = constants.OUTPUT_NAME;
let outputExt = formatChecker.getStringFromFormat(cmd.getOutputFormat()); let outputExt = formatChecker.getStringFromFormat(cmd.getOutputFormat());
if (outputExt) { if (outputExt) {
fileTo += '.' + outputExt; fileTo += '.' + outputExt;
} }
let status = yield* convertByCmd(cmd, true, fileTo, undefined, opt_priority, opt_expiration, opt_queue); let status = yield* convertByCmd(ctx, cmd, true, fileTo, undefined, opt_priority, opt_expiration, opt_queue);
if (status.end) { if (status.end) {
let fileToPath = yield* getConvertPath(docId, fileTo, cmd.getOutputFormat()); let fileToPath = yield* getConvertPath(ctx, docId, fileTo, cmd.getOutputFormat());
status.setExtName(path.extname(fileToPath)); status.setExtName(path.extname(fileToPath));
status.setUrl(yield* getConvertUrl(baseUrl, fileToPath, cmd.getTitle())); status.setUrl(yield* getConvertUrl(ctx, baseUrl, fileToPath, cmd.getTitle()));
} }
return status; return status;
} });
function parseIntParam(val){ function parseIntParam(val){
return (typeof val === 'string') ? parseInt(val) : val; return (typeof val === 'string') ? parseInt(val) : val;
} }
function convertRequest(req, res, isJson) { function convertRequest(req, res, isJson) {
return co(function* () { return co(function* () {
var docId = 'convertRequest'; let ctx = new operationContext.Context();
try { try {
ctx.initFromRequest(req);
ctx.logger.info('convertRequest start');
let params; let params;
let authRes = docsCoServer.getRequestParams(docId, req); let authRes = yield docsCoServer.getRequestParams(ctx, req);
if(authRes.code === constants.NO_ERROR){ if(authRes.code === constants.NO_ERROR){
params = authRes.params; params = authRes.params;
} else { } else {
ctx.logger.warn('convertRequest auth failed %j', authRes);
utils.fillResponse(req, res, new commonDefines.ConvertStatus(authRes.code), isJson); utils.fillResponse(req, res, new commonDefines.ConvertStatus(authRes.code), isJson);
return; return;
} }
let outputtype = params.outputtype || '';
let docId = 'conv_' + params.key + '_' + outputtype;
ctx.setDocId(docId);
if (params.key && !constants.DOC_ID_REGEX.test(params.key)) { if (params.key && !constants.DOC_ID_REGEX.test(params.key)) {
logger.warn('convertRequest unexpected key = %s: docId = %s', params.key, docId); ctx.logger.warn('convertRequest unexpected key = %s', params.key);
utils.fillResponse(req, res, new commonDefines.ConvertStatus(constants.CONVERT_PARAMS), isJson); utils.fillResponse(req, res, new commonDefines.ConvertStatus(constants.CONVERT_PARAMS), isJson);
return; return;
} }
if (params.filetype && !constants.EXTENTION_REGEX.test(params.filetype)) { if (params.filetype && !constants.EXTENTION_REGEX.test(params.filetype)) {
logger.warn('convertRequest unexpected filetype = %s: docId = %s', params.filetype, docId); ctx.logger.warn('convertRequest unexpected filetype = %s', params.filetype);
utils.fillResponse(req, res, new commonDefines.ConvertStatus(constants.CONVERT_PARAMS), isJson); utils.fillResponse(req, res, new commonDefines.ConvertStatus(constants.CONVERT_PARAMS), isJson);
return; return;
} }
let outputtype = params.outputtype || '';
let outputFormat = formatChecker.getFormatFromString(outputtype); let outputFormat = formatChecker.getFormatFromString(outputtype);
if (constants.AVS_OFFICESTUDIO_FILE_UNKNOWN === outputFormat) { if (constants.AVS_OFFICESTUDIO_FILE_UNKNOWN === outputFormat) {
logger.warn('convertRequest unexpected outputtype = %s: docId = %s', outputtype, docId); ctx.logger.warn('convertRequest unexpected outputtype = %s', outputtype);
utils.fillResponse(req, res, new commonDefines.ConvertStatus(constants.CONVERT_PARAMS), isJson); utils.fillResponse(req, res, new commonDefines.ConvertStatus(constants.CONVERT_PARAMS), isJson);
return; return;
} }
@ -256,7 +264,6 @@ function convertRequest(req, res, isJson) {
cmd.setUrl(params.url); cmd.setUrl(params.url);
cmd.setEmbeddedFonts(false);//params.embeddedfonts']; cmd.setEmbeddedFonts(false);//params.embeddedfonts'];
cmd.setFormat(params.filetype); cmd.setFormat(params.filetype);
docId = 'conv_' + params.key + '_' + outputtype;
cmd.setDocId(docId); cmd.setDocId(docId);
cmd.setOutputFormat(outputFormat); cmd.setOutputFormat(outputFormat);
@ -275,7 +282,7 @@ function convertRequest(req, res, isJson) {
} }
if (params.password) { if (params.password) {
if (params.password.length > constants.PASSWORD_MAX_LENGTH) { if (params.password.length > constants.PASSWORD_MAX_LENGTH) {
logger.warn('convertRequest password too long actual = %s; max = %s;docId = %s', params.password.length, constants.PASSWORD_MAX_LENGTH, docId); ctx.logger.warn('convertRequest password too long actual = %s; max = %s', params.password.length, constants.PASSWORD_MAX_LENGTH);
utils.fillResponse(req, res, new commonDefines.ConvertStatus(constants.CONVERT_PARAMS), isJson); utils.fillResponse(req, res, new commonDefines.ConvertStatus(constants.CONVERT_PARAMS), isJson);
return; return;
} }
@ -340,23 +347,24 @@ function convertRequest(req, res, isJson) {
if (constants.AVS_OFFICESTUDIO_FILE_UNKNOWN !== cmd.getOutputFormat()) { if (constants.AVS_OFFICESTUDIO_FILE_UNKNOWN !== cmd.getOutputFormat()) {
let fileTo = constants.OUTPUT_NAME + '.' + outputExt; let fileTo = constants.OUTPUT_NAME + '.' + outputExt;
var status = yield* convertByCmd(cmd, async, fileTo, undefined, undefined, undefined, undefined, true); var status = yield* convertByCmd(ctx, cmd, async, fileTo, undefined, undefined, undefined, undefined, true);
if (status.end) { if (status.end) {
let fileToPath = yield* getConvertPath(docId, fileTo, cmd.getOutputFormat()); let fileToPath = yield* getConvertPath(ctx, docId, fileTo, cmd.getOutputFormat());
status.setExtName(path.extname(fileToPath)); status.setExtName(path.extname(fileToPath));
status.setUrl(yield* getConvertUrl(utils.getBaseUrlByRequest(req), fileToPath, cmd.getTitle())); status.setUrl(yield* getConvertUrl(ctx, utils.getBaseUrlByRequest(req), fileToPath, cmd.getTitle()));
logger.debug('convertRequest: url = %s docId = %s', status.url, docId); ctx.logger.debug('convertRequest: url = %s', status.url);
} }
utils.fillResponse(req, res, status, isJson); utils.fillResponse(req, res, status, isJson);
} else { } else {
var addresses = utils.forwarded(req); var addresses = utils.forwarded(req);
logger.warn('Error convert unknown outputtype: query = %j from = %s docId = %s', params, addresses, docId); ctx.logger.warn('Error convert unknown outputtype: query = %j from = %s', params, addresses);
utils.fillResponse(req, res, new commonDefines.ConvertStatus(constants.UNKNOWN), isJson); utils.fillResponse(req, res, new commonDefines.ConvertStatus(constants.UNKNOWN), isJson);
} }
} } catch (e) {
catch (e) { ctx.logger.error('convertRequest error: %s', e.stack);
logger.error('Error convert: docId = %s\r\n%s', docId, e.stack);
utils.fillResponse(req, res, new commonDefines.ConvertStatus(constants.UNKNOWN), isJson); utils.fillResponse(req, res, new commonDefines.ConvertStatus(constants.UNKNOWN), isJson);
} finally {
ctx.logger.info('convertRequest end');
} }
}); });
} }
@ -369,59 +377,65 @@ function convertRequestXml(req, res) {
function builderRequest(req, res) { function builderRequest(req, res) {
return co(function* () { return co(function* () {
let docId = 'builderRequest'; let ctx = new operationContext.Context();
try { try {
ctx.initFromRequest(req);
ctx.logger.info('builderRequest start');
let authRes; let authRes;
if (!utils.isEmptyObject(req.query)) { if (!utils.isEmptyObject(req.query)) {
//todo this is a stub for compatibility. remove in future version //todo this is a stub for compatibility. remove in future version
authRes = docsCoServer.getRequestParams(docId, req, true); authRes = yield docsCoServer.getRequestParams(ctx, req, true);
} else { } else {
authRes = docsCoServer.getRequestParams(docId, req); authRes = yield docsCoServer.getRequestParams(ctx, req);
} }
let params = authRes.params; let params = authRes.params;
let docId = params.key;
ctx.setDocId(docId);
let error = authRes.code; let error = authRes.code;
let urls; let urls;
let end = false; let end = false;
if (error === constants.NO_ERROR && if (error === constants.NO_ERROR &&
(params.key || params.url || (req.body && Buffer.isBuffer(req.body) && req.body.length > 0))) { (params.key || params.url || (req.body && Buffer.isBuffer(req.body) && req.body.length > 0))) {
docId = params.key;
let cmd = new commonDefines.InputCommand(); let cmd = new commonDefines.InputCommand();
cmd.setCommand('builder'); cmd.setCommand('builder');
cmd.setIsBuilder(true); cmd.setIsBuilder(true);
cmd.setWithAuthorization(true); cmd.setWithAuthorization(true);
cmd.setDocId(docId); cmd.setDocId(docId);
if (!docId) { if (!docId) {
let task = yield* taskResult.addRandomKeyTask(undefined, 'bld_', 8); let task = yield* taskResult.addRandomKeyTask(ctx, undefined, 'bld_', 8);
docId = task.key; docId = task.key;
cmd.setDocId(docId); cmd.setDocId(docId);
if (params.url) { if (params.url) {
cmd.setUrl(params.url); cmd.setUrl(params.url);
cmd.setFormat('docbuilder'); cmd.setFormat('docbuilder');
} else { } else {
yield storageBase.putObject(docId + '/script.docbuilder', req.body, req.body.length); yield storageBase.putObject(ctx, docId + '/script.docbuilder', req.body, req.body.length);
} }
let queueData = new commonDefines.TaskQueueData(); let queueData = new commonDefines.TaskQueueData();
queueData.setCtx(ctx);
queueData.setCmd(cmd); queueData.setCmd(cmd);
yield* docsCoServer.addTask(queueData, constants.QUEUE_PRIORITY_LOW); yield* docsCoServer.addTask(queueData, constants.QUEUE_PRIORITY_LOW);
} }
let async = (typeof params.async === 'string') ? 'true' === params.async : params.async; let async = (typeof params.async === 'string') ? 'true' === params.async : params.async;
let status = yield* convertByCmd(cmd, async, utils.getBaseUrlByRequest(req), undefined, true); let status = yield* convertByCmd(ctx, cmd, async, utils.getBaseUrlByRequest(req), undefined, true);
end = status.end; end = status.end;
error = status.err; error = status.err;
if (end) { if (end) {
urls = yield storageBase.getSignedUrls(utils.getBaseUrlByRequest(req), docId + '/output', urls = yield storageBase.getSignedUrls(ctx, utils.getBaseUrlByRequest(req), docId + '/output',
commonDefines.c_oAscUrlTypes.Temporary); commonDefines.c_oAscUrlTypes.Temporary);
} }
} else if (error === constants.NO_ERROR) { } else if (error === constants.NO_ERROR) {
error = constants.UNKNOWN; error = constants.UNKNOWN;
} }
logger.debug('End builderRequest request: docId = %s urls = %j end = %s error = %s', docId, urls, end, error); ctx.logger.debug('End builderRequest request: urls = %j end = %s error = %s', urls, end, error);
utils.fillResponseBuilder(res, docId, urls, end, error); utils.fillResponseBuilder(res, docId, urls, end, error);
} }
catch (e) { catch (e) {
logger.error('Error builderRequest: docId = %s\r\n%s', docId, e.stack); ctx.logger.error('Error builderRequest: %s', e.stack);
utils.fillResponseBuilder(res, undefined, undefined, undefined, constants.UNKNOWN); utils.fillResponseBuilder(res, undefined, undefined, undefined, constants.UNKNOWN);
} finally {
ctx.logger.info('builderRequest end');
} }
}); });
} }

View File

@ -35,6 +35,7 @@ const config = require('config');
const ms = require('ms'); const ms = require('ms');
const utils = require('./../../Common/sources/utils'); const utils = require('./../../Common/sources/utils');
const commonDefines = require('./../../Common/sources/commondefines'); const commonDefines = require('./../../Common/sources/commondefines');
const tenantManager = require('./../../Common/sources/tenantManager');
const cfgExpMonthUniqueUsers = ms(config.get('services.CoAuthoring.expire.monthUniqueUsers')); const cfgExpMonthUniqueUsers = ms(config.get('services.CoAuthoring.expire.monthUniqueUsers'));
@ -46,18 +47,22 @@ function EditorData() {
this.uniqueViewUser = {}; this.uniqueViewUser = {};
this.uniqueViewUsersOfMonth = {}; this.uniqueViewUsersOfMonth = {};
this.shutdown = {}; this.shutdown = {};
this.stat = []; this.stat = {};
} }
EditorData.prototype._getDocumentData = function(docId) { EditorData.prototype._getDocumentData = function(ctx, docId) {
let options = this.data[docId]; let tenantData = this.data[ctx.tenant];
if (!tenantData) {
this.data[ctx.tenant] = tenantData = {};
}
let options = tenantData[docId];
if (!options) { if (!options) {
this.data[docId] = options = {}; tenantData[docId] = options = {};
} }
return options; return options;
}; };
EditorData.prototype._checkAndLock = function(name, docId, fencingToken, ttl) { EditorData.prototype._checkAndLock = function(ctx, name, docId, fencingToken, ttl) {
let data = this._getDocumentData(docId); let data = this._getDocumentData(ctx, docId);
const now = Date.now(); const now = Date.now();
let res = true; let res = true;
if (data[name] && now < data[name].expireAt && fencingToken !== data[name].fencingToken) { if (data[name] && now < data[name].expireAt && fencingToken !== data[name].fencingToken) {
@ -68,8 +73,8 @@ EditorData.prototype._checkAndLock = function(name, docId, fencingToken, ttl) {
} }
return Promise.resolve(res); return Promise.resolve(res);
}; };
EditorData.prototype._checkAndUnlock = function(name, docId, fencingToken) { EditorData.prototype._checkAndUnlock = function(ctx, name, docId, fencingToken) {
let data = this._getDocumentData(docId); let data = this._getDocumentData(ctx, docId);
const now = Date.now(); const now = Date.now();
let res; let res;
if (data[name] && now < data[name].expireAt) { if (data[name] && now < data[name].expireAt) {
@ -86,100 +91,101 @@ EditorData.prototype._checkAndUnlock = function(name, docId, fencingToken) {
return Promise.resolve(res); return Promise.resolve(res);
}; };
EditorData.prototype.addPresence = function(docId, userId, userInfo) { EditorData.prototype.addPresence = function(ctx, docId, userId, userInfo) {
return Promise.resolve(); return Promise.resolve();
}; };
EditorData.prototype.removePresence = function(docId, userId) { EditorData.prototype.removePresence = function(ctx, docId, userId) {
return Promise.resolve(); return Promise.resolve();
}; };
EditorData.prototype.getPresence = function(docId, connections) { EditorData.prototype.getPresence = function(ctx, docId, connections) {
let hvals = []; let hvals = [];
for (let i = 0; i < connections.length; ++i) { for (let i = 0; i < connections.length; ++i) {
if (connections[i].docId === docId) { let conn = connections[i];
hvals.push(utils.getConnectionInfoStr(connections[i])); if (conn.docId === docId && ctx.tenant === tenantManager.getTenantByConnection(ctx, conn)) {
hvals.push(utils.getConnectionInfoStr(conn));
} }
} }
return Promise.resolve(hvals); return Promise.resolve(hvals);
}; };
EditorData.prototype.lockSave = function(docId, userId, ttl) { EditorData.prototype.lockSave = function(ctx, docId, userId, ttl) {
return this._checkAndLock('lockSave', docId, userId, ttl); return this._checkAndLock(ctx, 'lockSave', docId, userId, ttl);
}; };
EditorData.prototype.unlockSave = function(docId, userId) { EditorData.prototype.unlockSave = function(ctx, docId, userId) {
return this._checkAndUnlock('lockSave', docId, userId); return this._checkAndUnlock(ctx, 'lockSave', docId, userId);
}; };
EditorData.prototype.lockAuth = function(docId, userId, ttl) { EditorData.prototype.lockAuth = function(ctx, docId, userId, ttl) {
return this._checkAndLock('lockAuth', docId, userId, ttl); return this._checkAndLock(ctx, 'lockAuth', docId, userId, ttl);
}; };
EditorData.prototype.unlockAuth = function(docId, userId) { EditorData.prototype.unlockAuth = function(ctx, docId, userId) {
return this._checkAndUnlock('lockAuth', docId, userId); return this._checkAndUnlock(ctx, 'lockAuth', docId, userId);
}; };
EditorData.prototype.getDocumentPresenceExpired = function(now) { EditorData.prototype.getDocumentPresenceExpired = function(now) {
return Promise.resolve([]); return Promise.resolve([]);
}; };
EditorData.prototype.removePresenceDocument = function(docId) { EditorData.prototype.removePresenceDocument = function(ctx, docId) {
return Promise.resolve(); return Promise.resolve();
}; };
EditorData.prototype.addLocks = function(docId, locks) { EditorData.prototype.addLocks = function(ctx, docId, locks) {
let data = this._getDocumentData(docId); let data = this._getDocumentData(ctx, docId);
if (!data.locks) { if (!data.locks) {
data.locks = []; data.locks = [];
} }
data.locks = data.locks.concat(locks); data.locks = data.locks.concat(locks);
return Promise.resolve(); return Promise.resolve();
}; };
EditorData.prototype.removeLocks = function(docId) { EditorData.prototype.removeLocks = function(ctx, docId) {
let data = this._getDocumentData(docId); let data = this._getDocumentData(ctx, docId);
data.locks = undefined; data.locks = undefined;
return Promise.resolve(); return Promise.resolve();
}; };
EditorData.prototype.getLocks = function(docId) { EditorData.prototype.getLocks = function(ctx, docId) {
let data = this._getDocumentData(docId); let data = this._getDocumentData(ctx, docId);
return Promise.resolve(data.locks || []); return Promise.resolve(data.locks || []);
}; };
EditorData.prototype.addMessage = function(docId, msg) { EditorData.prototype.addMessage = function(ctx, docId, msg) {
let data = this._getDocumentData(docId); let data = this._getDocumentData(ctx, docId);
if (!data.messages) { if (!data.messages) {
data.messages = []; data.messages = [];
} }
data.messages.push(msg); data.messages.push(msg);
return Promise.resolve(); return Promise.resolve();
}; };
EditorData.prototype.removeMessages = function(docId) { EditorData.prototype.removeMessages = function(ctx, docId) {
let data = this._getDocumentData(docId); let data = this._getDocumentData(ctx, docId);
data.messages = undefined; data.messages = undefined;
return Promise.resolve(); return Promise.resolve();
}; };
EditorData.prototype.getMessages = function(docId) { EditorData.prototype.getMessages = function(ctx, docId) {
let data = this._getDocumentData(docId); let data = this._getDocumentData(ctx, docId);
return Promise.resolve(data.messages || []); return Promise.resolve(data.messages || []);
}; };
EditorData.prototype.setSaved = function(docId, status) { EditorData.prototype.setSaved = function(ctx, docId, status) {
let data = this._getDocumentData(docId); let data = this._getDocumentData(ctx, docId);
data.saved = status; data.saved = status;
return Promise.resolve(); return Promise.resolve();
}; };
EditorData.prototype.getdelSaved = function(docId) { EditorData.prototype.getdelSaved = function(ctx, docId) {
let data = this._getDocumentData(docId); let data = this._getDocumentData(ctx, docId);
let res = data.saved; let res = data.saved;
data.saved = undefined; data.saved = undefined;
return Promise.resolve(res); return Promise.resolve(res);
}; };
EditorData.prototype.setForceSave = function(docId, time, index, baseUrl, changeInfo) { EditorData.prototype.setForceSave = function(ctx, docId, time, index, baseUrl, changeInfo) {
let data = this._getDocumentData(docId); let data = this._getDocumentData(ctx, docId);
data.forceSave = {time: time, index: index, baseUrl: baseUrl, changeInfo: changeInfo, started: false, ended: false}; data.forceSave = {time: time, index: index, baseUrl: baseUrl, changeInfo: changeInfo, started: false, ended: false};
return Promise.resolve(); return Promise.resolve();
}; };
EditorData.prototype.getForceSave = function(docId) { EditorData.prototype.getForceSave = function(ctx, docId) {
let data = this._getDocumentData(docId); let data = this._getDocumentData(ctx, docId);
return Promise.resolve(data.forceSave || null); return Promise.resolve(data.forceSave || null);
}; };
EditorData.prototype.checkAndStartForceSave = function(docId) { EditorData.prototype.checkAndStartForceSave = function(ctx, docId) {
let data = this._getDocumentData(docId); let data = this._getDocumentData(ctx, docId);
let res; let res;
if (data.forceSave && !data.forceSave.started) { if (data.forceSave && !data.forceSave.started) {
data.forceSave.started = true; data.forceSave.started = true;
@ -188,8 +194,8 @@ EditorData.prototype.checkAndStartForceSave = function(docId) {
} }
return Promise.resolve(res); return Promise.resolve(res);
}; };
EditorData.prototype.checkAndSetForceSave = function(docId, time, index, started, ended) { EditorData.prototype.checkAndSetForceSave = function(ctx, docId, time, index, started, ended) {
let data = this._getDocumentData(docId); let data = this._getDocumentData(ctx, docId);
let res; let res;
if (data.forceSave && time === data.forceSave.time && index === data.forceSave.index) { if (data.forceSave && time === data.forceSave.time && index === data.forceSave.index) {
data.forceSave.started = started; data.forceSave.started = started;
@ -198,180 +204,235 @@ EditorData.prototype.checkAndSetForceSave = function(docId, time, index, started
} }
return Promise.resolve(res); return Promise.resolve(res);
}; };
EditorData.prototype.removeForceSave = function(docId) { EditorData.prototype.removeForceSave = function(ctx, docId) {
let data = this._getDocumentData(docId); let data = this._getDocumentData(ctx, docId);
data.forceSave = undefined; data.forceSave = undefined;
return Promise.resolve(); return Promise.resolve();
}; };
EditorData.prototype.cleanDocumentOnExit = function(docId) { EditorData.prototype.cleanDocumentOnExit = function(ctx, docId) {
delete this.data[docId]; let tenantData = this.data[ctx.tenant];
delete this.forceSaveTimer[docId]; if (tenantData) {
delete tenantData[docId];
}
let tenantTimer = this.forceSaveTimer[ctx.tenant];
if (tenantTimer) {
delete tenantTimer[docId];
}
return Promise.resolve(); return Promise.resolve();
}; };
EditorData.prototype.addForceSaveTimerNX = function(docId, expireAt) { EditorData.prototype.addForceSaveTimerNX = function(ctx, docId, expireAt) {
if (!this.forceSaveTimer[docId]) { let tenantTimer = this.forceSaveTimer[ctx.tenant];
this.forceSaveTimer[docId] = expireAt; if (!tenantTimer) {
this.forceSaveTimer[ctx.tenant] = tenantTimer = {};
}
if (!tenantTimer[docId]) {
tenantTimer[docId] = expireAt;
} }
return Promise.resolve(); return Promise.resolve();
}; };
EditorData.prototype.getForceSaveTimer = function(now) { EditorData.prototype.getForceSaveTimer = function(now) {
let res = []; let res = [];
for (let docId in this.forceSaveTimer) { for (let tenant in this.forceSaveTimer) {
if (this.forceSaveTimer.hasOwnProperty(docId)) { if (this.forceSaveTimer.hasOwnProperty(tenant)) {
if (this.forceSaveTimer[docId] < now) { let tenantTimer = this.forceSaveTimer[tenant];
res.push(docId); for (let docId in tenantTimer) {
delete this.forceSaveTimer[docId]; if (tenantTimer.hasOwnProperty(docId)) {
if (tenantTimer[docId] < now) {
res.push([tenant, docId]);
delete tenantTimer[docId];
}
}
} }
} }
} }
return Promise.resolve(res); return Promise.resolve(res);
}; };
EditorData.prototype.addPresenceUniqueUser = function(userId, expireAt, userInfo) { EditorData.prototype.addPresenceUniqueUser = function(ctx, userId, expireAt, userInfo) {
this.uniqueUser[userId] = {expireAt: expireAt, userInfo: userInfo}; let tenantUser = this.uniqueUser[ctx.tenant];
if (!tenantUser) {
this.uniqueUser[ctx.tenant] = tenantUser = {};
}
tenantUser[userId] = {expireAt: expireAt, userInfo: userInfo};
return Promise.resolve(); return Promise.resolve();
}; };
EditorData.prototype.getPresenceUniqueUser = function(nowUTC) { EditorData.prototype.getPresenceUniqueUser = function(ctx, nowUTC) {
let res = []; let res = [];
for (let userId in this.uniqueUser) { let tenantUser = this.uniqueUser[ctx.tenant];
if (this.uniqueUser.hasOwnProperty(userId)) { if (!tenantUser) {
if (this.uniqueUser[userId].expireAt > nowUTC) { this.uniqueUser[ctx.tenant] = tenantUser = {};
let elem = this.uniqueUser[userId]; }
for (let userId in tenantUser) {
if (tenantUser.hasOwnProperty(userId)) {
if (tenantUser[userId].expireAt > nowUTC) {
let elem = tenantUser[userId];
let newElem = {userid: userId, expire: new Date(elem.expireAt * 1000)}; let newElem = {userid: userId, expire: new Date(elem.expireAt * 1000)};
Object.assign(newElem, elem.userInfo); Object.assign(newElem, elem.userInfo);
res.push(newElem); res.push(newElem);
} else { } else {
delete this.uniqueUser[userId]; delete tenantUser[userId];
} }
} }
} }
return Promise.resolve(res); return Promise.resolve(res);
}; };
EditorData.prototype.addPresenceUniqueUsersOfMonth = function(userId, period, userInfo) { EditorData.prototype.addPresenceUniqueUsersOfMonth = function(ctx, userId, period, userInfo) {
if(!this.uniqueUsersOfMonth[period]) { let tenantUser = this.uniqueUsersOfMonth[ctx.tenant];
let expireAt = Date.now() + cfgExpMonthUniqueUsers; if (!tenantUser) {
this.uniqueUsersOfMonth[period] = {expireAt: expireAt, data: {}}; this.uniqueUsersOfMonth[ctx.tenant] = tenantUser = {};
} }
this.uniqueUsersOfMonth[period].data[userId] = userInfo; if(!tenantUser[period]) {
let expireAt = Date.now() + cfgExpMonthUniqueUsers;
tenantUser[period] = {expireAt: expireAt, data: {}};
}
tenantUser[period].data[userId] = userInfo;
return Promise.resolve(); return Promise.resolve();
}; };
EditorData.prototype.getPresenceUniqueUsersOfMonth = function() { EditorData.prototype.getPresenceUniqueUsersOfMonth = function(ctx) {
let res = {}; let res = {};
let nowUTC = Date.now(); let nowUTC = Date.now();
for (let periodId in this.uniqueUsersOfMonth) { let tenantUser = this.uniqueUsersOfMonth[ctx.tenant];
if (this.uniqueUsersOfMonth.hasOwnProperty(periodId)) { if (!tenantUser) {
if (this.uniqueUsersOfMonth[periodId].expireAt <= nowUTC) { this.uniqueUsersOfMonth[ctx.tenant] = tenantUser = {};
delete this.uniqueUsersOfMonth[periodId]; }
for (let periodId in tenantUser) {
if (tenantUser.hasOwnProperty(periodId)) {
if (tenantUser[periodId].expireAt <= nowUTC) {
delete tenantUser[periodId];
} else { } else {
let date = new Date(parseInt(periodId)).toISOString(); let date = new Date(parseInt(periodId)).toISOString();
res[date] = this.uniqueUsersOfMonth[periodId].data; res[date] = tenantUser[periodId].data;
} }
} }
} }
return Promise.resolve(res); return Promise.resolve(res);
}; };
EditorData.prototype.addPresenceUniqueViewUser = function(userId, expireAt, userInfo) { EditorData.prototype.addPresenceUniqueViewUser = function(ctx, userId, expireAt, userInfo) {
this.uniqueViewUser[userId] = {expireAt: expireAt, userInfo: userInfo}; let tenantUser = this.uniqueViewUser[ctx.tenant];
if (!tenantUser) {
this.uniqueViewUser[ctx.tenant] = tenantUser = {};
}
tenantUser[userId] = {expireAt: expireAt, userInfo: userInfo};
return Promise.resolve(); return Promise.resolve();
}; };
EditorData.prototype.getPresenceUniqueViewUser = function(nowUTC) { EditorData.prototype.getPresenceUniqueViewUser = function(ctx, nowUTC) {
let res = []; let res = [];
for (let userId in this.uniqueViewUser) { let tenantUser = this.uniqueViewUser[ctx.tenant];
if (this.uniqueViewUser.hasOwnProperty(userId)) { if (!tenantUser) {
if (this.uniqueViewUser[userId].expireAt > nowUTC) { this.uniqueViewUser[ctx.tenant] = tenantUser = {};
let elem = this.uniqueViewUser[userId]; }
for (let userId in tenantUser) {
if (tenantUser.hasOwnProperty(userId)) {
if (tenantUser[userId].expireAt > nowUTC) {
let elem = tenantUser[userId];
let newElem = {userid: userId, expire: new Date(elem.expireAt * 1000)}; let newElem = {userid: userId, expire: new Date(elem.expireAt * 1000)};
Object.assign(newElem, elem.userInfo); Object.assign(newElem, elem.userInfo);
res.push(newElem); res.push(newElem);
} else { } else {
delete this.uniqueViewUser[userId]; delete tenantUser[userId];
} }
} }
} }
return Promise.resolve(res); return Promise.resolve(res);
}; };
EditorData.prototype.addPresenceUniqueViewUsersOfMonth = function(userId, period, userInfo) { EditorData.prototype.addPresenceUniqueViewUsersOfMonth = function(ctx, userId, period, userInfo) {
if(!this.uniqueViewUsersOfMonth[period]) { let tenantUser = this.uniqueViewUsersOfMonth[ctx.tenant];
let expireAt = Date.now() + cfgExpMonthUniqueUsers; if (!tenantUser) {
this.uniqueViewUsersOfMonth[period] = {expireAt: expireAt, data: {}}; this.uniqueViewUsersOfMonth[ctx.tenant] = tenantUser = {};
} }
this.uniqueViewUsersOfMonth[period].data[userId] = userInfo; if(!tenantUser[period]) {
let expireAt = Date.now() + cfgExpMonthUniqueUsers;
tenantUser[period] = {expireAt: expireAt, data: {}};
}
tenantUser[period].data[userId] = userInfo;
return Promise.resolve(); return Promise.resolve();
}; };
EditorData.prototype.getPresenceUniqueViewUsersOfMonth = function() { EditorData.prototype.getPresenceUniqueViewUsersOfMonth = function(ctx) {
let res = {}; let res = {};
let nowUTC = Date.now(); let nowUTC = Date.now();
for (let periodId in this.uniqueViewUsersOfMonth) { let tenantUser = this.uniqueViewUsersOfMonth[ctx.tenant];
if (this.uniqueViewUsersOfMonth.hasOwnProperty(periodId)) { if (!tenantUser) {
if (this.uniqueViewUsersOfMonth[periodId].expireAt <= nowUTC) { this.uniqueViewUsersOfMonth[ctx.tenant] = tenantUser = {};
delete this.uniqueViewUsersOfMonth[periodId]; }
for (let periodId in tenantUser) {
if (tenantUser.hasOwnProperty(periodId)) {
if (tenantUser[periodId].expireAt <= nowUTC) {
delete tenantUser[periodId];
} else { } else {
let date = new Date(parseInt(periodId)).toISOString(); let date = new Date(parseInt(periodId)).toISOString();
res[date] = this.uniqueViewUsersOfMonth[periodId].data; res[date] = tenantUser[periodId].data;
} }
} }
} }
return Promise.resolve(res); return Promise.resolve(res);
}; };
EditorData.prototype.setEditorConnections = function(countEdit, countLiveView, countView, now, precision) { EditorData.prototype.setEditorConnections = function(ctx, countEdit, countLiveView, countView, now, precision) {
this.stat.push({time: now, edit: countEdit, liveview: countLiveView, view: countView}); let tenantStat = this.stat[ctx.tenant];
if (!tenantStat) {
this.stat[ctx.tenant] = tenantStat = [];
}
tenantStat.push({time: now, edit: countEdit, liveview: countLiveView, view: countView});
let i = 0; let i = 0;
while (i < this.stat.length && this.stat[i] < now - precision[precision.length - 1].val) { while (i < tenantStat.length && tenantStat[i] < now - precision[precision.length - 1].val) {
i++; i++;
} }
this.stat.splice(0, i); tenantStat.splice(0, i);
return Promise.resolve(); return Promise.resolve();
}; };
EditorData.prototype.getEditorConnections = function() { EditorData.prototype.getEditorConnections = function(ctx) {
return Promise.resolve(this.stat); let tenantStat = this.stat[ctx.tenant];
if (!tenantStat) {
this.stat[ctx.tenant] = tenantStat = [];
}
return Promise.resolve(tenantStat);
}; };
EditorData.prototype.setEditorConnectionsCountByShard = function(shardId, count) { EditorData.prototype.setEditorConnectionsCountByShard = function(ctx, shardId, count) {
return Promise.resolve(); return Promise.resolve();
}; };
EditorData.prototype.incrEditorConnectionsCountByShard = function(shardId, count) { EditorData.prototype.incrEditorConnectionsCountByShard = function(ctx, shardId, count) {
return Promise.resolve(); return Promise.resolve();
}; };
EditorData.prototype.getEditorConnectionsCount = function(connections) { EditorData.prototype.getEditorConnectionsCount = function(ctx, connections) {
let count = 0; let count = 0;
for (let i = 0; i < connections.length; ++i) { for (let i = 0; i < connections.length; ++i) {
let conn = connections[i]; let conn = connections[i];
if (!(conn.isCloseCoAuthoring || (conn.user && conn.user.view))) { if (!(conn.isCloseCoAuthoring || (conn.user && conn.user.view)) && ctx.tenant === tenantManager.getTenantByConnection(ctx, conn)) {
count++; count++;
} }
} }
return Promise.resolve(count); return Promise.resolve(count);
}; };
EditorData.prototype.setViewerConnectionsCountByShard = function(shardId, count) { EditorData.prototype.setViewerConnectionsCountByShard = function(ctx, shardId, count) {
return Promise.resolve(); return Promise.resolve();
}; };
EditorData.prototype.incrViewerConnectionsCountByShard = function(shardId, count) { EditorData.prototype.incrViewerConnectionsCountByShard = function(ctx, shardId, count) {
return Promise.resolve(); return Promise.resolve();
}; };
EditorData.prototype.getViewerConnectionsCount = function(connections) { EditorData.prototype.getViewerConnectionsCount = function(ctx, connections) {
let count = 0; let count = 0;
for (let i = 0; i < connections.length; ++i) { for (let i = 0; i < connections.length; ++i) {
let conn = connections[i]; let conn = connections[i];
if (conn.isCloseCoAuthoring || (conn.user && conn.user.view)) { if (conn.isCloseCoAuthoring || (conn.user && conn.user.view) && ctx.tenant === tenantManager.getTenantByConnection(ctx, conn)) {
count++; count++;
} }
} }
return Promise.resolve(count); return Promise.resolve(count);
}; };
EditorData.prototype.setLiveViewerConnectionsCountByShard = function(shardId, count) { EditorData.prototype.setLiveViewerConnectionsCountByShard = function(ctx, shardId, count) {
return Promise.resolve(); return Promise.resolve();
}; };
EditorData.prototype.incrLiveViewerConnectionsCountByShard = function(shardId, count) { EditorData.prototype.incrLiveViewerConnectionsCountByShard = function(ctx, shardId, count) {
return Promise.resolve(); return Promise.resolve();
}; };
EditorData.prototype.getLiveViewerConnectionsCount = function(connections) { EditorData.prototype.getLiveViewerConnectionsCount = function(ctx, connections) {
let count = 0; let count = 0;
for (let i = 0; i < connections.length; ++i) { for (let i = 0; i < connections.length; ++i) {
let conn = connections[i]; let conn = connections[i];
if (utils.isLiveViewer(conn)) { if (utils.isLiveViewer(conn) && ctx.tenant === tenantManager.getTenantByConnection(ctx, conn)) {
count++; count++;
} }
} }

View File

@ -41,8 +41,8 @@ var utils = require('./../../Common/sources/utils');
var constants = require('./../../Common/sources/constants'); var constants = require('./../../Common/sources/constants');
var storageBase = require('./../../Common/sources/storage-base'); var storageBase = require('./../../Common/sources/storage-base');
var formatChecker = require('./../../Common/sources/formatchecker'); var formatChecker = require('./../../Common/sources/formatchecker');
var logger = require('./../../Common/sources/logger');
const commonDefines = require('./../../Common/sources/commondefines'); const commonDefines = require('./../../Common/sources/commondefines');
const operationContext = require('./../../Common/sources/operationContext');
var config = require('config'); var config = require('config');
var configServer = config.get('services.CoAuthoring.server'); var configServer = config.get('services.CoAuthoring.server');
@ -57,9 +57,12 @@ const PATTERN_ENCRYPTED = 'ENCRYPTED;';
exports.uploadTempFile = function(req, res) { exports.uploadTempFile = function(req, res) {
return co(function* () { return co(function* () {
var docId = 'uploadTempFile'; var docId = 'uploadTempFile';
let ctx = new operationContext.Context();
try { try {
ctx.initFromRequest(req);
ctx.logger.info('uploadTempFile start');
let params; let params;
let authRes = docsCoServer.getRequestParams(docId, req, true); let authRes = yield docsCoServer.getRequestParams(ctx, req, true);
if(authRes.code === constants.NO_ERROR){ if(authRes.code === constants.NO_ERROR){
params = authRes.params; params = authRes.params;
} else { } else {
@ -67,33 +70,34 @@ exports.uploadTempFile = function(req, res) {
return; return;
} }
docId = params.key; docId = params.key;
logger.debug('Start uploadTempFile: docId = %s', docId); ctx.setDocId(docId);
ctx.logger.debug('Start uploadTempFile');
if (docId && constants.DOC_ID_REGEX.test(docId) && req.body && Buffer.isBuffer(req.body)) { if (docId && constants.DOC_ID_REGEX.test(docId) && req.body && Buffer.isBuffer(req.body)) {
var task = yield* taskResult.addRandomKeyTask(docId); var task = yield* taskResult.addRandomKeyTask(ctx, docId);
var strPath = task.key + '/' + docId + '.tmp'; var strPath = task.key + '/' + docId + '.tmp';
yield storageBase.putObject(strPath, req.body, req.body.length); yield storageBase.putObject(ctx, strPath, req.body, req.body.length);
var url = yield storageBase.getSignedUrl(utils.getBaseUrlByRequest(req), strPath, var url = yield storageBase.getSignedUrl(ctx, utils.getBaseUrlByRequest(req), strPath,
commonDefines.c_oAscUrlTypes.Temporary); commonDefines.c_oAscUrlTypes.Temporary);
utils.fillResponse(req, res, new commonDefines.ConvertStatus(constants.NO_ERROR, url), false); utils.fillResponse(req, res, new commonDefines.ConvertStatus(constants.NO_ERROR, url), false);
} else { } else {
if (!constants.DOC_ID_REGEX.test(docId)) { if (!constants.DOC_ID_REGEX.test(docId)) {
logger.warn('Error uploadTempFile unexpected key: docId = %s', docId); ctx.logger.warn('Error uploadTempFile unexpected key');
} }
utils.fillResponse(req, res, new commonDefines.ConvertStatus(constants.UNKNOWN), false); utils.fillResponse(req, res, new commonDefines.ConvertStatus(constants.UNKNOWN), false);
} }
logger.debug('End uploadTempFile: docId = %s', docId); } catch (e) {
} ctx.logger.error('Error uploadTempFile: %s', e.stack);
catch (e) {
logger.error('Error uploadTempFile: docId = %s\r\n%s', docId, e.stack);
utils.fillResponse(req, res, new commonDefines.ConvertStatus(constants.UNKNOWN), false); utils.fillResponse(req, res, new commonDefines.ConvertStatus(constants.UNKNOWN), false);
} finally {
ctx.logger.info('uploadTempFile end');
} }
}); });
}; };
function checkJwtUpload(docId, errorName, token){ function* checkJwtUpload(ctx, errorName, token){
let checkJwtRes = docsCoServer.checkJwt(docId, token, commonDefines.c_oAscSecretType.Session); let checkJwtRes = yield docsCoServer.checkJwt(ctx, token, commonDefines.c_oAscSecretType.Session);
return checkJwtUploadTransformRes(docId, errorName, checkJwtRes); return checkJwtUploadTransformRes(ctx, errorName, checkJwtRes);
} }
function checkJwtUploadTransformRes(docId, errorName, checkJwtRes){ function checkJwtUploadTransformRes(ctx, errorName, checkJwtRes){
var res = {err: true, docId: null, userid: null, encrypted: null}; var res = {err: true, docId: null, userid: null, encrypted: null};
if (checkJwtRes.decoded) { if (checkJwtRes.decoded) {
var doc = checkJwtRes.decoded.document; var doc = checkJwtRes.decoded.document;
@ -106,94 +110,100 @@ function checkJwtUploadTransformRes(docId, errorName, checkJwtRes){
res.userid = edit.user.id; res.userid = edit.user.id;
} }
} else { } else {
logger.warn('Error %s jwt: docId = %s\r\n%s', errorName, docId, 'access deny'); ctx.logger.warn('Error %s jwt: %s', errorName, 'access deny');
} }
} else { } else {
logger.warn('Error %s jwt: docId = %s\r\n%s', errorName, docId, checkJwtRes.description); ctx.logger.warn('Error %s jwt: %s', errorName, checkJwtRes.description);
} }
return res; return res;
} }
exports.uploadImageFileOld = function(req, res) { exports.uploadImageFileOld = function(req, res) {
var docId = req.params.docid; return co(function* () {
logger.debug('Start uploadImageFileOld: docId = %s', docId); let ctx = new operationContext.Context();
var userid = req.params.userid; ctx.initFromRequest(req);
if (cfgTokenEnableBrowser) { var docId = req.params.docid;
var checkJwtRes = checkJwtUpload(docId, 'uploadImageFileOld', req.query['token']); var userid = req.params.userid;
if(!checkJwtRes.err){ ctx.init(ctx.tenant, docId, userid);
docId = checkJwtRes.docId || docId; ctx.logger.debug('Start uploadImageFileOld');
userid = checkJwtRes.userid || userid; if (cfgTokenEnableBrowser) {
} else { var checkJwtRes = yield* checkJwtUpload(ctx, 'uploadImageFileOld', req.query['token']);
res.sendStatus(403); if(!checkJwtRes.err){
return; docId = checkJwtRes.docId || docId;
} userid = checkJwtRes.userid || userid;
}
var listImages = [];
//todo userid
if (docId) {
var isError = false;
var form = new multiparty.Form();
form.on('error', function(err) {
logger.error('Error parsing form: docId = %s\r\n%s', docId, err.toString());
res.sendStatus(400);
});
form.on('part', function(part) {
if (!part.filename) {
// ignore field's content
part.resume();
}
if (part.filename) {
if (part.byteCount > cfgImageSize) {
isError = true;
}
if (isError) {
part.resume();
} else {
//в начале пишется хеш, чтобы избежать ошибок при параллельном upload в совместном редактировании
var strImageName = crypto.randomBytes(16).toString("hex");
var strPath = docId + '/media/' + strImageName + '.jpg';
listImages.push(strPath);
utils.stream2Buffer(part).then(function(buffer) {
return storageBase.putObject(strPath, buffer, buffer.length);
}).then(function() {
part.resume();
}).catch(function(err) {
logger.error('Upload putObject: docId = %s\r\n%s', docId, err.stack);
isError = true;
part.resume();
});
}
}
part.on('error', function(err) {
logger.error('Error parsing form part: docId = %s\r\n%s', docId, err.toString());
});
});
form.on('close', function() {
if (isError) {
res.sendStatus(400);
} else { } else {
storageBase.getSignedUrlsByArray(utils.getBaseUrlByRequest(req), listImages, docId, res.sendStatus(403);
commonDefines.c_oAscUrlTypes.Session).then(function(urls) { return;
var outputData = {'type': 0, 'error': constants.NO_ERROR, 'urls': urls, 'input': req.query};
var output = '<html><head><script type="text/javascript">function load(){ parent.postMessage("';
output += JSON.stringify(outputData).replace(/"/g, '\\"');
output += '", "*"); }</script></head><body onload="load()"></body></html>';
//res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Content-Type', 'text/html');
res.send(output);
logger.debug('End uploadImageFileOld: docId = %s %s', docId, output);
}
).catch(function(err) {
res.sendStatus(400);
logger.error('upload getSignedUrlsByArray: docId = %s\r\n%s', docId, err.stack);
});
} }
}); }
form.parse(req); ctx.init(ctx.tenant, docId, userid);
} else { var listImages = [];
logger.debug('Error params uploadImageFileOld: docId = %s', docId); //todo userid
res.sendStatus(400); if (docId) {
} var isError = false;
var form = new multiparty.Form();
form.on('error', function(err) {
ctx.logger.error('Error parsing form:%s', err.toString());
res.sendStatus(400);
});
form.on('part', function(part) {
if (!part.filename) {
// ignore field's content
part.resume();
}
if (part.filename) {
if (part.byteCount > cfgImageSize) {
isError = true;
}
if (isError) {
part.resume();
} else {
//в начале пишется хеш, чтобы избежать ошибок при параллельном upload в совместном редактировании
var strImageName = crypto.randomBytes(16).toString("hex");
var strPath = docId + '/media/' + strImageName + '.jpg';
listImages.push(strPath);
utils.stream2Buffer(part).then(function(buffer) {
return storageBase.putObject(ctx, strPath, buffer, buffer.length);
}).then(function() {
part.resume();
}).catch(function(err) {
ctx.logger.error('Upload putObject:%s', err.stack);
isError = true;
part.resume();
});
}
}
part.on('error', function(err) {
ctx.logger.error('Error parsing form part:%s', err.toString());
});
});
form.on('close', function() {
if (isError) {
res.sendStatus(400);
} else {
storageBase.getSignedUrlsByArray(ctx, utils.getBaseUrlByRequest(req), listImages, docId,
commonDefines.c_oAscUrlTypes.Session).then(function(urls) {
var outputData = {'type': 0, 'error': constants.NO_ERROR, 'urls': urls, 'input': req.query};
var output = '<html><head><script type="text/javascript">function load(){ parent.postMessage("';
output += JSON.stringify(outputData).replace(/"/g, '\\"');
output += '", "*"); }</script></head><body onload="load()"></body></html>';
//res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Content-Type', 'text/html');
res.send(output);
ctx.logger.debug('End uploadImageFileOld:%s', output);
}
).catch(function(err) {
res.sendStatus(400);
ctx.logger.error('upload getSignedUrlsByArray:%s', err.stack);
});
}
});
form.parse(req);
} else {
ctx.logger.debug('Error params uploadImageFileOld');
res.sendStatus(400);
}
});
}; };
exports.uploadImageFile = function(req, res) { exports.uploadImageFile = function(req, res) {
return co(function* () { return co(function* () {
@ -201,18 +211,21 @@ exports.uploadImageFile = function(req, res) {
var docId = 'null'; var docId = 'null';
let output = {}; let output = {};
let isValidJwt = true; let isValidJwt = true;
let ctx = new operationContext.Context();
try { try {
ctx.initFromRequest(req);
docId = req.params.docid; docId = req.params.docid;
ctx.setDocId(docId);
let encrypted = false; let encrypted = false;
logger.debug('Start uploadImageFile: docId = %s', docId); ctx.logger.debug('Start uploadImageFile');
if (cfgTokenEnableBrowser) { if (cfgTokenEnableBrowser) {
let checkJwtRes = docsCoServer.checkJwtHeader(docId, req, 'Authorization', 'Bearer ', commonDefines.c_oAscSecretType.Session); let checkJwtRes = yield docsCoServer.checkJwtHeader(ctx, req, 'Authorization', 'Bearer ', commonDefines.c_oAscSecretType.Session);
if (!checkJwtRes) { if (!checkJwtRes) {
//todo remove compatibility with previous versions //todo remove compatibility with previous versions
checkJwtRes = docsCoServer.checkJwt(docId, req.query['token'], commonDefines.c_oAscSecretType.Session); checkJwtRes = yield docsCoServer.checkJwt(ctx, req.query['token'], commonDefines.c_oAscSecretType.Session);
} }
let transformedRes = checkJwtUploadTransformRes(docId, 'uploadImageFile', checkJwtRes); let transformedRes = checkJwtUploadTransformRes(ctx, 'uploadImageFile', checkJwtRes);
if (!transformedRes.err) { if (!transformedRes.err) {
docId = transformedRes.docId || docId; docId = transformedRes.docId || docId;
encrypted = transformedRes.encrypted; encrypted = transformedRes.encrypted;
@ -220,11 +233,12 @@ exports.uploadImageFile = function(req, res) {
isValidJwt = false; isValidJwt = false;
} }
} }
ctx.setDocId(docId);
if (isValidJwt && docId && req.body && Buffer.isBuffer(req.body)) { if (isValidJwt && docId && req.body && Buffer.isBuffer(req.body)) {
let buffer = req.body; let buffer = req.body;
if (buffer.length <= cfgImageSize) { if (buffer.length <= cfgImageSize) {
var format = formatChecker.getImageFormat(buffer, undefined); var format = formatChecker.getImageFormat(ctx, buffer);
var formatStr = formatChecker.getStringFromFormat(format); var formatStr = formatChecker.getStringFromFormat(format);
if (encrypted && PATTERN_ENCRYPTED === buffer.toString('utf8', 0, PATTERN_ENCRYPTED.length)) { if (encrypted && PATTERN_ENCRYPTED === buffer.toString('utf8', 0, PATTERN_ENCRYPTED.length)) {
formatStr = buffer.toString('utf8', PATTERN_ENCRYPTED.length, buffer.indexOf(';', PATTERN_ENCRYPTED.length)); formatStr = buffer.toString('utf8', PATTERN_ENCRYPTED.length, buffer.indexOf(';', PATTERN_ENCRYPTED.length));
@ -236,20 +250,20 @@ exports.uploadImageFile = function(req, res) {
var strImageName = crypto.randomBytes(16).toString("hex"); var strImageName = crypto.randomBytes(16).toString("hex");
var strPathRel = 'media/' + strImageName + '.' + formatStr; var strPathRel = 'media/' + strImageName + '.' + formatStr;
var strPath = docId + '/' + strPathRel; var strPath = docId + '/' + strPathRel;
yield storageBase.putObject(strPath, buffer, buffer.length); yield storageBase.putObject(ctx, strPath, buffer, buffer.length);
output[strPathRel] = yield storageBase.getSignedUrl(utils.getBaseUrlByRequest(req), strPath, output[strPathRel] = yield storageBase.getSignedUrl(ctx, utils.getBaseUrlByRequest(req), strPath,
commonDefines.c_oAscUrlTypes.Session); commonDefines.c_oAscUrlTypes.Session);
isError = false; isError = false;
} else { } else {
logger.debug('uploadImageFile format is not supported: docId = %s', docId); ctx.logger.debug('uploadImageFile format is not supported');
} }
} else { } else {
logger.debug('uploadImageFile size limit exceeded: buffer.length = %d docId = %s', buffer.length, docId); ctx.logger.debug('uploadImageFile size limit exceeded: buffer.length = %d', buffer.length);
} }
} }
} catch (e) { } catch (e) {
isError = true; isError = true;
logger.error('Error uploadImageFile: docId = %s\r\n%s', docId, e.stack); ctx.logger.error('Error uploadImageFile:%s', e.stack);
} finally { } finally {
try { try {
if (!isError) { if (!isError) {
@ -258,9 +272,9 @@ exports.uploadImageFile = function(req, res) {
} else { } else {
res.sendStatus(isValidJwt ? 400 : 403); res.sendStatus(isValidJwt ? 400 : 403);
} }
logger.debug('End uploadImageFile: isError = %s docId = %s', isError, docId); ctx.logger.debug('End uploadImageFile: isError = %s', isError);
} catch (e) { } catch (e) {
logger.error('Error uploadImageFile: docId = %s\r\n%s', docId, e.stack); ctx.logger.error('Error uploadImageFile:%s', e.stack);
} }
} }
}); });

View File

@ -46,6 +46,7 @@ var logger = require('./../../Common/sources/logger');
var constants = require('./../../Common/sources/constants'); var constants = require('./../../Common/sources/constants');
var commondefines = require('./../../Common/sources/commondefines'); var commondefines = require('./../../Common/sources/commondefines');
var queueService = require('./../../Common/sources/taskqueueRabbitMQ'); var queueService = require('./../../Common/sources/taskqueueRabbitMQ');
var operationContext = require('./../../Common/sources/operationContext');
var pubsubService = require('./pubsubRabbitMQ'); var pubsubService = require('./pubsubRabbitMQ');
var cfgExpFilesCron = config.get('expire.filesCron'); var cfgExpFilesCron = config.get('expire.filesCron');
@ -65,31 +66,36 @@ let expDocumentsStep = getCronStep(cfgExpDocumentsCron);
var checkFileExpire = function() { var checkFileExpire = function() {
return co(function* () { return co(function* () {
let ctx = new operationContext.Context();
try { try {
logger.debug('checkFileExpire start'); ctx.logger.info('checkFileExpire start');
let removedCount = 0;
var expired; var expired;
var removedCount = 0;
var currentRemovedCount; var currentRemovedCount;
do { do {
currentRemovedCount = 0; currentRemovedCount = 0;
expired = yield taskResult.getExpired(cfgExpFilesRemovedAtOnce, cfgExpFiles); expired = yield taskResult.getExpired(ctx, cfgExpFilesRemovedAtOnce, cfgExpFiles);
for (var i = 0; i < expired.length; ++i) { for (var i = 0; i < expired.length; ++i) {
var docId = expired[i].id; let tenant = expired[i].tenant;
let docId = expired[i].id;
ctx.init(tenant, docId, ctx.userId);
//todo tenant
//проверяем что никто не сидит в документе //проверяем что никто не сидит в документе
let editorsCount = yield docsCoServer.getEditorsCountPromise(docId); let editorsCount = yield docsCoServer.getEditorsCountPromise(ctx, docId);
if(0 === editorsCount){ if(0 === editorsCount){
if (yield canvasService.cleanupCache(docId)) { if (yield canvasService.cleanupCache(ctx)) {
currentRemovedCount++; currentRemovedCount++;
} }
} else { } else {
logger.debug('checkFileExpire expire but presence: editorsCount = %d; docId = %s', editorsCount, docId); ctx.logger.debug('checkFileExpire expire but presence: editorsCount = %d', editorsCount);
} }
} }
removedCount += currentRemovedCount; removedCount += currentRemovedCount;
} while (currentRemovedCount > 0); } while (currentRemovedCount > 0);
logger.debug('checkFileExpire end: removedCount = %d', removedCount); ctx.initDefault();
ctx.logger.info('checkFileExpire end: removedCount = %d', removedCount);
} catch (e) { } catch (e) {
logger.error('checkFileExpire error:\r\n%s', e.stack); ctx.logger.error('checkFileExpire error: %s', e.stack);
} finally { } finally {
setTimeout(checkFileExpire, expFilesStep); setTimeout(checkFileExpire, expFilesStep);
} }
@ -100,8 +106,9 @@ var checkDocumentExpire = function() {
var queue = null; var queue = null;
var removedCount = 0; var removedCount = 0;
var startSaveCount = 0; var startSaveCount = 0;
let ctx = new operationContext.Context();
try { try {
logger.debug('checkDocumentExpire start'); ctx.logger.info('checkDocumentExpire start');
var now = (new Date()).getTime(); var now = (new Date()).getTime();
let expiredKeys = yield docsCoServer.editorData.getDocumentPresenceExpired(now); let expiredKeys = yield docsCoServer.editorData.getDocumentPresenceExpired(now);
if (expiredKeys.length > 0) { if (expiredKeys.length > 0) {
@ -109,30 +116,33 @@ var checkDocumentExpire = function() {
yield queue.initPromise(true, false, false, false, false, false); yield queue.initPromise(true, false, false, false, false, false);
for (var i = 0; i < expiredKeys.length; ++i) { for (var i = 0; i < expiredKeys.length; ++i) {
var docId = expiredKeys[i]; let tenant = expiredKeys[i][0];
let docId = expiredKeys[i][1];
if (docId) { if (docId) {
var hasChanges = yield docsCoServer.hasChanges(docId); ctx.init(tenant, docId, ctx.userId);
var hasChanges = yield docsCoServer.hasChanges(ctx, docId);
if (hasChanges) { if (hasChanges) {
yield docsCoServer.createSaveTimerPromise(docId, null, null, queue, true); yield docsCoServer.createSaveTimer(ctx, docId, null, null, queue, true);
startSaveCount++; startSaveCount++;
} else { } else {
yield docsCoServer.cleanDocumentOnExitNoChangesPromise(docId); yield docsCoServer.cleanDocumentOnExitNoChangesPromise(ctx, docId);
removedCount++; removedCount++;
} }
} }
} }
} }
ctx.initDefault();
ctx.logger.info('checkDocumentExpire end: startSaveCount = %d, removedCount = %d', startSaveCount, removedCount);
} catch (e) { } catch (e) {
logger.error('checkDocumentExpire error:\r\n%s', e.stack); ctx.logger.error('checkDocumentExpire error: %s', e.stack);
} finally { } finally {
try { try {
if (queue) { if (queue) {
yield queue.close(); yield queue.close();
} }
} catch (e) { } catch (e) {
logger.error('checkDocumentExpire error:\r\n%s', e.stack); ctx.logger.error('checkDocumentExpire error: %s', e.stack);
} }
logger.debug('checkDocumentExpire end: startSaveCount = %d, removedCount = %d', startSaveCount, removedCount);
setTimeout(checkDocumentExpire, expDocumentsStep); setTimeout(checkDocumentExpire, expDocumentsStep);
} }
}); });
@ -141,8 +151,9 @@ let forceSaveTimeout = function() {
return co(function* () { return co(function* () {
let queue = null; let queue = null;
let pubsub = null; let pubsub = null;
let ctx = new operationContext.Context();
try { try {
logger.debug('forceSaveTimeout start'); ctx.logger.info('forceSaveTimeout start');
let now = (new Date()).getTime(); let now = (new Date()).getTime();
let expiredKeys = yield docsCoServer.editorData.getForceSaveTimer(now); let expiredKeys = yield docsCoServer.editorData.getForceSaveTimer(now);
if (expiredKeys.length > 0) { if (expiredKeys.length > 0) {
@ -154,18 +165,21 @@ let forceSaveTimeout = function() {
let actions = []; let actions = [];
for (let i = 0; i < expiredKeys.length; ++i) { for (let i = 0; i < expiredKeys.length; ++i) {
let docId = expiredKeys[i]; let tenant = expiredKeys[i][0];
let docId = expiredKeys[i][1];
if (docId) { if (docId) {
actions.push(docsCoServer.startForceSave(docId, commondefines.c_oAscForceSaveTypes.Timeout, ctx.init(tenant, docId, ctx.userId);
actions.push(docsCoServer.startForceSave(ctx, docId, commondefines.c_oAscForceSaveTypes.Timeout,
undefined, undefined, undefined, undefined, undefined, undefined, queue, pubsub)); undefined, undefined, undefined, undefined, undefined, undefined, queue, pubsub));
} }
} }
yield Promise.all(actions); yield Promise.all(actions);
logger.debug('forceSaveTimeout actions.length %d', actions.length); ctx.logger.debug('forceSaveTimeout actions.length %d', actions.length);
} }
logger.debug('forceSaveTimeout end'); ctx.initDefault();
ctx.logger.info('forceSaveTimeout end');
} catch (e) { } catch (e) {
logger.error('forceSaveTimeout error:\r\n%s', e.stack); ctx.logger.error('forceSaveTimeout error: %s', e.stack);
} finally { } finally {
try { try {
if (queue) { if (queue) {
@ -175,7 +189,7 @@ let forceSaveTimeout = function() {
yield pubsub.close(); yield pubsub.close();
} }
} catch (e) { } catch (e) {
logger.error('checkDocumentExpire error:\r\n%s', e.stack); ctx.logger.error('checkDocumentExpire error: %s', e.stack);
} }
setTimeout(forceSaveTimeout, cfgForceSaveStep); setTimeout(forceSaveTimeout, cfgForceSaveStep);
} }

View File

@ -46,22 +46,21 @@ var pool = mysql.createPool({
timezone : 'Z', timezone : 'Z',
flags : '-FOUND_ROWS' flags : '-FOUND_ROWS'
}); });
var logger = require('./../../Common/sources/logger');
exports.sqlQuery = function (sqlCommand, callbackFunction, opt_noModifyRes, opt_noLog, opt_values) { exports.sqlQuery = function (ctx, sqlCommand, callbackFunction, opt_noModifyRes, opt_noLog, opt_values) {
pool.getConnection(function(err, connection) { pool.getConnection(function(err, connection) {
if (err) { if (err) {
logger.error('pool.getConnection error: %s', err); ctx.logger.error('pool.getConnection error: %s', err);
if (callbackFunction) callbackFunction(err, null); if (callbackFunction) callbackFunction(err, null);
return; return;
} }
let queryCallback = function (error, result) { let queryCallback = function (error, result) {
connection.release(); connection.release();
if (error) { if (error) {
logger.error('________________________error_____________________'); ctx.logger.error('________________________error_____________________');
logger.error('sqlQuery: %s sqlCommand: %s', error.code, sqlCommand); ctx.logger.error('sqlQuery: %s sqlCommand: %s', error.code, sqlCommand);
logger.error(error); ctx.logger.error(error);
logger.error('_____________________end_error_____________________'); ctx.logger.error('_____________________end_error_____________________');
} }
if (callbackFunction) callbackFunction(error, result); if (callbackFunction) callbackFunction(error, result);
}; };
@ -82,7 +81,7 @@ let concatParams = function (val1, val2) {
}; };
exports.concatParams = concatParams; exports.concatParams = concatParams;
exports.upsert = function(task, opt_updateUserIndex) { exports.upsert = function(ctx, task, opt_updateUserIndex) {
return new Promise(function(resolve, reject) { return new Promise(function(resolve, reject) {
task.completeDefaults(); task.completeDefaults();
let dateNow = new Date(); let dateNow = new Date();
@ -93,6 +92,7 @@ exports.upsert = function(task, opt_updateUserIndex) {
userCallback.fromValues(task.userIndex, task.callback); userCallback.fromValues(task.userIndex, task.callback);
cbInsert = userCallback.toSQLInsert(); cbInsert = userCallback.toSQLInsert();
} }
let p0 = addSqlParam(task.tenant, values);
let p1 = addSqlParam(task.key, values); let p1 = addSqlParam(task.key, values);
let p2 = addSqlParam(task.status, values); let p2 = addSqlParam(task.status, values);
let p3 = addSqlParam(task.statusInfo, values); let p3 = addSqlParam(task.statusInfo, values);
@ -102,8 +102,8 @@ exports.upsert = function(task, opt_updateUserIndex) {
let p7 = addSqlParam(cbInsert, values); let p7 = addSqlParam(cbInsert, values);
let p8 = addSqlParam(task.baseurl, values); let p8 = addSqlParam(task.baseurl, values);
let p9 = addSqlParam(dateNow, values); let p9 = addSqlParam(dateNow, values);
var sqlCommand = 'INSERT INTO task_result (id, status, status_info, last_open_date, user_index, change_id, callback, baseurl)'+ var sqlCommand = 'INSERT INTO task_result (tenant, id, status, status_info, last_open_date, user_index, change_id, callback, baseurl)'+
` VALUES (${p1}, ${p2}, ${p3}, ${p4}, ${p5}, ${p6}, ${p7}, ${p8}) ON DUPLICATE KEY UPDATE` + ` VALUES (${p0}, ${p1}, ${p2}, ${p3}, ${p4}, ${p5}, ${p6}, ${p7}, ${p8}) ON DUPLICATE KEY UPDATE` +
` last_open_date = ${p9}`; ` last_open_date = ${p9}`;
if (task.callback) { if (task.callback) {
let p10 = addSqlParam(JSON.stringify(task.callback), values); let p10 = addSqlParam(JSON.stringify(task.callback), values);
@ -118,7 +118,7 @@ exports.upsert = function(task, opt_updateUserIndex) {
} }
sqlCommand += ';'; sqlCommand += ';';
exports.sqlQuery(sqlCommand, function(error, result) { exports.sqlQuery(ctx, sqlCommand, function(error, result) {
if (error) { if (error) {
reject(error); reject(error);
} else { } else {

View File

@ -62,11 +62,9 @@ types.setTypeParser(1184, function(stringValue) {
return new Date(stringValue + '+0000'); return new Date(stringValue + '+0000');
}); });
var logger = require('./../../Common/sources/logger');
var maxPacketSize = configSql.get('max_allowed_packet'); var maxPacketSize = configSql.get('max_allowed_packet');
exports.sqlQuery = function(sqlCommand, callbackFunction, opt_noModifyRes, opt_noLog, opt_values) { exports.sqlQuery = function(ctx, sqlCommand, callbackFunction, opt_noModifyRes, opt_noLog, opt_values) {
co(function *() { co(function *() {
var result = null; var result = null;
var error = null; var error = null;
@ -75,7 +73,7 @@ exports.sqlQuery = function(sqlCommand, callbackFunction, opt_noModifyRes, opt_n
} catch (err) { } catch (err) {
error = err; error = err;
if (!opt_noLog) { if (!opt_noLog) {
logger.warn('sqlQuery error sqlCommand: %s:\r\n%s', sqlCommand.slice(0, 50), err.stack); ctx.logger.warn('sqlQuery error sqlCommand: %s: %s', sqlCommand.slice(0, 50), err.stack);
} }
} finally { } finally {
if (callbackFunction) { if (callbackFunction) {
@ -112,6 +110,7 @@ function getUpsertString(task, values) {
userCallback.fromValues(task.userIndex, task.callback); userCallback.fromValues(task.userIndex, task.callback);
cbInsert = userCallback.toSQLInsert(); cbInsert = userCallback.toSQLInsert();
} }
let p0 = addSqlParam(task.tenant, values);
let p1 = addSqlParam(task.key, values); let p1 = addSqlParam(task.key, values);
let p2 = addSqlParam(task.status, values); let p2 = addSqlParam(task.status, values);
let p3 = addSqlParam(task.statusInfo, values); let p3 = addSqlParam(task.statusInfo, values);
@ -123,9 +122,9 @@ function getUpsertString(task, values) {
if (isSupportOnConflict) { if (isSupportOnConflict) {
let p9 = addSqlParam(dateNow, values); let p9 = addSqlParam(dateNow, values);
//http://stackoverflow.com/questions/34762732/how-to-find-out-if-an-upsert-was-an-update-with-postgresql-9-5-upsert //http://stackoverflow.com/questions/34762732/how-to-find-out-if-an-upsert-was-an-update-with-postgresql-9-5-upsert
let sqlCommand = "INSERT INTO task_result (id, status, status_info, last_open_date, user_index, change_id, callback, baseurl)"; let sqlCommand = "INSERT INTO task_result (tenant, id, status, status_info, last_open_date, user_index, change_id, callback, baseurl)";
sqlCommand += ` VALUES (${p1}, ${p2}, ${p3}, ${p4}, ${p5}, ${p6}, ${p7}, ${p8})`; sqlCommand += ` VALUES (${p0}, ${p1}, ${p2}, ${p3}, ${p4}, ${p5}, ${p6}, ${p7}, ${p8})`;
sqlCommand += ` ON CONFLICT (id) DO UPDATE SET last_open_date = ${p9}`; sqlCommand += ` ON CONFLICT (tenant, id) DO UPDATE SET last_open_date = ${p9}`;
if (task.callback) { if (task.callback) {
let p10 = addSqlParam(JSON.stringify(task.callback), values); let p10 = addSqlParam(JSON.stringify(task.callback), values);
sqlCommand += `, callback = task_result.callback || '${sqlBase.UserCallback.prototype.delimiter}{"userIndex":' `; sqlCommand += `, callback = task_result.callback || '${sqlBase.UserCallback.prototype.delimiter}{"userIndex":' `;
@ -138,20 +137,20 @@ function getUpsertString(task, values) {
sqlCommand += ", user_index = task_result.user_index + 1 RETURNING user_index as userindex;"; sqlCommand += ", user_index = task_result.user_index + 1 RETURNING user_index as userindex;";
return sqlCommand; return sqlCommand;
} else { } else {
return `SELECT * FROM merge_db(${p1}, ${p2}, ${p3}, ${p4}, ${p5}, ${p6}, ${p7}, ${p8});`; return `SELECT * FROM merge_db(${p0}, ${p1}, ${p2}, ${p3}, ${p4}, ${p5}, ${p6}, ${p7}, ${p8});`;
} }
} }
exports.upsert = function(task) { exports.upsert = function(ctx, task) {
return new Promise(function(resolve, reject) { return new Promise(function(resolve, reject) {
let values = []; let values = [];
var sqlCommand = getUpsertString(task, values); var sqlCommand = getUpsertString(task, values);
exports.sqlQuery(sqlCommand, function(error, result) { exports.sqlQuery(ctx, sqlCommand, function(error, result) {
if (error) { if (error) {
if (isSupportOnConflict && '42601' === error.code) { if (isSupportOnConflict && '42601' === error.code) {
//SYNTAX ERROR //SYNTAX ERROR
isSupportOnConflict = false; isSupportOnConflict = false;
logger.warn('checkIsSupportOnConflict false'); ctx.logger.warn('checkIsSupportOnConflict false');
resolve(exports.upsert(task)); resolve(exports.upsert(ctx, task));
} else { } else {
reject(error); reject(error);
} }
@ -167,12 +166,13 @@ exports.upsert = function(task) {
}, true, undefined, values); }, true, undefined, values);
}); });
}; };
exports.insertChanges = function(tableChanges, startIndex, objChanges, docId, index, user, callback) { exports.insertChanges = function(ctx, tableChanges, startIndex, objChanges, docId, index, user, callback) {
let i = startIndex; let i = startIndex;
if (i >= objChanges.length) { if (i >= objChanges.length) {
return; return;
} }
let isSupported = true; let isSupported = true;
let tenant = [];
let id = []; let id = [];
let changeId = []; let changeId = [];
let userId = []; let userId = [];
@ -181,27 +181,28 @@ exports.insertChanges = function(tableChanges, startIndex, objChanges, docId, in
let change = []; let change = [];
let time = []; let time = [];
//Postgres 9.4 multi-argument unnest //Postgres 9.4 multi-argument unnest
let sqlCommand = `INSERT INTO ${tableChanges} (id, change_id, user_id, user_id_original, user_name, change_data, change_date) `; let sqlCommand = `INSERT INTO ${tableChanges} (tenant, id, change_id, user_id, user_id_original, user_name, change_data, change_date) `;
sqlCommand += "SELECT * FROM UNNEST ($1::text[], $2::int[], $3::text[], $4::text[], $5::text[], $6::text[], $7::timestamp[]);"; sqlCommand += "SELECT * FROM UNNEST ($1::text[], $2::text[], $3::int[], $4::text[], $5::text[], $6::text[], $7::text[], $8::timestamp[]);";
let values = [id, changeId, userId, userIdOriginal, username, change, time]; let values = [tenant, id, changeId, userId, userIdOriginal, username, change, time];
let curLength = sqlCommand.length; let curLength = sqlCommand.length;
for (; i < objChanges.length; ++i) { for (; i < objChanges.length; ++i) {
//4 is max utf8 bytes per symbol //4 is max utf8 bytes per symbol
curLength += 4 * (docId.length + user.id.length + user.idOriginal.length + user.username.length + objChanges[i].change.length) + 4 + 8; curLength += 4 * (docId.length + user.id.length + user.idOriginal.length + user.username.length + objChanges[i].change.length) + 4 + 8;
if (curLength >= maxPacketSize && i > startIndex) { if (curLength >= maxPacketSize && i > startIndex) {
exports.sqlQuery(sqlCommand, function(error, output) { exports.sqlQuery(ctx, sqlCommand, function(error, output) {
if (error && '42883' == error.code) { if (error && '42883' == error.code) {
isSupported = false; isSupported = false;
logger.warn('postgresql does not support UNNEST'); ctx.logger.warn('postgresql does not support UNNEST');
} }
if (error) { if (error) {
callback(error, output, isSupported); callback(error, output, isSupported);
} else { } else {
exports.insertChanges(tableChanges, i, objChanges, docId, index, user, callback); exports.insertChanges(ctx, tableChanges, i, objChanges, docId, index, user, callback);
} }
}, undefined, undefined, values); }, undefined, undefined, values);
return; return;
} }
tenant.push(ctx.tenant);
id.push(docId); id.push(docId);
changeId.push(index++); changeId.push(index++);
userId.push(user.id); userId.push(user.id);
@ -210,10 +211,10 @@ exports.insertChanges = function(tableChanges, startIndex, objChanges, docId, in
change.push(objChanges[i].change); change.push(objChanges[i].change);
time.push(objChanges[i].time); time.push(objChanges[i].time);
} }
exports.sqlQuery(sqlCommand, function(error, output) { exports.sqlQuery(ctx, sqlCommand, function(error, output) {
if (error && '42883' == error.code) { if (error && '42883' == error.code) {
isSupported = false; isSupported = false;
logger.warn('postgresql does not support UNNEST'); ctx.logger.warn('postgresql does not support UNNEST');
} }
callback(error, output, isSupported); callback(error, output, isSupported);
}, undefined, undefined, values); }, undefined, undefined, values);

View File

@ -40,7 +40,6 @@ const commonDefines = require('./../../Common/sources/commondefines');
var utils = require('./../../Common/sources/utils'); var utils = require('./../../Common/sources/utils');
var rabbitMQCore = require('./../../Common/sources/rabbitMQCore'); var rabbitMQCore = require('./../../Common/sources/rabbitMQCore');
var activeMQCore = require('./../../Common/sources/activeMQCore'); var activeMQCore = require('./../../Common/sources/activeMQCore');
const logger = require('./../../Common/sources/logger');
const cfgQueueType = config.get('queue.type'); const cfgQueueType = config.get('queue.type');
var cfgRabbitExchangePubSub = config.get('rabbitmq.exchangepubsub'); var cfgRabbitExchangePubSub = config.get('rabbitmq.exchangepubsub');

View File

@ -56,6 +56,8 @@ const wopiClient = require('./wopiClient');
const constants = require('./../../Common/sources/constants'); const constants = require('./../../Common/sources/constants');
const utils = require('./../../Common/sources/utils'); const utils = require('./../../Common/sources/utils');
const commonDefines = require('./../../Common/sources/commondefines'); const commonDefines = require('./../../Common/sources/commondefines');
const operationContext = require('./../../Common/sources/operationContext');
const tenantManager = require('./../../Common/sources/tenantManager');
const configStorage = configCommon.get('storage'); const configStorage = configCommon.get('storage');
const cfgWopiEnable = configCommon.get('wopi.enable'); const cfgWopiEnable = configCommon.get('wopi.enable');
@ -63,6 +65,7 @@ const cfgHtmlTemplate = configCommon.get('wopi.htmlTemplate');
const cfgTokenEnableBrowser = configCommon.get('services.CoAuthoring.token.enable.browser'); const cfgTokenEnableBrowser = configCommon.get('services.CoAuthoring.token.enable.browser');
const cfgTokenEnableRequestInbox = configCommon.get('services.CoAuthoring.token.enable.request.inbox'); const cfgTokenEnableRequestInbox = configCommon.get('services.CoAuthoring.token.enable.request.inbox');
const cfgTokenEnableRequestOutbox = configCommon.get('services.CoAuthoring.token.enable.request.outbox'); const cfgTokenEnableRequestOutbox = configCommon.get('services.CoAuthoring.token.enable.request.outbox');
const cfgLicenseFile = configCommon.get('license.license_file');
const app = express(); const app = express();
app.disable('x-powered-by'); app.disable('x-powered-by');
@ -74,43 +77,49 @@ const server = http.createServer(app);
let licenseInfo, licenseOriginal, updatePluginsTime, userPlugins, pluginsLoaded; let licenseInfo, licenseOriginal, updatePluginsTime, userPlugins, pluginsLoaded;
const updatePlugins = (eventType, filename) => { const updatePlugins = (eventType, filename) => {
console.log('update Folder: %s ; %s', eventType, filename); operationContext.global.logger.info('update Folder: %s ; %s', eventType, filename);
if (updatePluginsTime && 1000 >= (new Date() - updatePluginsTime)) { if (updatePluginsTime && 1000 >= (new Date() - updatePluginsTime)) {
return; return;
} }
console.log('update Folder true: %s ; %s', eventType, filename); operationContext.global.logger.info('update Folder true: %s ; %s', eventType, filename);
updatePluginsTime = new Date(); updatePluginsTime = new Date();
pluginsLoaded = false; pluginsLoaded = false;
}; };
const readLicense = function*() { const readLicense = function*() {
[licenseInfo, licenseOriginal] = yield* license.readLicense(); [licenseInfo, licenseOriginal] = yield* license.readLicense(cfgLicenseFile);
}; };
const updateLicense = () => { const updateLicense = () => {
return co(function*() { return co(function*() {
try { try {
yield* readLicense(); yield* readLicense();
docsCoServer.setLicenseInfo(licenseInfo, licenseOriginal); docsCoServer.setLicenseInfo(licenseInfo, licenseOriginal);
console.log('End updateLicense'); operationContext.global.logger.info('End updateLicense');
} catch (err) { } catch (err) {
logger.error('updateLicense error:\r\n%s', err.stack); operationContext.global.logger.error('updateLicense error: %s', err.stack);
} }
}); });
}; };
logger.warn('Express server starting...'); operationContext.global.logger.warn('Express server starting...');
if (!(cfgTokenEnableBrowser && cfgTokenEnableRequestInbox && cfgTokenEnableRequestOutbox)) { if (!(cfgTokenEnableBrowser && cfgTokenEnableRequestInbox && cfgTokenEnableRequestOutbox)) {
logger.warn('Set services.CoAuthoring.token.enable.browser, services.CoAuthoring.token.enable.request.inbox, ' + operationContext.global.logger.warn('Set services.CoAuthoring.token.enable.browser, services.CoAuthoring.token.enable.request.inbox, ' +
'services.CoAuthoring.token.enable.request.outbox in the Document Server config ' + 'services.CoAuthoring.token.enable.request.outbox in the Document Server config ' +
'to prevent an unauthorized access to your documents and the substitution of important parameters in ONLYOFFICE Document Server requests.'); 'to prevent an unauthorized access to your documents and the substitution of important parameters in ONLYOFFICE Document Server requests.');
} }
updateLicense(); if (!tenantManager.isMultitenantMode()) {
updateLicense();
fs.watchFile(cfgLicenseFile, updateLicense);
setInterval(updateLicense, 86400000);
}
if (config.has('server.static_content')) { if (config.has('server.static_content')) {
const staticContent = config.get('server.static_content'); const staticContent = config.get('server.static_content');
for (let i in staticContent) { for (let i in staticContent) {
app.use(i, express.static(staticContent[i]['path'], staticContent[i]['options'])); if (staticContent.hasOwnProperty(i)) {
app.use(i, express.static(staticContent[i]['path'], staticContent[i]['options']));
}
} }
} }
@ -133,7 +142,7 @@ if (configStorage.has('fs.folderPath')) {
const realUrl = req.url.substring(0, index); const realUrl = req.url.substring(0, index);
res.sendFile(realUrl, sendFileOptions, (err) => { res.sendFile(realUrl, sendFileOptions, (err) => {
if (err) { if (err) {
logger.error(err); operationContext.global.logger.error(err);
res.status(400).end(); res.status(400).end();
} }
}); });
@ -146,33 +155,36 @@ if (configStorage.has('fs.folderPath')) {
try { try {
fs.watch(config.get('plugins.path'), updatePlugins); fs.watch(config.get('plugins.path'), updatePlugins);
} catch (e) { } catch (e) {
logger.warn('Failed to subscribe to plugin folder updates. When changing the list of plugins, you must restart the server. https://nodejs.org/docs/latest/api/fs.html#fs_availability'); operationContext.global.logger.warn('Failed to subscribe to plugin folder updates. When changing the list of plugins, you must restart the server. https://nodejs.org/docs/latest/api/fs.html#fs_availability');
} }
fs.watchFile(configCommon.get('license').get('license_file'), updateLicense);
setInterval(updateLicense, 86400000);
// Если захочется использовать 'development' и 'production', // Если захочется использовать 'development' и 'production',
// то с помощью app.settings.env (https://github.com/strongloop/express/issues/936) // то с помощью app.settings.env (https://github.com/strongloop/express/issues/936)
// Если нужна обработка ошибок, то теперь она такая https://github.com/expressjs/errorhandler // Если нужна обработка ошибок, то теперь она такая https://github.com/expressjs/errorhandler
docsCoServer.install(server, () => { docsCoServer.install(server, () => {
console.log('Start callbackFunction'); operationContext.global.logger.info('Start callbackFunction');
server.listen(config.get('server.port'), () => { server.listen(config.get('server.port'), () => {
logger.warn("Express server listening on port %d in %s mode. Version: %s. Build: %s", config.get('server.port'), app.settings.env, commonDefines.buildVersion, commonDefines.buildNumber); operationContext.global.logger.warn("Express server listening on port %d in %s mode. Version: %s. Build: %s", config.get('server.port'), app.settings.env, commonDefines.buildVersion, commonDefines.buildNumber);
}); });
app.get('/index.html', (req, res) => { app.get('/index.html', (req, res) => {
let buildVersion = commonDefines.buildVersion; return co(function*() {
let buildNumber = commonDefines.buildNumber; let ctx = new operationContext.Context();
let buildDate, packageType, customerId = ""; ctx.initFromRequest(req);
if (licenseInfo) { let licenseInfo = yield tenantManager.getTenantLicense(ctx);
buildDate = licenseInfo.buildDate.toISOString(); let buildVersion = commonDefines.buildVersion;
packageType = licenseInfo.packageType; let buildNumber = commonDefines.buildNumber;
customerId = licenseInfo.customerId; let buildDate, packageType, customerId = "";
} if (licenseInfo) {
let output = `Server is functioning normally. Version: ${buildVersion}. Build: ${buildNumber}`; buildDate = licenseInfo.buildDate.toISOString();
output += `. Release date: ${buildDate}. Package type: ${packageType}. Customer Id: ${customerId}`; packageType = licenseInfo.packageType;
res.send(output); customerId = licenseInfo.customerId;
}
let output = `Server is functioning normally. Version: ${buildVersion}. Build: ${buildNumber}`;
output += `. Release date: ${buildDate}. Package type: ${packageType}. Customer Id: ${customerId}`;
res.send(output);
});
}); });
const rawFileParser = bodyParser.raw( const rawFileParser = bodyParser.raw(
{inflate: true, limit: config.get('server.limits_tempfile_upload'), type: function() {return true;}}); {inflate: true, limit: config.get('server.limits_tempfile_upload'), type: function() {return true;}});
@ -237,7 +249,9 @@ docsCoServer.install(server, () => {
} }
app.post('/dummyCallback', utils.checkClientIp, rawFileParser, function(req, res){ app.post('/dummyCallback', utils.checkClientIp, rawFileParser, function(req, res){
logger.debug(`dummyCallback req.body:%s`, req.body); let ctx = new operationContext.Context();
ctx.initFromRequest(req);
ctx.logger.debug(`dummyCallback req.body:%s`, req.body);
utils.fillResponseSimple(res, JSON.stringify({error: 0}, "application/json")); utils.fillResponseSimple(res, JSON.stringify({error: 0}, "application/json"));
}); });
@ -292,8 +306,10 @@ docsCoServer.install(server, () => {
app.get('/themes.json', apicache.middleware("5 minutes"), (req, res) => { app.get('/themes.json', apicache.middleware("5 minutes"), (req, res) => {
return co(function*() { return co(function*() {
let themes = []; let themes = [];
let ctx = new operationContext.Context();
try { try {
logger.info('themes.json start'); ctx.initFromRequest(req);
ctx.logger.info('themes.json start');
if (!config.has('server.static_content') || !config.has('themes.uri')) { if (!config.has('server.static_content') || !config.has('themes.uri')) {
return; return;
} }
@ -305,8 +321,8 @@ docsCoServer.install(server, () => {
if (staticContent.hasOwnProperty(i) && themesUri.startsWith(i)) { if (staticContent.hasOwnProperty(i) && themesUri.startsWith(i)) {
let dir = staticContent[i].path + themesUri.substring(i.length); let dir = staticContent[i].path + themesUri.substring(i.length);
themesList = yield utils.listObjects(dir, true); themesList = yield utils.listObjects(dir, true);
logger.debug('themes.json dir:%s', dir); ctx.logger.debug('themes.json dir:%s', dir);
logger.debug('themes.json themesList:%j', themesList); ctx.logger.debug('themes.json themesList:%j', themesList);
for (let j = 0; j < themesList.length; ++j) { for (let j = 0; j < themesList.length; ++j) {
if (themesList[j].endsWith('.json')) { if (themesList[j].endsWith('.json')) {
let data = yield utils.readFile(themesList[j], true); let data = yield utils.readFile(themesList[j], true);
@ -317,7 +333,7 @@ docsCoServer.install(server, () => {
} }
} }
} catch (err) { } catch (err) {
logger.error('themes.json error:%s', err.stack); ctx.logger.error('themes.json error:%s', err.stack);
} finally { } finally {
if (themes.length > 0) { if (themes.length > 0) {
res.setHeader('Content-Type', 'application/json'); res.setHeader('Content-Type', 'application/json');
@ -325,15 +341,15 @@ docsCoServer.install(server, () => {
} else { } else {
res.sendStatus(404); res.sendStatus(404);
} }
logger.info('themes.json end'); ctx.logger.info('themes.json end');
} }
}); });
}); });
}); });
process.on('uncaughtException', (err) => { process.on('uncaughtException', (err) => {
logger.error((new Date).toUTCString() + ' uncaughtException:', err.message); operationContext.global.logger.error((new Date).toUTCString() + ' uncaughtException:', err.message);
logger.error(err.stack); operationContext.global.logger.error(err.stack);
logger.shutdown(() => { logger.shutdown(() => {
process.exit(1); process.exit(1);
}); });

View File

@ -47,11 +47,11 @@ var WAIT_TIMEOUT = 30000;
var LOOP_TIMEOUT = 1000; var LOOP_TIMEOUT = 1000;
var EXEC_TIMEOUT = WAIT_TIMEOUT + utils.CONVERTION_TIMEOUT; var EXEC_TIMEOUT = WAIT_TIMEOUT + utils.CONVERTION_TIMEOUT;
exports.shutdown = function(editorData, status) { exports.shutdown = function(ctx, editorData, status) {
return co(function*() { return co(function*() {
var res = true; var res = true;
try { try {
logger.debug('shutdown start:' + EXEC_TIMEOUT); ctx.logger.debug('shutdown start:' + EXEC_TIMEOUT);
//redisKeyShutdown не простой счетчик, чтобы его не уменьшала сборка, которая началась перед запуском Shutdown //redisKeyShutdown не простой счетчик, чтобы его не уменьшала сборка, которая началась перед запуском Shutdown
//сбрасываем redisKeyShutdown на всякий случай, если предыдущий запуск не дошел до конца //сбрасываем redisKeyShutdown на всякий случай, если предыдущий запуск не дошел до конца
@ -60,24 +60,24 @@ exports.shutdown = function(editorData, status) {
var pubsub = new pubsubService(); var pubsub = new pubsubService();
yield pubsub.initPromise(); yield pubsub.initPromise();
//inner ping to update presence //inner ping to update presence
logger.debug('shutdown pubsub shutdown message'); ctx.logger.debug('shutdown pubsub shutdown message');
pubsub.publish(JSON.stringify({type: commonDefines.c_oPublishType.shutdown, status: status})); pubsub.publish(JSON.stringify({type: commonDefines.c_oPublishType.shutdown, ctx: ctx, status: status}));
//wait while pubsub deliver and start conversion //wait while pubsub deliver and start conversion
logger.debug('shutdown start wait pubsub deliver'); ctx.logger.debug('shutdown start wait pubsub deliver');
var startTime = new Date().getTime(); var startTime = new Date().getTime();
var isStartWait = true; var isStartWait = true;
while (true) { while (true) {
var curTime = new Date().getTime() - startTime; var curTime = new Date().getTime() - startTime;
if (isStartWait && curTime >= WAIT_TIMEOUT) { if (isStartWait && curTime >= WAIT_TIMEOUT) {
isStartWait = false; isStartWait = false;
logger.debug('shutdown stop wait pubsub deliver'); ctx.logger.debug('shutdown stop wait pubsub deliver');
} else if (curTime >= EXEC_TIMEOUT) { } else if (curTime >= EXEC_TIMEOUT) {
res = false; res = false;
logger.debug('shutdown timeout'); ctx.logger.debug('shutdown timeout');
break; break;
} }
var remainingFiles = yield editorData.getShutdownCount(redisKeyShutdown); var remainingFiles = yield editorData.getShutdownCount(redisKeyShutdown);
logger.debug('shutdown remaining files:%d', remainingFiles); ctx.logger.debug('shutdown remaining files:%d', remainingFiles);
if (!isStartWait && remainingFiles <= 0) { if (!isStartWait && remainingFiles <= 0) {
break; break;
} }
@ -88,10 +88,10 @@ exports.shutdown = function(editorData, status) {
yield editorData.cleanupShutdown(redisKeyShutdown); yield editorData.cleanupShutdown(redisKeyShutdown);
yield pubsub.close(); yield pubsub.close();
logger.debug('shutdown end'); ctx.logger.debug('shutdown end');
} catch (e) { } catch (e) {
res = false; res = false;
logger.error('shutdown error:\r\n%s', e.stack); ctx.logger.error('shutdown error: %s', e.stack);
} }
return res; return res;
}); });

View File

@ -34,9 +34,9 @@
const crypto = require('crypto'); const crypto = require('crypto');
var sqlBase = require('./baseConnector'); var sqlBase = require('./baseConnector');
var logger = require('./../../Common/sources/logger');
var utils = require('./../../Common/sources/utils'); var utils = require('./../../Common/sources/utils');
var constants = require('./../../Common/sources/constants'); var constants = require('./../../Common/sources/constants');
var tenantManager = require('./../../Common/sources/tenantManager');
let addSqlParam = sqlBase.baseConnector.addSqlParameter; let addSqlParam = sqlBase.baseConnector.addSqlParameter;
let concatParams = sqlBase.baseConnector.concatParams; let concatParams = sqlBase.baseConnector.concatParams;
@ -56,6 +56,7 @@ var FileStatus = {
}; };
function TaskResultData() { function TaskResultData() {
this.tenant = null;
this.key = null; this.key = null;
this.status = null; this.status = null;
this.statusInfo = null; this.statusInfo = null;
@ -71,6 +72,9 @@ function TaskResultData() {
this.innerPasswordChange = null;//not a DB field this.innerPasswordChange = null;//not a DB field
} }
TaskResultData.prototype.completeDefaults = function() { TaskResultData.prototype.completeDefaults = function() {
if (!this.tenant) {
this.tenant = tenantManager.getDefautTenant();
}
if (!this.key) { if (!this.key) {
this.key = ''; this.key = '';
} }
@ -100,16 +104,17 @@ TaskResultData.prototype.completeDefaults = function() {
} }
}; };
function upsert(task, opt_updateUserIndex) { function upsert(ctx, task, opt_updateUserIndex) {
return sqlBase.baseConnector.upsert(task, opt_updateUserIndex); return sqlBase.baseConnector.upsert(ctx, task, opt_updateUserIndex);
} }
function select(docId) { function select(ctx, docId) {
return new Promise(function(resolve, reject) { return new Promise(function(resolve, reject) {
let values = []; let values = [];
let sqlParam = addSqlParam(docId, values); let p1 = addSqlParam(ctx.tenant, values);
let sqlCommand = `SELECT * FROM task_result WHERE id=${sqlParam};`; let p2 = addSqlParam(docId, values);
sqlBase.baseConnector.sqlQuery(sqlCommand, function(error, result) { let sqlCommand = `SELECT * FROM task_result WHERE tenant=${p1} AND id=${p2};`;
sqlBase.baseConnector.sqlQuery(ctx, sqlCommand, function(error, result) {
if (error) { if (error) {
reject(error); reject(error);
} else { } else {
@ -170,14 +175,15 @@ function toUpdateArray(task, updateTime, isMask, values, setPassword) {
return res; return res;
} }
function update(task, setPassword) { function update(ctx, task, setPassword) {
return new Promise(function(resolve, reject) { return new Promise(function(resolve, reject) {
let values = []; let values = [];
let updateElems = toUpdateArray(task, true, false, values, setPassword); let updateElems = toUpdateArray(task, true, false, values, setPassword);
let sqlSet = updateElems.join(', '); let sqlSet = updateElems.join(', ');
let sqlParam = addSqlParam(task.key, values); let p1 = addSqlParam(task.tenant, values);
let sqlCommand = `UPDATE task_result SET ${sqlSet} WHERE id=${sqlParam};`; let p2 = addSqlParam(task.key, values);
sqlBase.baseConnector.sqlQuery(sqlCommand, function(error, result) { let sqlCommand = `UPDATE task_result SET ${sqlSet} WHERE tenant=${p1} AND id=${p2};`;
sqlBase.baseConnector.sqlQuery(ctx, sqlCommand, function(error, result) {
if (error) { if (error) {
reject(error); reject(error);
} else { } else {
@ -187,16 +193,17 @@ function update(task, setPassword) {
}); });
} }
function updateIf(task, mask) { function updateIf(ctx, task, mask) {
return new Promise(function(resolve, reject) { return new Promise(function(resolve, reject) {
let values = []; let values = [];
let commandArg = toUpdateArray(task, true, false, values, false); let commandArg = toUpdateArray(task, true, false, values, false);
let commandArgMask = toUpdateArray(mask, false, true, values, false); let commandArgMask = toUpdateArray(mask, false, true, values, false);
commandArgMask.push('tenant=' + addSqlParam(mask.tenant, values));
commandArgMask.push('id=' + addSqlParam(mask.key, values)); commandArgMask.push('id=' + addSqlParam(mask.key, values));
let sqlSet = commandArg.join(', '); let sqlSet = commandArg.join(', ');
let sqlWhere = commandArgMask.join(' AND '); let sqlWhere = commandArgMask.join(' AND ');
let sqlCommand = `UPDATE task_result SET ${sqlSet} WHERE ${sqlWhere};`; let sqlCommand = `UPDATE task_result SET ${sqlSet} WHERE ${sqlWhere};`;
sqlBase.baseConnector.sqlQuery(sqlCommand, function(error, result) { sqlBase.baseConnector.sqlQuery(ctx, sqlCommand, function(error, result) {
if (error) { if (error) {
reject(error); reject(error);
} else { } else {
@ -205,28 +212,30 @@ function updateIf(task, mask) {
}, undefined, undefined, values); }, undefined, undefined, values);
}); });
} }
function restoreInitialPassword(docId) { function restoreInitialPassword(ctx, docId) {
return select(docId).then(function(selectRes) { return select(ctx, docId).then(function(selectRes) {
if (selectRes.length > 0) { if (selectRes.length > 0) {
var row = selectRes[0]; var row = selectRes[0];
let docPassword = sqlBase.DocumentPassword.prototype.getDocPassword(docId, row.password); let docPassword = sqlBase.DocumentPassword.prototype.getDocPassword(ctx, row.password);
var updateTask = new TaskResultData(); var updateTask = new TaskResultData();
updateTask.tenant = ctx.tenant;
updateTask.key = docId; updateTask.key = docId;
if (docPassword.initial) { if (docPassword.initial) {
var documentPassword = new sqlBase.DocumentPassword(); var documentPassword = new sqlBase.DocumentPassword();
documentPassword.fromValues(docPassword.initial); documentPassword.fromValues(docPassword.initial);
updateTask.password = documentPassword.toSQLInsert(); updateTask.password = documentPassword.toSQLInsert();
return update(updateTask, true); return update(ctx, updateTask, true);
} else if (docPassword.current) { } else if (docPassword.current) {
updateTask.password = null; updateTask.password = null;
return update(updateTask, true); return update(ctx, updateTask, true);
} }
} }
}); });
} }
function addRandomKey(task, opt_prefix, opt_size) { function addRandomKey(ctx, task, opt_prefix, opt_size) {
return new Promise(function(resolve, reject) { return new Promise(function(resolve, reject) {
task.tenant = ctx.tenant;
if (undefined !== opt_prefix && undefined !== opt_size) { if (undefined !== opt_prefix && undefined !== opt_size) {
task.key = opt_prefix + crypto.randomBytes(opt_size).toString("hex"); task.key = opt_prefix + crypto.randomBytes(opt_size).toString("hex");
} else { } else {
@ -234,6 +243,7 @@ function addRandomKey(task, opt_prefix, opt_size) {
} }
task.completeDefaults(); task.completeDefaults();
let values = []; let values = [];
let p0 = addSqlParam(task.tenant, values);
let p1 = addSqlParam(task.key, values); let p1 = addSqlParam(task.key, values);
let p2 = addSqlParam(task.status, values); let p2 = addSqlParam(task.status, values);
let p3 = addSqlParam(task.statusInfo, values); let p3 = addSqlParam(task.statusInfo, values);
@ -242,9 +252,9 @@ function addRandomKey(task, opt_prefix, opt_size) {
let p6 = addSqlParam(task.changeId, values); let p6 = addSqlParam(task.changeId, values);
let p7 = addSqlParam(task.callback, values); let p7 = addSqlParam(task.callback, values);
let p8 = addSqlParam(task.baseurl, values); let p8 = addSqlParam(task.baseurl, values);
let sqlCommand = 'INSERT INTO task_result (id, status, status_info, last_open_date, user_index, change_id, callback, baseurl)' + let sqlCommand = 'INSERT INTO task_result (tenant, id, status, status_info, last_open_date, user_index, change_id, callback, baseurl)' +
` VALUES (${p1}, ${p2}, ${p3}, ${p4}, ${p5}, ${p6}, ${p7}, ${p8});`; ` VALUES (${p0}, ${p1}, ${p2}, ${p3}, ${p4}, ${p5}, ${p6}, ${p7}, ${p8});`;
sqlBase.baseConnector.sqlQuery(sqlCommand, function(error, result) { sqlBase.baseConnector.sqlQuery(ctx, sqlCommand, function(error, result) {
if (error) { if (error) {
reject(error); reject(error);
} else { } else {
@ -253,8 +263,9 @@ function addRandomKey(task, opt_prefix, opt_size) {
}, undefined, undefined, values); }, undefined, undefined, values);
}); });
} }
function* addRandomKeyTask(key, opt_prefix, opt_size) { function* addRandomKeyTask(ctx, key, opt_prefix, opt_size) {
var task = new TaskResultData(); var task = new TaskResultData();
task.tenant = ctx.tenant;
task.key = key; task.key = key;
task.status = FileStatus.WaitQueue; task.status = FileStatus.WaitQueue;
//nTryCount чтобы не зависнуть если реально будут проблемы с DB //nTryCount чтобы не зависнуть если реально будут проблемы с DB
@ -262,7 +273,7 @@ function* addRandomKeyTask(key, opt_prefix, opt_size) {
var addRes = null; var addRes = null;
while (nTryCount-- > 0) { while (nTryCount-- > 0) {
try { try {
addRes = yield addRandomKey(task, opt_prefix, opt_size); addRes = yield addRandomKey(ctx, task, opt_prefix, opt_size);
} catch (e) { } catch (e) {
addRes = null; addRes = null;
//key exist, try again //key exist, try again
@ -278,12 +289,13 @@ function* addRandomKeyTask(key, opt_prefix, opt_size) {
} }
} }
function remove(docId) { function remove(ctx, docId) {
return new Promise(function(resolve, reject) { return new Promise(function(resolve, reject) {
let values = []; let values = [];
let sqlParam = addSqlParam(docId, values); let p1 = addSqlParam(ctx.tenant, values);
const sqlCommand = `DELETE FROM task_result WHERE id=${sqlParam};`; let p2 = addSqlParam(docId, values);
sqlBase.baseConnector.sqlQuery(sqlCommand, function(error, result) { const sqlCommand = `DELETE FROM task_result WHERE tenant=${p1} AND id=${p2};`;
sqlBase.baseConnector.sqlQuery(ctx, sqlCommand, function(error, result) {
if (error) { if (error) {
reject(error); reject(error);
} else { } else {
@ -292,14 +304,15 @@ function remove(docId) {
}, undefined, undefined, values); }, undefined, undefined, values);
}); });
} }
function removeIf(mask) { function removeIf(ctx, mask) {
return new Promise(function(resolve, reject) { return new Promise(function(resolve, reject) {
let values = []; let values = [];
let commandArgMask = toUpdateArray(mask, false, true, values, false); let commandArgMask = toUpdateArray(mask, false, true, values, false);
commandArgMask.push('tenant=' + addSqlParam(mask.tenant, values));
commandArgMask.push('id=' + addSqlParam(mask.key, values)); commandArgMask.push('id=' + addSqlParam(mask.key, values));
let sqlWhere = commandArgMask.join(' AND '); let sqlWhere = commandArgMask.join(' AND ');
const sqlCommand = `DELETE FROM task_result WHERE ${sqlWhere};`; const sqlCommand = `DELETE FROM task_result WHERE ${sqlWhere};`;
sqlBase.baseConnector.sqlQuery(sqlCommand, function(error, result) { sqlBase.baseConnector.sqlQuery(ctx, sqlCommand, function(error, result) {
if (error) { if (error) {
reject(error); reject(error);
} else { } else {
@ -308,7 +321,7 @@ function removeIf(mask) {
}, undefined, undefined, values); }, undefined, undefined, values);
}); });
} }
function getExpired(maxCount, expireSeconds) { function getExpired(ctx, maxCount, expireSeconds) {
return new Promise(function(resolve, reject) { return new Promise(function(resolve, reject) {
let values = []; let values = [];
let expireDate = new Date(); let expireDate = new Date();
@ -316,8 +329,8 @@ function getExpired(maxCount, expireSeconds) {
let sqlParam1 = addSqlParam(expireDate, values); let sqlParam1 = addSqlParam(expireDate, values);
let sqlParam2 = addSqlParam(maxCount, values); let sqlParam2 = addSqlParam(maxCount, values);
let sqlCommand = `SELECT * FROM task_result WHERE last_open_date <= ${sqlParam1}` + let sqlCommand = `SELECT * FROM task_result WHERE last_open_date <= ${sqlParam1}` +
` AND NOT EXISTS(SELECT id FROM doc_changes WHERE doc_changes.id = task_result.id LIMIT 1) LIMIT ${sqlParam2};`; ` AND NOT EXISTS(SELECT tenant, id FROM doc_changes WHERE doc_changes.tenant = task_result.tenant AND doc_changes.id = task_result.id LIMIT 1) LIMIT ${sqlParam2};`;
sqlBase.baseConnector.sqlQuery(sqlCommand, function(error, result) { sqlBase.baseConnector.sqlQuery(ctx, sqlCommand, function(error, result) {
if (error) { if (error) {
reject(error); reject(error);
} else { } else {

View File

@ -44,13 +44,14 @@ const logger = require('./../../Common/sources/logger');
const utils = require('./../../Common/sources/utils'); const utils = require('./../../Common/sources/utils');
const constants = require('./../../Common/sources/constants'); const constants = require('./../../Common/sources/constants');
const commonDefines = require('./../../Common/sources/commondefines'); const commonDefines = require('./../../Common/sources/commondefines');
const operationContext = require('./../../Common/sources/operationContext');
const tenantManager = require('./../../Common/sources/tenantManager');
const sqlBase = require('./baseConnector'); const sqlBase = require('./baseConnector');
const taskResult = require('./taskresult'); const taskResult = require('./taskresult');
const canvasService = require('./canvasservice'); const canvasService = require('./canvasservice');
const cfgTokenOutboxAlgorithm = config.get('services.CoAuthoring.token.outbox.algorithm'); const cfgTokenOutboxAlgorithm = config.get('services.CoAuthoring.token.outbox.algorithm');
const cfgTokenOutboxExpires = config.get('services.CoAuthoring.token.outbox.expires'); const cfgTokenOutboxExpires = config.get('services.CoAuthoring.token.outbox.expires');
const cfgSignatureSecretOutbox = config.get('services.CoAuthoring.secret.outbox');
const cfgTokenEnableBrowser = config.get('services.CoAuthoring.token.enable.browser'); const cfgTokenEnableBrowser = config.get('services.CoAuthoring.token.enable.browser');
const cfgCallbackRequestTimeout = config.get('services.CoAuthoring.server.callbackRequestTimeout'); const cfgCallbackRequestTimeout = config.get('services.CoAuthoring.server.callbackRequestTimeout');
const cfgDownloadTimeout = config.get('FileConverter.converter.downloadTimeout'); const cfgDownloadTimeout = config.get('FileConverter.converter.downloadTimeout');
@ -97,8 +98,10 @@ let mimeTypesByExt = (function() {
function discovery(req, res) { function discovery(req, res) {
return co(function*() { return co(function*() {
let output = ''; let output = '';
let ctx = new operationContext.Context();
try { try {
logger.info('wopiDiscovery start'); ctx.initFromRequest(req);
ctx.logger.info('wopiDiscovery start');
let baseUrl = cfgWopiHost || utils.getBaseUrlByRequest(req); let baseUrl = cfgWopiHost || utils.getBaseUrlByRequest(req);
let names = ['Word','Excel','PowerPoint']; let names = ['Word','Excel','PowerPoint'];
let favIconUrls = [cfgWopiFavIconUrlWord, cfgWopiFavIconUrlCell, cfgWopiFavIconUrlSlide]; let favIconUrls = [cfgWopiFavIconUrlWord, cfgWopiFavIconUrlCell, cfgWopiFavIconUrlSlide];
@ -179,11 +182,11 @@ function discovery(req, res) {
} }
output += `</net-zone>${proofKey}</wopi-discovery>`; output += `</net-zone>${proofKey}</wopi-discovery>`;
} catch (err) { } catch (err) {
logger.error('wopiDiscovery error:%s', err.stack); ctx.logger.error('wopiDiscovery error:%s', err.stack);
} finally { } finally {
res.setHeader('Content-Type', 'text/xml'); res.setHeader('Content-Type', 'text/xml');
res.send(output); res.send(output);
logger.info('wopiDiscovery end'); ctx.logger.info('wopiDiscovery end');
} }
}); });
} }
@ -193,13 +196,15 @@ function collaboraCapabilities(req, res) {
"convert-to": {"available": false}, "hasMobileSupport": true, "hasProxyPrefix": false, "hasTemplateSaveAs": false, "convert-to": {"available": false}, "hasMobileSupport": true, "hasProxyPrefix": false, "hasTemplateSaveAs": false,
"hasTemplateSource": true, "productVersion": commonDefines.buildVersion "hasTemplateSource": true, "productVersion": commonDefines.buildVersion
}; };
let ctx = new operationContext.Context();
try { try {
logger.info('collaboraCapabilities start'); ctx.initFromRequest(req);
ctx.logger.info('collaboraCapabilities start');
} catch (err) { } catch (err) {
logger.error('collaboraCapabilities error:%s', err.stack); ctx.logger.error('collaboraCapabilities error:%s', err.stack);
} finally { } finally {
utils.fillResponseSimple(res, JSON.stringify(output), "application/json"); utils.fillResponseSimple(res, JSON.stringify(output), "application/json");
logger.info('collaboraCapabilities end'); ctx.logger.info('collaboraCapabilities end');
} }
}); });
} }
@ -233,7 +238,7 @@ function getLastModifiedTimeFromCallbacks(callbacks) {
function isCorrectUserAuth(userAuth) { function isCorrectUserAuth(userAuth) {
return undefined !== userAuth.wopiSrc; return undefined !== userAuth.wopiSrc;
} }
function parseWopiCallback(docId, userAuthStr, opt_url) { function parseWopiCallback(ctx, userAuthStr, opt_url) {
let wopiParams = null; let wopiParams = null;
if (isWopiCallback(userAuthStr)) { if (isWopiCallback(userAuthStr)) {
let userAuth = JSON.parse(userAuthStr); let userAuth = JSON.parse(userAuthStr);
@ -243,58 +248,59 @@ function parseWopiCallback(docId, userAuthStr, opt_url) {
let commonInfo = null; let commonInfo = null;
let lastModifiedTime = null; let lastModifiedTime = null;
if (opt_url) { if (opt_url) {
let commonInfoStr = sqlBase.UserCallback.prototype.getCallbackByUserIndex(docId, opt_url, 1); let commonInfoStr = sqlBase.UserCallback.prototype.getCallbackByUserIndex(ctx, opt_url, 1);
if (isWopiCallback(commonInfoStr)) { if (isWopiCallback(commonInfoStr)) {
commonInfo = JSON.parse(commonInfoStr); commonInfo = JSON.parse(commonInfoStr);
lastModifiedTime = commonInfo.fileInfo.LastModifiedTime; lastModifiedTime = commonInfo.fileInfo.LastModifiedTime;
if (lastModifiedTime) { if (lastModifiedTime) {
let callbacks = sqlBase.UserCallback.prototype.getCallbacks(docId, opt_url); let callbacks = sqlBase.UserCallback.prototype.getCallbacks(ctx, opt_url);
lastModifiedTime = getLastModifiedTimeFromCallbacks(callbacks); lastModifiedTime = getLastModifiedTimeFromCallbacks(callbacks);
} }
} }
} }
wopiParams = {commonInfo: commonInfo, userAuth: userAuth, LastModifiedTime: lastModifiedTime}; wopiParams = {commonInfo: commonInfo, userAuth: userAuth, LastModifiedTime: lastModifiedTime};
logger.debug('parseWopiCallback wopiParams:%j', wopiParams); ctx.logger.debug('parseWopiCallback wopiParams:%j', wopiParams);
} }
return wopiParams; return wopiParams;
} }
function checkAndInvalidateCache(docId, fileInfo) { function checkAndInvalidateCache(ctx, docId, fileInfo) {
return co(function*() { return co(function*() {
let res = {success: true, lockId: undefined}; let res = {success: true, lockId: undefined};
let selectRes = yield taskResult.select(docId); let selectRes = yield taskResult.select(ctx, docId);
if (selectRes.length > 0) { if (selectRes.length > 0) {
let row = selectRes[0]; let row = selectRes[0];
if (row.callback) { if (row.callback) {
let commonInfoStr = sqlBase.UserCallback.prototype.getCallbackByUserIndex(docId, row.callback, 1); let commonInfoStr = sqlBase.UserCallback.prototype.getCallbackByUserIndex(ctx, row.callback, 1);
if (isWopiCallback(commonInfoStr)) { if (isWopiCallback(commonInfoStr)) {
let commonInfo = JSON.parse(commonInfoStr); let commonInfo = JSON.parse(commonInfoStr);
res.lockId = commonInfo.lockId; res.lockId = commonInfo.lockId;
logger.debug('wopiEditor lockId from DB lockId=%s', res.lockId); ctx.logger.debug('wopiEditor lockId from DB lockId=%s', res.lockId);
let unlockMarkStr = sqlBase.UserCallback.prototype.getCallbackByUserIndex(docId, row.callback); let unlockMarkStr = sqlBase.UserCallback.prototype.getCallbackByUserIndex(ctx, row.callback);
logger.debug('wopiEditor commonInfoStr=%s', commonInfoStr); ctx.logger.debug('wopiEditor commonInfoStr=%s', commonInfoStr);
logger.debug('wopiEditor unlockMarkStr=%s', unlockMarkStr); ctx.logger.debug('wopiEditor unlockMarkStr=%s', unlockMarkStr);
let hasUnlockMarker = isWopiUnlockMarker(unlockMarkStr); let hasUnlockMarker = isWopiUnlockMarker(unlockMarkStr);
logger.debug('wopiEditor hasUnlockMarker=%s', hasUnlockMarker); ctx.logger.debug('wopiEditor hasUnlockMarker=%s', hasUnlockMarker);
if (hasUnlockMarker) { if (hasUnlockMarker) {
let fileInfoVersion = fileInfo.Version; let fileInfoVersion = fileInfo.Version;
let cacheVersion = commonInfo.fileInfo.Version; let cacheVersion = commonInfo.fileInfo.Version;
let fileInfoModified = fileInfo.LastModifiedTime; let fileInfoModified = fileInfo.LastModifiedTime;
let cacheModified = commonInfo.fileInfo.LastModifiedTime; let cacheModified = commonInfo.fileInfo.LastModifiedTime;
logger.debug('wopiEditor version fileInfo=%s; cache=%s', fileInfoVersion, cacheVersion); ctx.logger.debug('wopiEditor version fileInfo=%s; cache=%s', fileInfoVersion, cacheVersion);
logger.debug('wopiEditor LastModifiedTime fileInfo=%s; cache=%s', fileInfoModified, cacheModified); ctx.logger.debug('wopiEditor LastModifiedTime fileInfo=%s; cache=%s', fileInfoModified, cacheModified);
if (fileInfoVersion !== cacheVersion || (fileInfoModified !== cacheModified)) { if (fileInfoVersion !== cacheVersion || (fileInfoModified !== cacheModified)) {
var mask = new taskResult.TaskResultData(); var mask = new taskResult.TaskResultData();
mask.tenant = ctx.tenant;
mask.key = docId; mask.key = docId;
mask.last_open_date = row.last_open_date; mask.last_open_date = row.last_open_date;
//cleanupRes can be false in case of simultaneous opening. it is OK //cleanupRes can be false in case of simultaneous opening. it is OK
let cleanupRes = yield canvasService.cleanupCacheIf(mask); let cleanupRes = yield canvasService.cleanupCacheIf(ctx, mask);
logger.debug('wopiEditor cleanupRes=%s', cleanupRes); ctx.logger.debug('wopiEditor cleanupRes=%s', cleanupRes);
res.lockId = undefined; res.lockId = undefined;
} }
} }
} else { } else {
res.success = false; res.success = false;
logger.warn('wopiEditor attempt to open not wopi record'); ctx.logger.warn('wopiEditor attempt to open not wopi record');
} }
} }
} }
@ -304,14 +310,19 @@ function checkAndInvalidateCache(docId, fileInfo) {
function getEditorHtml(req, res) { function getEditorHtml(req, res) {
return co(function*() { return co(function*() {
let params = {key: undefined, fileInfo: {}, userAuth: {}, queryParams: req.query, token: undefined, documentType: undefined}; let params = {key: undefined, fileInfo: {}, userAuth: {}, queryParams: req.query, token: undefined, documentType: undefined};
let ctx = new operationContext.Context();
try { try {
logger.info('wopiEditor start'); ctx.initFromRequest(req);
logger.debug(`wopiEditor req.url:%s`, req.url); let wopiSrc = req.query['wopisrc'];
logger.debug(`wopiEditor req.query:%j`, req.query); let fileId = wopiSrc.substring(wopiSrc.lastIndexOf('/') + 1);
logger.debug(`wopiEditor req.body:%j`, req.body); ctx.setDocId(fileId);
ctx.logger.info('wopiEditor start');
ctx.logger.debug(`wopiEditor req.url:%s`, req.url);
ctx.logger.debug(`wopiEditor req.query:%j`, req.query);
ctx.logger.debug(`wopiEditor req.body:%j`, req.body);
params.documentType = req.params.documentType; params.documentType = req.params.documentType;
let mode = req.params.mode; let mode = req.params.mode;
let wopiSrc = req.query['wopisrc'];
let sc = req.query['sc']; let sc = req.query['sc'];
let hostSessionId = req.query['hid']; let hostSessionId = req.query['hid'];
let access_token = req.body['access_token'] || ""; let access_token = req.body['access_token'] || "";
@ -319,7 +330,7 @@ function getEditorHtml(req, res) {
let uri = `${encodeURI(wopiSrc)}?access_token=${encodeURIComponent(access_token)}`; let uri = `${encodeURI(wopiSrc)}?access_token=${encodeURIComponent(access_token)}`;
let fileInfo = params.fileInfo = yield checkFileInfo(uri, access_token, sc); let fileInfo = params.fileInfo = yield checkFileInfo(ctx, uri, access_token, sc);
if (!fileInfo) { if (!fileInfo) {
params.fileInfo = {}; params.fileInfo = {};
return; return;
@ -330,7 +341,6 @@ function getEditorHtml(req, res) {
} }
//docId //docId
let docId = undefined; let docId = undefined;
let fileId = wopiSrc.substring(wopiSrc.lastIndexOf('/') + 1);
if ('view' !== mode) { if ('view' !== mode) {
docId = `${fileId}`; docId = `${fileId}`;
} else { } else {
@ -344,7 +354,7 @@ function getEditorHtml(req, res) {
} }
} }
docId = docId.replace(constants.DOC_ID_REPLACE_REGEX, '_').substring(0, constants.DOC_ID_MAX_LENGTH); docId = docId.replace(constants.DOC_ID_REPLACE_REGEX, '_').substring(0, constants.DOC_ID_MAX_LENGTH);
logger.debug(`wopiEditor docId=%s`, docId); ctx.logger.debug(`wopiEditor`);
params.key = docId; params.key = docId;
let userAuth = params.userAuth = { let userAuth = params.userAuth = {
wopiSrc: wopiSrc, access_token: access_token, access_token_ttl: access_token_ttl, wopiSrc: wopiSrc, access_token: access_token, access_token_ttl: access_token_ttl,
@ -352,7 +362,7 @@ function getEditorHtml(req, res) {
}; };
//check and invalidate cache //check and invalidate cache
let checkRes = yield checkAndInvalidateCache(docId, fileInfo); let checkRes = yield checkAndInvalidateCache(ctx, docId, fileInfo);
let lockId = checkRes.lockId; let lockId = checkRes.lockId;
if (!checkRes.success) { if (!checkRes.success) {
params.fileInfo = {}; params.fileInfo = {};
@ -364,12 +374,12 @@ function getEditorHtml(req, res) {
fileType = fileInfo.FileExtension ? fileInfo.FileExtension.substr(1) : fileType; fileType = fileInfo.FileExtension ? fileInfo.FileExtension.substr(1) : fileType;
lockId = crypto.randomBytes(16).toString('base64'); lockId = crypto.randomBytes(16).toString('base64');
let commonInfo = JSON.stringify({lockId: lockId, fileInfo: fileInfo}); let commonInfo = JSON.stringify({lockId: lockId, fileInfo: fileInfo});
yield canvasService.commandOpenStartPromise(docId, utils.getBaseUrlByRequest(req), 1, commonInfo, fileType); yield canvasService.commandOpenStartPromise(ctx, docId, utils.getBaseUrlByRequest(req), 1, commonInfo, fileType);
} }
//Lock //Lock
if ('view' !== mode) { if ('view' !== mode) {
let lockRes = yield lock('LOCK', lockId, fileInfo, userAuth); let lockRes = yield lock(ctx, 'LOCK', lockId, fileInfo, userAuth);
if (!lockRes) { if (!lockRes) {
params.fileInfo = {}; params.fileInfo = {};
return; return;
@ -384,36 +394,36 @@ function getEditorHtml(req, res) {
if (cfgTokenEnableBrowser) { if (cfgTokenEnableBrowser) {
let options = {algorithm: cfgTokenOutboxAlgorithm, expiresIn: cfgTokenOutboxExpires}; let options = {algorithm: cfgTokenOutboxAlgorithm, expiresIn: cfgTokenOutboxExpires};
let secret = utils.getSecretByElem(cfgSignatureSecretOutbox); let secret = yield tenantManager.getTenantSecret(ctx, commonDefines.c_oAscSecretType.Inbox);
params.token = jwt.sign(params, secret, options); params.token = jwt.sign(params, secret, options);
} }
} catch (err) { } catch (err) {
logger.error('wopiEditor error:%s', err.stack); ctx.logger.error('wopiEditor error:%s', err.stack);
params.fileInfo = {}; params.fileInfo = {};
} finally { } finally {
logger.debug('wopiEditor render params=%j', params); ctx.logger.debug('wopiEditor render params=%j', params);
try { try {
res.render("editor-wopi", params); res.render("editor-wopi", params);
} catch (err) { } catch (err) {
logger.error('wopiEditor error:%s', err.stack); ctx.logger.error('wopiEditor error:%s', err.stack);
res.sendStatus(400); res.sendStatus(400);
} }
logger.info('wopiEditor end'); ctx.logger.info('wopiEditor end');
} }
}); });
} }
function putFile(wopiParams, data, dataStream, dataSize, userLastChangeId, isModifiedByUser, isAutosave, isExitSave) { function putFile(ctx, wopiParams, data, dataStream, dataSize, userLastChangeId, isModifiedByUser, isAutosave, isExitSave) {
return co(function* () { return co(function* () {
let postRes = null; let postRes = null;
try { try {
logger.info('wopi PutFile start'); ctx.logger.info('wopi PutFile start');
if (!wopiParams.userAuth) { if (!wopiParams.userAuth) {
return postRes; return postRes;
} }
let fileInfo = wopiParams.commonInfo.fileInfo; let fileInfo = wopiParams.commonInfo.fileInfo;
let userAuth = wopiParams.userAuth; let userAuth = wopiParams.userAuth;
let uri = `${userAuth.wopiSrc}/contents?access_token=${userAuth.access_token}`; let uri = `${userAuth.wopiSrc}/contents?access_token=${userAuth.access_token}`;
let filterStatus = yield checkIpFilter(uri); let filterStatus = yield checkIpFilter(ctx, uri);
if (0 !== filterStatus) { if (0 !== filterStatus) {
return postRes; return postRes;
} }
@ -432,33 +442,33 @@ function putFile(wopiParams, data, dataStream, dataSize, userLastChangeId, isMod
headers['X-LOOL-WOPI-Timestamp'] = wopiParams.LastModifiedTime; headers['X-LOOL-WOPI-Timestamp'] = wopiParams.LastModifiedTime;
} }
logger.debug('wopi PutFile request uri=%s headers=%j', uri, headers); ctx.logger.debug('wopi PutFile request uri=%s headers=%j', uri, headers);
postRes = yield utils.postRequestPromise(uri, data, dataStream, dataSize, cfgCallbackRequestTimeout, undefined, headers); postRes = yield utils.postRequestPromise(uri, data, dataStream, dataSize, cfgCallbackRequestTimeout, undefined, headers);
logger.debug('wopi PutFile response headers=%j', postRes.response.headers); ctx.logger.debug('wopi PutFile response headers=%j', postRes.response.headers);
logger.debug('wopi PutFile response body:%s', postRes.body); ctx.logger.debug('wopi PutFile response body:%s', postRes.body);
} else { } else {
logger.warn('wopi SupportsUpdate = false or UserCanWrite = false'); ctx.logger.warn('wopi SupportsUpdate = false or UserCanWrite = false');
} }
} catch (err) { } catch (err) {
logger.error('wopi error PutFile:%s', err.stack); ctx.logger.error('wopi error PutFile:%s', err.stack);
} finally { } finally {
logger.info('wopi PutFile end'); ctx.logger.info('wopi PutFile end');
} }
return postRes; return postRes;
}); });
} }
function renameFile(wopiParams, name) { function renameFile(ctx, wopiParams, name) {
return co(function* () { return co(function* () {
let res; let res = undefined;
try { try {
logger.info('wopi RenameFile start'); ctx.logger.info('wopi RenameFile start');
if (!wopiParams.userAuth) { if (!wopiParams.userAuth) {
return res; return res;
} }
let fileInfo = wopiParams.commonInfo.fileInfo; let fileInfo = wopiParams.commonInfo.fileInfo;
let userAuth = wopiParams.userAuth; let userAuth = wopiParams.userAuth;
let uri = `${userAuth.wopiSrc}?access_token=${userAuth.access_token}`; let uri = `${userAuth.wopiSrc}?access_token=${userAuth.access_token}`;
let filterStatus = yield checkIpFilter(uri); let filterStatus = yield checkIpFilter(ctx, uri);
if (0 !== filterStatus) { if (0 !== filterStatus) {
return res; return res;
} }
@ -471,9 +481,9 @@ function renameFile(wopiParams, name) {
let headers = {'X-WOPI-Override': 'RENAME_FILE', 'X-WOPI-Lock': commonInfo.lockId, 'X-WOPI-RequestedName': utf7.encode(name)}; let headers = {'X-WOPI-Override': 'RENAME_FILE', 'X-WOPI-Lock': commonInfo.lockId, 'X-WOPI-RequestedName': utf7.encode(name)};
fillStandardHeaders(headers, uri, userAuth.access_token); fillStandardHeaders(headers, uri, userAuth.access_token);
logger.debug('wopi RenameFile request uri=%s headers=%j', uri, headers); ctx.logger.debug('wopi RenameFile request uri=%s headers=%j', uri, headers);
let postRes = yield utils.postRequestPromise(uri, undefined, undefined, undefined, cfgCallbackRequestTimeout, undefined, headers); let postRes = yield utils.postRequestPromise(uri, undefined, undefined, undefined, cfgCallbackRequestTimeout, undefined, headers);
logger.debug('wopi RenameFile response headers=%j body=%s', postRes.response.headers, postRes.body); ctx.logger.debug('wopi RenameFile response headers=%j body=%s', postRes.response.headers, postRes.body);
if (postRes.body) { if (postRes.body) {
res = JSON.parse(postRes.body); res = JSON.parse(postRes.body);
} else { } else {
@ -481,22 +491,22 @@ function renameFile(wopiParams, name) {
res = {"Name": name}; res = {"Name": name};
} }
} else { } else {
logger.info('wopi SupportsRename = false'); ctx.logger.info('wopi SupportsRename = false');
} }
} catch (err) { } catch (err) {
logger.error('wopi error RenameFile:%s', err.stack); ctx.logger.error('wopi error RenameFile:%s', err.stack);
} finally { } finally {
logger.info('wopi RenameFile end'); ctx.logger.info('wopi RenameFile end');
} }
return res; return res;
}); });
} }
function checkFileInfo(uri, access_token, sc) { function checkFileInfo(ctx, uri, access_token, sc) {
return co(function* () { return co(function* () {
let fileInfo; let fileInfo = undefined;
try { try {
logger.info('wopi checkFileInfo start'); ctx.logger.info('wopi checkFileInfo start');
let filterStatus = yield checkIpFilter(uri); let filterStatus = yield checkIpFilter(ctx, uri);
if (0 !== filterStatus) { if (0 !== filterStatus) {
return fileInfo; return fileInfo;
} }
@ -505,23 +515,23 @@ function checkFileInfo(uri, access_token, sc) {
headers['X-WOPI-SessionContext'] = sc; headers['X-WOPI-SessionContext'] = sc;
} }
fillStandardHeaders(headers, uri, access_token); fillStandardHeaders(headers, uri, access_token);
logger.debug('wopi checkFileInfo request uri=%s headers=%j', uri, headers); ctx.logger.debug('wopi checkFileInfo request uri=%s headers=%j', uri, headers);
let getRes = yield utils.downloadUrlPromise(uri, cfgDownloadTimeout, undefined, undefined, false, headers); let getRes = yield utils.downloadUrlPromise(ctx, uri, cfgDownloadTimeout, undefined, undefined, false, headers);
logger.debug(`wopi checkFileInfo headers=%j body=%s`, getRes.response.headers, getRes.body); ctx.logger.debug(`wopi checkFileInfo headers=%j body=%s`, getRes.response.headers, getRes.body);
fileInfo = JSON.parse(getRes.body); fileInfo = JSON.parse(getRes.body);
} catch (err) { } catch (err) {
logger.error('wopi error checkFileInfo:%s', err.stack); ctx.logger.error('wopi error checkFileInfo:%s', err.stack);
} finally { } finally {
logger.info('wopi checkFileInfo end'); ctx.logger.info('wopi checkFileInfo end');
} }
return fileInfo; return fileInfo;
}); });
} }
function lock(command, lockId, fileInfo, userAuth) { function lock(ctx, command, lockId, fileInfo, userAuth) {
return co(function* () { return co(function* () {
let res = true; let res = true;
try { try {
logger.info('wopi %s start', command); ctx.logger.info('wopi %s start', command);
if (fileInfo && fileInfo.SupportsLocks) { if (fileInfo && fileInfo.SupportsLocks) {
if (!userAuth) { if (!userAuth) {
return false; return false;
@ -529,32 +539,32 @@ function lock(command, lockId, fileInfo, userAuth) {
let wopiSrc = userAuth.wopiSrc; let wopiSrc = userAuth.wopiSrc;
let access_token = userAuth.access_token; let access_token = userAuth.access_token;
let uri = `${wopiSrc}?access_token=${access_token}`; let uri = `${wopiSrc}?access_token=${access_token}`;
let filterStatus = yield checkIpFilter(uri); let filterStatus = yield checkIpFilter(ctx, uri);
if (0 !== filterStatus) { if (0 !== filterStatus) {
return false; return false;
} }
let headers = {"X-WOPI-Override": command, "X-WOPI-Lock": lockId}; let headers = {"X-WOPI-Override": command, "X-WOPI-Lock": lockId};
fillStandardHeaders(headers, uri, access_token); fillStandardHeaders(headers, uri, access_token);
logger.debug('wopi %s request uri=%s headers=%j', command, uri, headers); ctx.logger.debug('wopi %s request uri=%s headers=%j', command, uri, headers);
let postRes = yield utils.postRequestPromise(uri, undefined, undefined, undefined, cfgCallbackRequestTimeout, undefined, headers); let postRes = yield utils.postRequestPromise(uri, undefined, undefined, undefined, cfgCallbackRequestTimeout, undefined, headers);
logger.debug('wopi %s response headers=%j', command, postRes.response.headers); ctx.logger.debug('wopi %s response headers=%j', command, postRes.response.headers);
} else { } else {
logger.info('wopi %s SupportsLocks = false', command); ctx.logger.info('wopi %s SupportsLocks = false', command);
} }
} catch (err) { } catch (err) {
res = false; res = false;
logger.error('wopi error %s:%s', command, err.stack); ctx.logger.error('wopi error %s:%s', command, err.stack);
} finally { } finally {
logger.info('wopi %s end', command); ctx.logger.info('wopi %s end', command);
} }
return res; return res;
}); });
} }
function unlock(wopiParams) { function unlock(ctx, wopiParams) {
return co(function* () { return co(function* () {
try { try {
logger.info('wopi Unlock start'); ctx.logger.info('wopi Unlock start');
let fileInfo = wopiParams.commonInfo.fileInfo; let fileInfo = wopiParams.commonInfo.fileInfo;
if (fileInfo && fileInfo.SupportsLocks) { if (fileInfo && fileInfo.SupportsLocks) {
if (!wopiParams.userAuth) { if (!wopiParams.userAuth) {
@ -564,23 +574,23 @@ function unlock(wopiParams) {
let lockId = wopiParams.commonInfo.lockId; let lockId = wopiParams.commonInfo.lockId;
let access_token = wopiParams.userAuth.access_token; let access_token = wopiParams.userAuth.access_token;
let uri = `${wopiSrc}?access_token=${access_token}`; let uri = `${wopiSrc}?access_token=${access_token}`;
let filterStatus = yield checkIpFilter(uri); let filterStatus = yield checkIpFilter(ctx, uri);
if (0 !== filterStatus) { if (0 !== filterStatus) {
return; return;
} }
let headers = {"X-WOPI-Override": "UNLOCK", "X-WOPI-Lock": lockId}; let headers = {"X-WOPI-Override": "UNLOCK", "X-WOPI-Lock": lockId};
fillStandardHeaders(headers, uri, access_token); fillStandardHeaders(headers, uri, access_token);
logger.debug('wopi Unlock request uri=%s headers=%j', uri, headers); ctx.logger.debug('wopi Unlock request uri=%s headers=%j', uri, headers);
let postRes = yield utils.postRequestPromise(uri, undefined, undefined, undefined, cfgCallbackRequestTimeout, undefined, headers); let postRes = yield utils.postRequestPromise(uri, undefined, undefined, undefined, cfgCallbackRequestTimeout, undefined, headers);
logger.debug('wopi Unlock response headers=%j', postRes.response.headers); ctx.logger.debug('wopi Unlock response headers=%j', postRes.response.headers);
} else { } else {
logger.info('wopi SupportsLocks = false'); ctx.logger.info('wopi SupportsLocks = false');
} }
} catch (err) { } catch (err) {
logger.error('wopi error Unlock:%s', err.stack); ctx.logger.error('wopi error Unlock:%s', err.stack);
} finally { } finally {
logger.info('wopi Unlock end'); ctx.logger.info('wopi Unlock end');
} }
}); });
} }
@ -630,12 +640,12 @@ function fillStandardHeaders(headers, url, access_token) {
headers['Authorization'] = `Bearer ${access_token}`; headers['Authorization'] = `Bearer ${access_token}`;
} }
function checkIpFilter(uri){ function checkIpFilter(ctx, uri){
return co(function* () { return co(function* () {
let urlParsed = new URL(uri); let urlParsed = new URL(uri);
let filterStatus = yield* utils.checkHostFilter(urlParsed.hostname); let filterStatus = yield* utils.checkHostFilter(ctx, urlParsed.hostname);
if (0 !== filterStatus) { if (0 !== filterStatus) {
logger.warn('wopi checkIpFilter error: url = %s', uri); ctx.logger.warn('wopi checkIpFilter error: url = %s', uri);
} }
return filterStatus; return filterStatus;
}); });

View File

@ -53,6 +53,8 @@ const wopiClient = require('./../../DocService/sources/wopiClient');
var statsDClient = require('./../../Common/sources/statsdclient'); var statsDClient = require('./../../Common/sources/statsdclient');
var queueService = require('./../../Common/sources/taskqueueRabbitMQ'); var queueService = require('./../../Common/sources/taskqueueRabbitMQ');
const formatChecker = require('./../../Common/sources/formatchecker'); const formatChecker = require('./../../Common/sources/formatchecker');
const operationContext = require('./../../Common/sources/operationContext');
const tenantManager = require('./../../Common/sources/tenantManager');
var cfgDownloadMaxBytes = configConverter.has('maxDownloadBytes') ? configConverter.get('maxDownloadBytes') : 100000000; var cfgDownloadMaxBytes = configConverter.has('maxDownloadBytes') ? configConverter.get('maxDownloadBytes') : 100000000;
var cfgDownloadTimeout = configConverter.has('downloadTimeout') ? configConverter.get('downloadTimeout') : 60; var cfgDownloadTimeout = configConverter.has('downloadTimeout') ? configConverter.get('downloadTimeout') : 60;
@ -278,7 +280,7 @@ function getTempDir() {
fs.mkdirSync(resultDir); fs.mkdirSync(resultDir);
return {temp: newTemp, source: sourceDir, result: resultDir}; return {temp: newTemp, source: sourceDir, result: resultDir};
} }
function* replaceEmptyFile(docId, fileFrom, ext, _lcid) { function* replaceEmptyFile(ctx, fileFrom, ext, _lcid) {
if (!fs.existsSync(fileFrom) || 0 === fs.lstatSync(fileFrom).size) { if (!fs.existsSync(fileFrom) || 0 === fs.lstatSync(fileFrom).size) {
let locale = 'en-US'; let locale = 'en-US';
if (_lcid) { if (_lcid) {
@ -288,11 +290,11 @@ function* replaceEmptyFile(docId, fileFrom, ext, _lcid) {
if (fs.existsSync(path.join(cfgNewFileTemplate, localeNew))) { if (fs.existsSync(path.join(cfgNewFileTemplate, localeNew))) {
locale = localeNew; locale = localeNew;
} else { } else {
logger.debug('replaceEmptyFile empty locale dir locale=%s (id=%s)', localeNew, docId); ctx.logger.debug('replaceEmptyFile empty locale dir locale=%s', localeNew);
} }
} }
} }
logger.debug('replaceEmptyFile format=%s locale=%s (id=%s)', ext, locale, docId); ctx.logger.debug('replaceEmptyFile format=%s locale=%s', ext, locale);
let format = formatChecker.getFormatFromString(ext); let format = formatChecker.getFormatFromString(ext);
if (formatChecker.isDocumentFormat(format)) { if (formatChecker.isDocumentFormat(format)) {
fs.copyFileSync(path.join(cfgNewFileTemplate, locale, 'new.docx'), fileFrom); fs.copyFileSync(path.join(cfgNewFileTemplate, locale, 'new.docx'), fileFrom);
@ -303,25 +305,26 @@ function* replaceEmptyFile(docId, fileFrom, ext, _lcid) {
} }
} }
} }
function* downloadFile(docId, uri, fileFrom, withAuthorization, filterPrivate, opt_headers) { function* downloadFile(ctx, uri, fileFrom, withAuthorization, filterPrivate, opt_headers) {
var res = constants.CONVERT_DOWNLOAD; var res = constants.CONVERT_DOWNLOAD;
var data = null; var data = null;
var downloadAttemptCount = 0; var downloadAttemptCount = 0;
var urlParsed = url.parse(uri); var urlParsed = url.parse(uri);
var filterStatus = yield* utils.checkHostFilter(urlParsed.hostname); var filterStatus = yield* utils.checkHostFilter(ctx, urlParsed.hostname);
if (0 == filterStatus) { if (0 == filterStatus) {
while (constants.NO_ERROR !== res && downloadAttemptCount++ < cfgDownloadAttemptMaxCount) { while (constants.NO_ERROR !== res && downloadAttemptCount++ < cfgDownloadAttemptMaxCount) {
try { try {
let authorization; let authorization;
if (utils.canIncludeOutboxAuthorization(uri) && withAuthorization) { if (utils.canIncludeOutboxAuthorization(ctx, uri) && withAuthorization) {
authorization = utils.fillJwtForRequest({url: uri}, false); let secret = yield tenantManager.getTenantSecret(ctx, commonDefines.c_oAscSecretType.Outbox);
authorization = utils.fillJwtForRequest({url: uri}, secret, false);
} }
let getRes = yield utils.downloadUrlPromise(uri, cfgDownloadTimeout, cfgDownloadMaxBytes, authorization, filterPrivate, opt_headers); let getRes = yield utils.downloadUrlPromise(ctx, uri, cfgDownloadTimeout, cfgDownloadMaxBytes, authorization, filterPrivate, opt_headers);
data = getRes.body; data = getRes.body;
res = constants.NO_ERROR; res = constants.NO_ERROR;
} catch (err) { } catch (err) {
res = constants.CONVERT_DOWNLOAD; res = constants.CONVERT_DOWNLOAD;
logger.error('error downloadFile:url=%s;attempt=%d;code:%s;connect:%s;(id=%s)\r\n%s', uri, downloadAttemptCount, err.code, err.connect, docId, err.stack); ctx.logger.error('error downloadFile:url=%s;attempt=%d;code:%s;connect:%s %s', uri, downloadAttemptCount, err.code, err.connect, err.stack);
//not continue attempts if timeout //not continue attempts if timeout
if (err.code === 'ETIMEDOUT' || err.code === 'ESOCKETTIMEDOUT') { if (err.code === 'ETIMEDOUT' || err.code === 'ESOCKETTIMEDOUT') {
break; break;
@ -334,18 +337,18 @@ function* downloadFile(docId, uri, fileFrom, withAuthorization, filterPrivate, o
} }
} }
if (constants.NO_ERROR === res) { if (constants.NO_ERROR === res) {
logger.debug('downloadFile complete filesize=%d (id=%s)', data.length, docId); ctx.logger.debug('downloadFile complete filesize=%d', data.length);
fs.writeFileSync(fileFrom, data); fs.writeFileSync(fileFrom, data);
} }
} else { } else {
logger.error('checkIpFilter error:url=%s;code:%s;(id=%s)', uri, filterStatus, docId); ctx.logger.error('checkIpFilter error:url=%s;code:%s;', uri, filterStatus);
res = constants.CONVERT_DOWNLOAD; res = constants.CONVERT_DOWNLOAD;
} }
return res; return res;
} }
function* downloadFileFromStorage(id, strPath, dir) { function* downloadFileFromStorage(ctx, strPath, dir) {
var list = yield storage.listObjects(strPath); var list = yield storage.listObjects(ctx, strPath);
logger.debug('downloadFileFromStorage list %s (id=%s)', list.toString(), id); ctx.logger.debug('downloadFileFromStorage list %s', list.toString());
//create dirs //create dirs
var dirsToCreate = []; var dirsToCreate = [];
var dirStruct = {}; var dirStruct = {};
@ -371,36 +374,36 @@ function* downloadFileFromStorage(id, strPath, dir) {
for (var i = 0; i < list.length; ++i) { for (var i = 0; i < list.length; ++i) {
var file = list[i]; var file = list[i];
var fileRel = storage.getRelativePath(strPath, file); var fileRel = storage.getRelativePath(strPath, file);
var data = yield storage.getObject(file); var data = yield storage.getObject(ctx, file);
fs.writeFileSync(path.join(dir, fileRel), data); fs.writeFileSync(path.join(dir, fileRel), data);
} }
} }
function* processDownloadFromStorage(dataConvert, cmd, task, tempDirs, authorProps) { function* processDownloadFromStorage(ctx, dataConvert, cmd, task, tempDirs, authorProps) {
let res = constants.NO_ERROR; let res = constants.NO_ERROR;
let needConcatFiles = false; let needConcatFiles = false;
if (task.getFromOrigin() || task.getFromSettings()) { if (task.getFromOrigin() || task.getFromSettings()) {
dataConvert.fileFrom = path.join(tempDirs.source, 'origin.' + cmd.getFormat()); dataConvert.fileFrom = path.join(tempDirs.source, 'origin.' + cmd.getFormat());
} else { } else {
//перезаписываем некоторые файлы из m_sKey(например Editor.bin или changes) //перезаписываем некоторые файлы из m_sKey(например Editor.bin или changes)
yield* downloadFileFromStorage(cmd.getSaveKey(), cmd.getSaveKey(), tempDirs.source); yield* downloadFileFromStorage(ctx, cmd.getSaveKey(), tempDirs.source);
let format = cmd.getFormat() || 'bin'; let format = cmd.getFormat() || 'bin';
dataConvert.fileFrom = path.join(tempDirs.source, 'Editor.' + format); dataConvert.fileFrom = path.join(tempDirs.source, 'Editor.' + format);
needConcatFiles = true; needConcatFiles = true;
} }
if (!utils.checkPathTraversal(dataConvert.key, tempDirs.source, dataConvert.fileFrom)) { if (!utils.checkPathTraversal(ctx, dataConvert.key, tempDirs.source, dataConvert.fileFrom)) {
return constants.CONVERT_PARAMS; return constants.CONVERT_PARAMS;
} }
//mail merge //mail merge
let mailMergeSend = cmd.getMailMergeSend(); let mailMergeSend = cmd.getMailMergeSend();
if (mailMergeSend) { if (mailMergeSend) {
yield* downloadFileFromStorage(mailMergeSend.getJsonKey(), mailMergeSend.getJsonKey(), tempDirs.source); yield* downloadFileFromStorage(ctx, mailMergeSend.getJsonKey(), tempDirs.source);
needConcatFiles = true; needConcatFiles = true;
} }
if (needConcatFiles) { if (needConcatFiles) {
yield* concatFiles(tempDirs.source); yield* concatFiles(tempDirs.source);
} }
if (task.getFromChanges()) { if (task.getFromChanges()) {
res = yield* processChanges(tempDirs, cmd, authorProps); res = yield* processChanges(ctx, tempDirs, cmd, authorProps);
} }
//todo rework //todo rework
if (!fs.existsSync(dataConvert.fileFrom)) { if (!fs.existsSync(dataConvert.fileFrom)) {
@ -443,7 +446,7 @@ function* concatFiles(source) {
} }
} }
function* processChanges(tempDirs, cmd, authorProps) { function* processChanges(ctx, tempDirs, cmd, authorProps) {
let res = constants.NO_ERROR; let res = constants.NO_ERROR;
let changesDir = path.join(tempDirs.source, constants.CHANGES_NAME); let changesDir = path.join(tempDirs.source, constants.CHANGES_NAME);
fs.mkdirSync(changesDir); fs.mkdirSync(changesDir);
@ -472,13 +475,13 @@ function* processChanges(tempDirs, cmd, authorProps) {
}]; }];
} }
let streamObj = yield* streamCreate(cmd.getDocId(), changesDir, indexFile++, {highWaterMark: cfgStreamWriterBufferSize}); let streamObj = yield* streamCreate(ctx, changesDir, indexFile++, {highWaterMark: cfgStreamWriterBufferSize});
let curIndexStart = 0; let curIndexStart = 0;
let curIndexEnd = Math.min(curIndexStart + cfgMaxRequestChanges, forceSaveIndex); let curIndexEnd = Math.min(curIndexStart + cfgMaxRequestChanges, forceSaveIndex);
while (curIndexStart < curIndexEnd || extChanges) { while (curIndexStart < curIndexEnd || extChanges) {
let changes = []; let changes = [];
if (curIndexStart < curIndexEnd) { if (curIndexStart < curIndexEnd) {
changes = yield baseConnector.getChangesPromise(cmd.getDocId(), curIndexStart, curIndexEnd, forceSaveTime); changes = yield baseConnector.getChangesPromise(ctx, cmd.getDocId(), curIndexStart, curIndexEnd, forceSaveTime);
} }
if (0 === changes.length && extChanges) { if (0 === changes.length && extChanges) {
changes = extChanges; changes = extChanges;
@ -487,7 +490,7 @@ function* processChanges(tempDirs, cmd, authorProps) {
for (let i = 0; i < changes.length; ++i) { for (let i = 0; i < changes.length; ++i) {
let change = changes[i]; let change = changes[i];
if (change.change_data.startsWith('ENCRYPTED;')) { if (change.change_data.startsWith('ENCRYPTED;')) {
logger.warn('processChanges encrypted changes (id=%s)', cmd.getDocId()); ctx.logger.warn('processChanges encrypted changes');
//todo sql request instead? //todo sql request instead?
res = constants.EDITOR_CHANGES; res = constants.EDITOR_CHANGES;
break; break;
@ -495,7 +498,7 @@ function* processChanges(tempDirs, cmd, authorProps) {
if (null === changesAuthor || changesAuthor !== change.user_id_original) { if (null === changesAuthor || changesAuthor !== change.user_id_original) {
if (null !== changesAuthor) { if (null !== changesAuthor) {
yield* streamEnd(streamObj, ']'); yield* streamEnd(streamObj, ']');
streamObj = yield* streamCreate(cmd.getDocId(), changesDir, indexFile++); streamObj = yield* streamCreate(ctx, changesDir, indexFile++);
} }
let strDate = baseConnector.getDateTime(change.change_date); let strDate = baseConnector.getDateTime(change.change_date);
changesHistory.changes.push({'created': strDate, 'user': {'id': change.user_id_original, 'name': change.user_name}}); changesHistory.changes.push({'created': strDate, 'user': {'id': change.user_id_original, 'name': change.user_name}});
@ -537,13 +540,13 @@ function* processChanges(tempDirs, cmd, authorProps) {
return res; return res;
} }
function* streamCreate(docId, changesDir, indexFile, opt_options) { function* streamCreate(ctx, changesDir, indexFile, opt_options) {
let fileName = constants.CHANGES_NAME + indexFile + '.json'; let fileName = constants.CHANGES_NAME + indexFile + '.json';
let filePath = path.join(changesDir, fileName); let filePath = path.join(changesDir, fileName);
let writeStream = yield utils.promiseCreateWriteStream(filePath, opt_options); let writeStream = yield utils.promiseCreateWriteStream(filePath, opt_options);
writeStream.on('error', function(err) { writeStream.on('error', function(err) {
//todo integrate error handle in main thread (probable: set flag here and check it in main thread) //todo integrate error handle in main thread (probable: set flag here and check it in main thread)
logger.error('WriteStreamError (id=%s)\r\n%s', docId, err.stack); ctx.logger.error('WriteStreamError %s', err.stack);
}); });
return {writeStream: writeStream, filePath: filePath, isNoChangesInFile: true}; return {writeStream: writeStream, filePath: filePath, isNoChangesInFile: true};
} }
@ -558,41 +561,41 @@ function* streamEnd(streamObj, text) {
streamObj.writeStream.end(text, 'utf8'); streamObj.writeStream.end(text, 'utf8');
yield utils.promiseWaitClose(streamObj.writeStream); yield utils.promiseWaitClose(streamObj.writeStream);
} }
function* processUploadToStorage(dir, storagePath) { function* processUploadToStorage(ctx, dir, storagePath) {
var list = yield utils.listObjects(dir); var list = yield utils.listObjects(dir);
if (list.length < MAX_OPEN_FILES) { if (list.length < MAX_OPEN_FILES) {
yield* processUploadToStorageChunk(list, dir, storagePath); yield* processUploadToStorageChunk(ctx, list, dir, storagePath);
} else { } else {
for (var i = 0, j = list.length; i < j; i += MAX_OPEN_FILES) { for (var i = 0, j = list.length; i < j; i += MAX_OPEN_FILES) {
yield* processUploadToStorageChunk(list.slice(i, i + MAX_OPEN_FILES), dir, storagePath); yield* processUploadToStorageChunk(ctx, list.slice(i, i + MAX_OPEN_FILES), dir, storagePath);
} }
} }
} }
function* processUploadToStorageChunk(list, dir, storagePath) { function* processUploadToStorageChunk(ctx, list, dir, storagePath) {
yield Promise.all(list.map(function (curValue) { yield Promise.all(list.map(function (curValue) {
let localValue = storagePath + '/' + curValue.substring(dir.length + 1); let localValue = storagePath + '/' + curValue.substring(dir.length + 1);
return storage.uploadObject(localValue, curValue); return storage.uploadObject(ctx, localValue, curValue);
})); }));
} }
function writeProcessOutputToLog(docId, childRes, isDebug) { function writeProcessOutputToLog(ctx, childRes, isDebug) {
if (childRes) { if (childRes) {
if (undefined !== childRes.stdout) { if (undefined !== childRes.stdout) {
if (isDebug) { if (isDebug) {
logger.debug('stdout (id=%s):%s', docId, childRes.stdout); ctx.logger.debug('stdout:%s', childRes.stdout);
} else { } else {
logger.error('stdout (id=%s):%s', docId, childRes.stdout); ctx.logger.error('stdout:%s', childRes.stdout);
} }
} }
if (undefined !== childRes.stderr) { if (undefined !== childRes.stderr) {
if (isDebug) { if (isDebug) {
logger.debug('stderr (id=%s):%s', docId, childRes.stderr); ctx.logger.debug('stderr:%s', childRes.stderr);
} else { } else {
logger.error('stderr (id=%s):%s', docId, childRes.stderr); ctx.logger.error('stderr:%s', childRes.stderr);
} }
} }
} }
} }
function* postProcess(cmd, dataConvert, tempDirs, childRes, error, isTimeout) { function* postProcess(ctx, cmd, dataConvert, tempDirs, childRes, error, isTimeout) {
var exitCode = 0; var exitCode = 0;
var exitSignal = null; var exitSignal = null;
if(childRes) { if(childRes) {
@ -608,23 +611,23 @@ function* postProcess(cmd, dataConvert, tempDirs, childRes, error, isTimeout) {
error = constants.CONVERT; error = constants.CONVERT;
} }
if (-1 !== exitCodesMinorError.indexOf(error)) { if (-1 !== exitCodesMinorError.indexOf(error)) {
writeProcessOutputToLog(dataConvert.key, childRes, true); writeProcessOutputToLog(ctx, childRes, true);
logger.debug('ExitCode (code=%d;signal=%s;error:%d;id=%s)', exitCode, exitSignal, error, dataConvert.key); ctx.logger.debug('ExitCode (code=%d;signal=%s;error:%d)', exitCode, exitSignal, error);
} else { } else {
writeProcessOutputToLog(dataConvert.key, childRes, false); writeProcessOutputToLog(ctx, childRes, false);
logger.error('ExitCode (code=%d;signal=%s;error:%d;id=%s)', exitCode, exitSignal, error, dataConvert.key); ctx.logger.error('ExitCode (code=%d;signal=%s;error:%d)', exitCode, exitSignal, error);
if (cfgErrorFiles) { if (cfgErrorFiles) {
yield* processUploadToStorage(tempDirs.temp, cfgErrorFiles + '/' + dataConvert.key); yield* processUploadToStorage(ctx, tempDirs.temp, cfgErrorFiles + '/' + dataConvert.key);
logger.debug('processUploadToStorage error complete(id=%s)', dataConvert.key); ctx.logger.debug('processUploadToStorage error complete(id=%s)', dataConvert.key);
} }
} }
} else { } else {
writeProcessOutputToLog(dataConvert.key, childRes, true); writeProcessOutputToLog(ctx, childRes, true);
logger.debug('ExitCode (code=%d;signal=%s;error:%d;id=%s)', exitCode, exitSignal, error, dataConvert.key); ctx.logger.debug('ExitCode (code=%d;signal=%s;error:%d)', exitCode, exitSignal, error);
} }
if (-1 !== exitCodesUpload.indexOf(error)) { if (-1 !== exitCodesUpload.indexOf(error)) {
yield* processUploadToStorage(tempDirs.result, dataConvert.key); yield* processUploadToStorage(ctx, tempDirs.result, dataConvert.key);
logger.debug('processUploadToStorage complete(id=%s)', dataConvert.key); ctx.logger.debug('processUploadToStorage complete');
} }
cmd.setStatusInfo(error); cmd.setStatusInfo(error);
var existFile = false; var existFile = false;
@ -651,13 +654,14 @@ function* postProcess(cmd, dataConvert, tempDirs, childRes, error, isTimeout) {
cmd.setTitle(cmd.getOutputPath()); cmd.setTitle(cmd.getOutputPath());
} }
var res = new commonDefines.TaskQueueData(); var queueData = new commonDefines.TaskQueueData();
res.setCmd(cmd); queueData.setCtx(ctx);
logger.debug('output (data=%s;id=%s)', JSON.stringify(res), dataConvert.key); queueData.setCmd(cmd);
return res; ctx.logger.debug('output (data=%j)', queueData);
return queueData;
} }
function* spawnProcess(isBuilder, tempDirs, dataConvert, authorProps, getTaskTime, task, cmd) { function* spawnProcess(ctx, isBuilder, tempDirs, dataConvert, authorProps, getTaskTime, task) {
let childRes, isTimeout = false; let childRes, isTimeout = false;
let childArgs; let childArgs;
if (cfgArgs.length > 0) { if (cfgArgs.length > 0) {
@ -707,8 +711,11 @@ function* spawnProcess(isBuilder, tempDirs, dataConvert, authorProps, getTaskTim
}, waitMS); }, waitMS);
childRes = yield spawnAsyncPromise; childRes = yield spawnAsyncPromise;
} catch (err) { } catch (err) {
let fLog = null === err.status ? logger.error : logger.debug; if (null === err.status) {
fLog.call(logger, 'error spawnAsync(id=%s)\r\n%s', cmd.getDocId(), err.stack); ctx.logger.error('error spawnAsync %s', err.stack);
} else {
ctx.logger.debug('error spawnAsync %s', err.stack);
}
childRes = err; childRes = err;
} }
if (undefined !== timeoutId) { if (undefined !== timeoutId) {
@ -717,7 +724,7 @@ function* spawnProcess(isBuilder, tempDirs, dataConvert, authorProps, getTaskTim
return {childRes: childRes, isTimeout: isTimeout}; return {childRes: childRes, isTimeout: isTimeout};
} }
function* ExecuteTask(task) { function* ExecuteTask(ctx, task) {
var startDate = null; var startDate = null;
var curDate = null; var curDate = null;
if(clientStatsD) { if(clientStatsD) {
@ -728,7 +735,7 @@ function* ExecuteTask(task) {
var getTaskTime = new Date(); var getTaskTime = new Date();
var cmd = task.getCmd(); var cmd = task.getCmd();
var dataConvert = new TaskQueueDataConvert(task); var dataConvert = new TaskQueueDataConvert(task);
logger.debug('Start Task(id=%s)', dataConvert.key); ctx.logger.info('Start Task');
var error = constants.NO_ERROR; var error = constants.NO_ERROR;
tempDirs = getTempDir(); tempDirs = getTempDir();
let fileTo = task.getToFile(); let fileTo = task.getToFile();
@ -738,7 +745,7 @@ function* ExecuteTask(task) {
if (cmd.getUrl()) { if (cmd.getUrl()) {
let format = cmd.getFormat(); let format = cmd.getFormat();
dataConvert.fileFrom = path.join(tempDirs.source, dataConvert.key + '.' + format); dataConvert.fileFrom = path.join(tempDirs.source, dataConvert.key + '.' + format);
if (utils.checkPathTraversal(dataConvert.key, tempDirs.source, dataConvert.fileFrom)) { if (utils.checkPathTraversal(ctx, dataConvert.key, tempDirs.source, dataConvert.fileFrom)) {
let url = cmd.getUrl(); let url = cmd.getUrl();
let withAuthorization = cmd.getWithAuthorization(); let withAuthorization = cmd.getWithAuthorization();
let filterPrivate = !withAuthorization; let filterPrivate = !withAuthorization;
@ -760,13 +767,13 @@ function* ExecuteTask(task) {
headers = {'X-WOPI-MaxExpectedSize': cfgDownloadMaxBytes, 'X-WOPI-ItemVersion': fileInfo.Version}; headers = {'X-WOPI-MaxExpectedSize': cfgDownloadMaxBytes, 'X-WOPI-ItemVersion': fileInfo.Version};
wopiClient.fillStandardHeaders(headers, url, userAuth.access_token); wopiClient.fillStandardHeaders(headers, url, userAuth.access_token);
} }
logger.debug('wopi url=%s; headers=%j(id=%s)', url, headers, dataConvert.key); ctx.logger.debug('wopi url=%s; headers=%j', url, headers);
} }
if (undefined === fileSize || fileSize > 0) { if (undefined === fileSize || fileSize > 0) {
error = yield* downloadFile(dataConvert.key, url, dataConvert.fileFrom, withAuthorization, filterPrivate, headers); error = yield* downloadFile(ctx, url, dataConvert.fileFrom, withAuthorization, filterPrivate, headers);
} }
if (constants.NO_ERROR === error) { if (constants.NO_ERROR === error) {
yield* replaceEmptyFile(dataConvert.key, dataConvert.fileFrom, format, cmd.getLCID()); yield* replaceEmptyFile(ctx, dataConvert.fileFrom, format, cmd.getLCID());
} }
if(clientStatsD) { if(clientStatsD) {
clientStatsD.timing('conv.downloadFile', new Date() - curDate); clientStatsD.timing('conv.downloadFile', new Date() - curDate);
@ -776,16 +783,16 @@ function* ExecuteTask(task) {
error = constants.CONVERT_PARAMS; error = constants.CONVERT_PARAMS;
} }
} else if (cmd.getSaveKey()) { } else if (cmd.getSaveKey()) {
yield* downloadFileFromStorage(cmd.getDocId(), cmd.getDocId(), tempDirs.source); yield* downloadFileFromStorage(ctx, cmd.getDocId(), tempDirs.source);
logger.debug('downloadFileFromStorage complete(id=%s)', dataConvert.key); ctx.logger.debug('downloadFileFromStorage complete');
if(clientStatsD) { if(clientStatsD) {
clientStatsD.timing('conv.downloadFileFromStorage', new Date() - curDate); clientStatsD.timing('conv.downloadFileFromStorage', new Date() - curDate);
curDate = new Date(); curDate = new Date();
} }
error = yield* processDownloadFromStorage(dataConvert, cmd, task, tempDirs, authorProps); error = yield* processDownloadFromStorage(ctx, dataConvert, cmd, task, tempDirs, authorProps);
} else if (cmd.getForgotten()) { } else if (cmd.getForgotten()) {
yield* downloadFileFromStorage(cmd.getDocId(), cmd.getForgotten(), tempDirs.source); yield* downloadFileFromStorage(ctx, cmd.getForgotten(), tempDirs.source);
logger.debug('downloadFileFromStorage complete(id=%s)', dataConvert.key); ctx.logger.debug('downloadFileFromStorage complete');
let list = yield utils.listObjects(tempDirs.source, false); let list = yield utils.listObjects(tempDirs.source, false);
if (list.length > 0) { if (list.length > 0) {
dataConvert.fileFrom = list[0]; dataConvert.fileFrom = list[0];
@ -797,8 +804,8 @@ function* ExecuteTask(task) {
} }
} else if (isBuilder) { } else if (isBuilder) {
//in cause script in POST body //in cause script in POST body
yield* downloadFileFromStorage(cmd.getDocId(), cmd.getDocId(), tempDirs.source); yield* downloadFileFromStorage(ctx, cmd.getDocId(), tempDirs.source);
logger.debug('downloadFileFromStorage complete(id=%s)', dataConvert.key); ctx.logger.debug('downloadFileFromStorage complete');
let list = yield utils.listObjects(tempDirs.source, false); let list = yield utils.listObjects(tempDirs.source, false);
if (list.length > 0) { if (list.length > 0) {
dataConvert.fileFrom = list[0]; dataConvert.fileFrom = list[0];
@ -813,16 +820,16 @@ function* ExecuteTask(task) {
//todo заглушка.вся конвертация на клиенте, но нет простого механизма сохранения на клиенте //todo заглушка.вся конвертация на клиенте, но нет простого механизма сохранения на клиенте
yield utils.pipeFiles(dataConvert.fileFrom, dataConvert.fileTo); yield utils.pipeFiles(dataConvert.fileFrom, dataConvert.fileTo);
} else { } else {
({childRes, isTimeout} = yield* spawnProcess(isBuilder, tempDirs, dataConvert, authorProps, getTaskTime, task, cmd)); ({childRes, isTimeout} = yield* spawnProcess(ctx, isBuilder, tempDirs, dataConvert, authorProps, getTaskTime, task));
if (childRes && 0 !== childRes.status && !isTimeout && task.getFromChanges() if (childRes && 0 !== childRes.status && !isTimeout && task.getFromChanges()
&& constants.AVS_OFFICESTUDIO_FILE_OTHER_OOXML !== dataConvert.formatTo && constants.AVS_OFFICESTUDIO_FILE_OTHER_OOXML !== dataConvert.formatTo
&& !formatChecker.isOOXFormat(dataConvert.formatTo) && !cmd.getWopiParams()) { && !formatChecker.isOOXFormat(dataConvert.formatTo) && !cmd.getWopiParams()) {
logger.warn('rollback to save changes to ooxml. See assemblyFormatAsOrigin param. formatTo=%s (id=%s)', formatChecker.getStringFromFormat(dataConvert.formatTo), dataConvert.key); ctx.logger.warn('rollback to save changes to ooxml. See assemblyFormatAsOrigin param. formatTo=%s', formatChecker.getStringFromFormat(dataConvert.formatTo));
let extOld = path.extname(dataConvert.fileTo); let extOld = path.extname(dataConvert.fileTo);
let extNew = '.' + formatChecker.getStringFromFormat(constants.AVS_OFFICESTUDIO_FILE_OTHER_OOXML); let extNew = '.' + formatChecker.getStringFromFormat(constants.AVS_OFFICESTUDIO_FILE_OTHER_OOXML);
dataConvert.formatTo = constants.AVS_OFFICESTUDIO_FILE_OTHER_OOXML; dataConvert.formatTo = constants.AVS_OFFICESTUDIO_FILE_OTHER_OOXML;
dataConvert.fileTo = dataConvert.fileTo.slice(0, -extOld.length) + extNew; dataConvert.fileTo = dataConvert.fileTo.slice(0, -extOld.length) + extNew;
({childRes, isTimeout} = yield* spawnProcess(isBuilder, tempDirs, dataConvert, authorProps, getTaskTime, task, cmd)); ({childRes, isTimeout} = yield* spawnProcess(ctx, isBuilder, tempDirs, dataConvert, authorProps, getTaskTime, task));
} }
} }
if(clientStatsD) { if(clientStatsD) {
@ -830,15 +837,15 @@ function* ExecuteTask(task) {
curDate = new Date(); curDate = new Date();
} }
} }
resData = yield* postProcess(cmd, dataConvert, tempDirs, childRes, error, isTimeout); resData = yield* postProcess(ctx, cmd, dataConvert, tempDirs, childRes, error, isTimeout);
logger.debug('postProcess (id=%s)', dataConvert.key); ctx.logger.debug('postProcess');
if(clientStatsD) { if(clientStatsD) {
clientStatsD.timing('conv.postProcess', new Date() - curDate); clientStatsD.timing('conv.postProcess', new Date() - curDate);
curDate = new Date(); curDate = new Date();
} }
if (tempDirs) { if (tempDirs) {
fs.rmSync(tempDirs.temp, { recursive: true, force: true }); fs.rmSync(tempDirs.temp, { recursive: true, force: true });
logger.debug('deleteFolderRecursive (id=%s)', dataConvert.key); ctx.logger.debug('deleteFolderRecursive');
if(clientStatsD) { if(clientStatsD) {
clientStatsD.timing('conv.deleteFolderRecursive', new Date() - curDate); clientStatsD.timing('conv.deleteFolderRecursive', new Date() - curDate);
curDate = new Date(); curDate = new Date();
@ -847,6 +854,7 @@ function* ExecuteTask(task) {
if(clientStatsD) { if(clientStatsD) {
clientStatsD.timing('conv.allconvert', new Date() - startDate); clientStatsD.timing('conv.allconvert', new Date() - startDate);
} }
ctx.logger.info('End Task');
return resData; return resData;
} }
@ -854,13 +862,15 @@ function receiveTask(data, ack) {
return co(function* () { return co(function* () {
var res = null; var res = null;
var task = null; var task = null;
let ctx = new operationContext.Context();
try { try {
task = new commonDefines.TaskQueueData(JSON.parse(data)); task = new commonDefines.TaskQueueData(JSON.parse(data));
if (task) { if (task) {
res = yield* ExecuteTask(task); ctx.initFromTaskQueueData(task);
res = yield* ExecuteTask(ctx, task);
} }
} catch (err) { } catch (err) {
logger.error(err); ctx.logger.error(err);
} finally { } finally {
try { try {
if (!res && task) { if (!res && task) {
@ -868,13 +878,14 @@ function receiveTask(data, ack) {
var cmd = task.getCmd(); var cmd = task.getCmd();
cmd.setStatusInfo(constants.CONVERT); cmd.setStatusInfo(constants.CONVERT);
res = new commonDefines.TaskQueueData(); res = new commonDefines.TaskQueueData();
res.setCtx(ctx);
res.setCmd(cmd); res.setCmd(cmd);
} }
if (res) { if (res) {
yield queue.addResponse(res); yield queue.addResponse(res);
} }
} catch (err) { } catch (err) {
logger.error(err); ctx.logger.error(err);
} finally { } finally {
ack(); ack();
} }
@ -883,10 +894,13 @@ function receiveTask(data, ack) {
} }
function simulateErrorResponse(data){ function simulateErrorResponse(data){
let task = new commonDefines.TaskQueueData(JSON.parse(data)); let task = new commonDefines.TaskQueueData(JSON.parse(data));
let ctx = new operationContext.Context();
ctx.initFromTaskQueueData(task);
//simulate error response //simulate error response
let cmd = task.getCmd(); let cmd = task.getCmd();
cmd.setStatusInfo(constants.CONVERT); cmd.setStatusInfo(constants.CONVERT);
let res = new commonDefines.TaskQueueData(); let res = new commonDefines.TaskQueueData();
task.setCtx(ctx);
res.setCmd(cmd); res.setCmd(cmd);
return res; return res;
} }
@ -895,7 +909,7 @@ function run() {
queue.on('task', receiveTask); queue.on('task', receiveTask);
queue.init(true, true, true, false, false, false, function(err) { queue.init(true, true, true, false, false, false, function(err) {
if (null != err) { if (null != err) {
logger.error('createTaskQueue error :\r\n%s', err.stack); operationContext.global.logger.error('createTaskQueue error: %s', err.stack);
} }
}); });
} }

View File

@ -34,6 +34,7 @@
const cluster = require('cluster'); const cluster = require('cluster');
const logger = require('./../../Common/sources/logger'); const logger = require('./../../Common/sources/logger');
const operationContext = require('./../../Common/sources/operationContext');
if (cluster.isMaster) { if (cluster.isMaster) {
const fs = require('fs'); const fs = require('fs');
@ -42,12 +43,18 @@ if (cluster.isMaster) {
const configCommon = require('config'); const configCommon = require('config');
const config = configCommon.get('FileConverter.converter'); const config = configCommon.get('FileConverter.converter');
const license = require('./../../Common/sources/license'); const license = require('./../../Common/sources/license');
const tenantManager = require('./../../Common/sources/tenantManager');
const cfgLicenseFile = configCommon.get('license.license_file');
const cfgMaxProcessCount = config.get('maxprocesscount'); const cfgMaxProcessCount = config.get('maxprocesscount');
var licenseInfo, workersCount = 0; var workersCount = 0;
const readLicense = function* () { const readLicense = function* () {
[licenseInfo] = yield* license.readLicense(); workersCount = Math.ceil(numCPUs * cfgMaxProcessCount);
workersCount = Math.min(licenseInfo.count, Math.ceil(numCPUs * cfgMaxProcessCount)); if (!tenantManager.isMultitenantMode()) {
let [licenseInfo] = yield* license.readLicense(cfgLicenseFile);
workersCount = Math.min(licenseInfo.count, workersCount);
}
}; };
const updateWorkers = () => { const updateWorkers = () => {
var i; var i;
@ -55,7 +62,7 @@ if (cluster.isMaster) {
if (arrKeyWorkers.length < workersCount) { if (arrKeyWorkers.length < workersCount) {
for (i = arrKeyWorkers.length; i < workersCount; ++i) { for (i = arrKeyWorkers.length; i < workersCount; ++i) {
const newWorker = cluster.fork(); const newWorker = cluster.fork();
logger.warn('worker %s started.', newWorker.process.pid); operationContext.global.logger.warn('worker %s started.', newWorker.process.pid);
} }
} else { } else {
for (i = workersCount; i < arrKeyWorkers.length; ++i) { for (i = workersCount; i < arrKeyWorkers.length; ++i) {
@ -70,31 +77,33 @@ if (cluster.isMaster) {
return co(function*() { return co(function*() {
try { try {
yield* readLicense(); yield* readLicense();
logger.warn('update cluster with %s workers', workersCount); operationContext.global.logger.warn('update cluster with %s workers', workersCount);
updateWorkers(); updateWorkers();
} catch (err) { } catch (err) {
logger.error('updateLicense error:\r\n%s', err.stack); operationContext.global.logger.error('updateLicense error: %s', err.stack);
} }
}); });
}; };
cluster.on('exit', (worker, code, signal) => { cluster.on('exit', (worker, code, signal) => {
logger.warn('worker %s died (code = %s; signal = %s).', worker.process.pid, code, signal); operationContext.global.logger.warn('worker %s died (code = %s; signal = %s).', worker.process.pid, code, signal);
updateWorkers(); updateWorkers();
}); });
updateLicense(); updateLicense();
fs.watchFile(configCommon.get('license').get('license_file'), updateLicense); if (!tenantManager.isMultitenantMode()) {
setInterval(updateLicense, 86400000); fs.watchFile(cfgLicenseFile, updateLicense);
setInterval(updateLicense, 86400000);
}
} else { } else {
const converter = require('./converter'); const converter = require('./converter');
converter.run(); converter.run();
} }
process.on('uncaughtException', (err) => { process.on('uncaughtException', (err) => {
logger.error((new Date).toUTCString() + ' uncaughtException:', err.message); operationContext.global.logger.error((new Date).toUTCString() + ' uncaughtException:', err.message);
logger.error(err.stack); operationContext.global.logger.error(err.stack);
logger.shutdown(() => { logger.shutdown(() => {
process.exit(1); process.exit(1);
}); });

View File

@ -26,6 +26,7 @@ USE onlyoffice;
-- --
CREATE TABLE IF NOT EXISTS `doc_changes` ( CREATE TABLE IF NOT EXISTS `doc_changes` (
`tenant` varchar(255) NOT NULL,
`id` varchar(255) NOT NULL, `id` varchar(255) NOT NULL,
`change_id` int(10) unsigned NOT NULL, `change_id` int(10) unsigned NOT NULL,
`user_id` varchar(255) NOT NULL, `user_id` varchar(255) NOT NULL,
@ -33,7 +34,7 @@ CREATE TABLE IF NOT EXISTS `doc_changes` (
`user_name` varchar(255) NOT NULL, `user_name` varchar(255) NOT NULL,
`change_data` longtext NOT NULL, `change_data` longtext NOT NULL,
`change_date` datetime NOT NULL, `change_date` datetime NOT NULL,
PRIMARY KEY (`id`,`change_id`) PRIMARY KEY (`tenant`, `id`,`change_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8; ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- --
@ -48,6 +49,7 @@ CREATE TABLE IF NOT EXISTS `doc_changes` (
-- --
CREATE TABLE IF NOT EXISTS `task_result` ( CREATE TABLE IF NOT EXISTS `task_result` (
`tenant` varchar(255) NOT NULL,
`id` varchar(255) NOT NULL, `id` varchar(255) NOT NULL,
`status` tinyint(3) NOT NULL, `status` tinyint(3) NOT NULL,
`status_info` int(10) NOT NULL, `status_info` int(10) NOT NULL,
@ -59,7 +61,7 @@ CREATE TABLE IF NOT EXISTS `task_result` (
`baseurl` text NOT NULL, `baseurl` text NOT NULL,
`password` longtext NULL, `password` longtext NULL,
`additional` longtext NULL, `additional` longtext NULL,
PRIMARY KEY (`id`) PRIMARY KEY (`tenant`, `id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8; ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- --

View File

@ -8,6 +8,7 @@
-- Table structure for doc_changes -- Table structure for doc_changes
-- ---------------------------- -- ----------------------------
CREATE TABLE IF NOT EXISTS "public"."doc_changes" ( CREATE TABLE IF NOT EXISTS "public"."doc_changes" (
"tenant" varchar(255) COLLATE "default" NOT NULL,
"id" varchar(255) COLLATE "default" NOT NULL, "id" varchar(255) COLLATE "default" NOT NULL,
"change_id" int4 NOT NULL, "change_id" int4 NOT NULL,
"user_id" varchar(255) COLLATE "default" NOT NULL, "user_id" varchar(255) COLLATE "default" NOT NULL,
@ -15,7 +16,7 @@ CREATE TABLE IF NOT EXISTS "public"."doc_changes" (
"user_name" varchar(255) COLLATE "default" NOT NULL, "user_name" varchar(255) COLLATE "default" NOT NULL,
"change_data" text COLLATE "default" NOT NULL, "change_data" text COLLATE "default" NOT NULL,
"change_date" timestamp without time zone NOT NULL, "change_date" timestamp without time zone NOT NULL,
PRIMARY KEY ("id", "change_id") PRIMARY KEY ("tenant", "id", "change_id")
) )
WITH (OIDS=FALSE); WITH (OIDS=FALSE);
@ -23,6 +24,7 @@ WITH (OIDS=FALSE);
-- Table structure for task_result -- Table structure for task_result
-- ---------------------------- -- ----------------------------
CREATE TABLE IF NOT EXISTS "public"."task_result" ( CREATE TABLE IF NOT EXISTS "public"."task_result" (
"tenant" varchar(255) COLLATE "default" NOT NULL,
"id" varchar(255) COLLATE "default" NOT NULL, "id" varchar(255) COLLATE "default" NOT NULL,
"status" int2 NOT NULL, "status" int2 NOT NULL,
"status_info" int4 NOT NULL, "status_info" int4 NOT NULL,
@ -34,11 +36,11 @@ CREATE TABLE IF NOT EXISTS "public"."task_result" (
"baseurl" text COLLATE "default" NOT NULL, "baseurl" text COLLATE "default" NOT NULL,
"password" text COLLATE "default" NULL, "password" text COLLATE "default" NULL,
"additional" text COLLATE "default" NULL, "additional" text COLLATE "default" NULL,
PRIMARY KEY ("id") PRIMARY KEY ("tenant", "id")
) )
WITH (OIDS=FALSE); WITH (OIDS=FALSE);
CREATE OR REPLACE FUNCTION merge_db(_id varchar(255), _status int2, _status_info int4, _last_open_date timestamp without time zone, _user_index int4, _change_id int4, _callback text, _baseurl text, OUT isupdate char(5), OUT userindex int4) AS CREATE OR REPLACE FUNCTION merge_db(_tetant varchar(255), _id varchar(255), _status int2, _status_info int4, _last_open_date timestamp without time zone, _user_index int4, _change_id int4, _callback text, _baseurl text, OUT isupdate char(5), OUT userindex int4) AS
$$ $$
DECLARE DECLARE
t_var "public"."task_result"."user_index"%TYPE; t_var "public"."task_result"."user_index"%TYPE;
@ -47,9 +49,9 @@ BEGIN
-- first try to update the key -- first try to update the key
-- note that "a" must be unique -- note that "a" must be unique
IF ((_callback <> '') IS TRUE) AND ((_baseurl <> '') IS TRUE) THEN IF ((_callback <> '') IS TRUE) AND ((_baseurl <> '') IS TRUE) THEN
UPDATE "public"."task_result" SET last_open_date=_last_open_date, user_index=user_index+1,callback=_callback,baseurl=_baseurl WHERE id = _id RETURNING user_index into userindex; UPDATE "public"."task_result" SET last_open_date=_last_open_date, user_index=user_index+1,callback=_callback,baseurl=_baseurl WHERE tenant = _tetant AND id = _id RETURNING user_index into userindex;
ELSE ELSE
UPDATE "public"."task_result" SET last_open_date=_last_open_date, user_index=user_index+1 WHERE id = _id RETURNING user_index into userindex; UPDATE "public"."task_result" SET last_open_date=_last_open_date, user_index=user_index+1 WHERE tenant = _tetant AND id = _id RETURNING user_index into userindex;
END IF; END IF;
IF found THEN IF found THEN
isupdate := 'true'; isupdate := 'true';
@ -59,7 +61,7 @@ BEGIN
-- if someone else inserts the same key concurrently, -- if someone else inserts the same key concurrently,
-- we could get a unique-key failure -- we could get a unique-key failure
BEGIN BEGIN
INSERT INTO "public"."task_result"(id, status, status_info, last_open_date, user_index, change_id, callback, baseurl) VALUES(_id, _status, _status_info, _last_open_date, _user_index, _change_id, _callback, _baseurl) RETURNING user_index into userindex; INSERT INTO "public"."task_result"(id, status, status_info, last_open_date, user_index, change_id, callback, baseurl) VALUES(_tetant, _id, _status, _status_info, _last_open_date, _user_index, _change_id, _callback, _baseurl) RETURNING user_index into userindex;
isupdate := 'false'; isupdate := 'false';
RETURN; RETURN;
EXCEPTION WHEN unique_violation THEN EXCEPTION WHEN unique_violation THEN