mirror of
https://github.com/ONLYOFFICE/server.git
synced 2026-02-10 18:05:07 +08:00
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:
@ -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",
|
||||
"refreshLockInterval": "10m"
|
||||
},
|
||||
"tenants": {
|
||||
"baseDir": "",
|
||||
"baseDomain": "",
|
||||
"filenameSecret": "secret.key",
|
||||
"filenameLicense": "license.lic",
|
||||
"defaultTetant": "tetant"
|
||||
},
|
||||
"services": {
|
||||
"CoAuthoring": {
|
||||
"server": {
|
||||
@ -200,8 +207,7 @@
|
||||
"allowMetaIPAddress": true
|
||||
},
|
||||
"secret": {
|
||||
"browser": {"string": "secret", "file": "", "tenants": {}},
|
||||
"inbox": {"string": "secret", "file": "", "tenants": {}},
|
||||
"inbox": {"string": "secret", "file": ""},
|
||||
"outbox": {"string": "secret", "file": ""},
|
||||
"session": {"string": "secret", "file": ""}
|
||||
},
|
||||
@ -213,9 +219,6 @@
|
||||
"outbox": false
|
||||
}
|
||||
},
|
||||
"browser": {
|
||||
"secretFromInbox": true
|
||||
},
|
||||
"inbox": {
|
||||
"header": "Authorization",
|
||||
"prefix": "Bearer ",
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
"type": "console",
|
||||
"layout": {
|
||||
"type": "pattern",
|
||||
"pattern": "%[[%d] [%p] %c -%] %.10000m"
|
||||
"pattern": "%[[%d] [%p] [%X{TENANT}] [%X{DOCID}] [%X{USERID}] %c -%] %.10000m"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
"type": "console",
|
||||
"layout": {
|
||||
"type": "pattern",
|
||||
"pattern": "[%d] [%p] %c - %.10000m"
|
||||
"pattern": "[%d] [%p] [%X{TENANT}] [%X{DOCID}] [%X{USERID}] %c - %.10000m"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -34,6 +34,7 @@
|
||||
var config = require('config');
|
||||
var container = require('rhea');
|
||||
var logger = require('./logger');
|
||||
const operationContext = require('./operationContext');
|
||||
|
||||
const cfgRabbitSocketOptions = config.get('activemq.connectOptions');
|
||||
|
||||
@ -45,20 +46,20 @@ function connetPromise(reconnectOnConnectionError, closeCallback) {
|
||||
let conn = container.create_container().connect(cfgRabbitSocketOptions);
|
||||
let isConnected = false;
|
||||
conn.on('connection_open', function(context) {
|
||||
logger.debug('[AMQP] connected');
|
||||
operationContext.global.logger.debug('[AMQP] connected');
|
||||
isConnected = true;
|
||||
resolve(conn);
|
||||
});
|
||||
conn.on('connection_error', function(context) {
|
||||
//todo
|
||||
logger.debug('[AMQP] connection_error %s', context.error);
|
||||
operationContext.global.logger.debug('[AMQP] connection_error %s', context.error);
|
||||
});
|
||||
conn.on('connection_close', function() {
|
||||
//todo
|
||||
logger.debug('[AMQP] conn close');
|
||||
operationContext.global.logger.debug('[AMQP] conn close');
|
||||
});
|
||||
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) {
|
||||
closeCallback();
|
||||
} else {
|
||||
|
||||
@ -708,6 +708,7 @@ CMailMergeSendData.prototype.setIsJsonKey = function(v) {
|
||||
};
|
||||
function TaskQueueData(data) {
|
||||
if (data) {
|
||||
this['ctx'] = data['ctx'];
|
||||
this['cmd'] = new InputCommand(data['cmd'], true);
|
||||
this['toFile'] = data['toFile'];
|
||||
this['fromOrigin'] = data['fromOrigin'];
|
||||
@ -718,6 +719,7 @@ function TaskQueueData(data) {
|
||||
this['dataKey'] = data['dataKey'];
|
||||
this['visibilityTimeout'] = data['visibilityTimeout'];
|
||||
} else {
|
||||
this['ctx'] = undefined;
|
||||
this['cmd'] = undefined;
|
||||
this['toFile'] = undefined;
|
||||
this['fromOrigin'] = undefined;
|
||||
@ -730,6 +732,12 @@ function TaskQueueData(data) {
|
||||
}
|
||||
}
|
||||
TaskQueueData.prototype = {
|
||||
getCtx : function() {
|
||||
return this['ctx'];
|
||||
},
|
||||
setCtx : function(data) {
|
||||
return this['ctx'] = data;
|
||||
},
|
||||
getCmd : function() {
|
||||
return this['cmd'];
|
||||
},
|
||||
|
||||
@ -35,6 +35,7 @@
|
||||
exports.DOC_ID_PATTERN = '0-9-.a-zA-Z_=';
|
||||
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_SOCKET_PATTERN = new RegExp("^/doc/([" + exports.DOC_ID_PATTERN + "]*)/c.+", 'i');
|
||||
exports.DOC_ID_MAX_LENGTH = 240;
|
||||
exports.USER_ID_MAX_LENGTH = 240;//255-240=15 symbols to make user id unique
|
||||
exports.USER_NAME_MAX_LENGTH = 255;
|
||||
@ -46,6 +47,8 @@ exports.ONLY_OFFICE_URL_PARAM = 'ooname';
|
||||
exports.DISPLAY_PREFIX = 'display';
|
||||
exports.CHANGES_NAME = 'changes';
|
||||
exports.VIEWER_ONLY = /^(?:(pdf|djvu|xps|oxps))$/;
|
||||
exports.DEFAULT_DOC_ID = 'docId';
|
||||
exports.DEFAULT_USER_ID = 'userId';
|
||||
|
||||
exports.RIGHTS = {
|
||||
None : 0,
|
||||
|
||||
@ -34,7 +34,6 @@
|
||||
|
||||
var path = require('path');
|
||||
var constants = require('./constants');
|
||||
var logger = require('./logger');
|
||||
|
||||
function getImageFormatBySignature(buffer) {
|
||||
var length = buffer.length;
|
||||
@ -500,7 +499,7 @@ exports.getStringFromFormat = function(format) {
|
||||
return '';
|
||||
}
|
||||
};
|
||||
exports.getImageFormat = function(buffer, optExt) {
|
||||
exports.getImageFormat = function(ctx, buffer, optExt) {
|
||||
var format = constants.AVS_OFFICESTUDIO_FILE_UNKNOWN;
|
||||
try {
|
||||
//signature
|
||||
@ -519,8 +518,7 @@ exports.getImageFormat = function(buffer, optExt) {
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
logger.error(optExt);
|
||||
logger.error('error getImageFormat:\r\n%s', e.stack);
|
||||
ctx.logger.error('error getImageFormat ext=%s: %s', optExt, e.stack);
|
||||
}
|
||||
return format;
|
||||
};
|
||||
@ -540,7 +538,6 @@ exports.isPresentationFormat = function(format) {
|
||||
format === constants.AVS_OFFICESTUDIO_FILE_TEAMLAB_PPTY;
|
||||
};
|
||||
exports.isOOXFormat = function(format) {
|
||||
console.log('isOOXFormat'+format);
|
||||
return constants.AVS_OFFICESTUDIO_FILE_DOCUMENT_DOCX === format
|
||||
|| constants.AVS_OFFICESTUDIO_FILE_DOCUMENT_DOCM === format
|
||||
|| constants.AVS_OFFICESTUDIO_FILE_DOCUMENT_DOTX === format
|
||||
|
||||
@ -73,7 +73,9 @@ if (config.get('log.options.replaceConsole')) {
|
||||
console.error = logger.error.bind(logger);
|
||||
console.debug = logger.debug.bind(logger);
|
||||
}
|
||||
|
||||
exports.getLogger = function (){
|
||||
return log4js.getLogger.apply(log4js, Array.prototype.slice.call(arguments));
|
||||
};
|
||||
exports.trace = function (){
|
||||
return logger.trace.apply(logger, Array.prototype.slice.call(arguments));
|
||||
};
|
||||
|
||||
91
Common/sources/operationContext.js
Normal file
91
Common/sources/operationContext.js
Normal 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();
|
||||
@ -34,6 +34,7 @@
|
||||
var config = require('config');
|
||||
var amqp = require('amqplib/callback_api');
|
||||
var logger = require('./logger');
|
||||
const operationContext = require('./operationContext');
|
||||
|
||||
var cfgRabbitUrl = config.get('rabbitmq.url');
|
||||
var cfgRabbitSocketOptions = config.get('rabbitmq.socketOptions');
|
||||
@ -45,7 +46,7 @@ function connetPromise(reconnectOnConnectionError, closeCallback) {
|
||||
function startConnect() {
|
||||
amqp.connect(cfgRabbitUrl, cfgRabbitSocketOptions, function(err, conn) {
|
||||
if (null != err) {
|
||||
logger.error('[AMQP] %s', err.stack);
|
||||
operationContext.global.logger.error('[AMQP] %s', err.stack);
|
||||
if (reconnectOnConnectionError) {
|
||||
setTimeout(startConnect, RECONNECT_TIMEOUT);
|
||||
} else {
|
||||
@ -53,16 +54,16 @@ function connetPromise(reconnectOnConnectionError, closeCallback) {
|
||||
}
|
||||
} else {
|
||||
conn.on('error', function(err) {
|
||||
logger.error('[AMQP] conn error', err.stack);
|
||||
operationContext.global.logger.error('[AMQP] conn error', err.stack);
|
||||
});
|
||||
var closeEventCallback = function() {
|
||||
//in some case receive multiple close events
|
||||
conn.removeListener('close', closeEventCallback);
|
||||
logger.debug('[AMQP] conn close');
|
||||
operationContext.global.logger.debug('[AMQP] conn close');
|
||||
closeCallback();
|
||||
};
|
||||
conn.on('close', closeEventCallback);
|
||||
logger.debug('[AMQP] connected');
|
||||
operationContext.global.logger.debug('[AMQP] connected');
|
||||
resolve(conn);
|
||||
}
|
||||
});
|
||||
|
||||
@ -33,84 +33,99 @@
|
||||
'use strict';
|
||||
var config = require('config');
|
||||
var utils = require('./utils');
|
||||
var logger = require('./logger');
|
||||
|
||||
var storage = require('./' + config.get('storage.name'));
|
||||
function getStoragePath(strPath) {
|
||||
return strPath.replace(/\\/g, '/');
|
||||
var tenantManager = require('./tenantManager');
|
||||
|
||||
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) {
|
||||
return storage.getObject(getStoragePath(strPath));
|
||||
exports.getObject = function(ctx, strPath) {
|
||||
return storage.getObject(getStoragePath(ctx, strPath));
|
||||
};
|
||||
exports.createReadStream = function(strPath) {
|
||||
return storage.createReadStream(getStoragePath(strPath));
|
||||
exports.createReadStream = function(ctx, strPath) {
|
||||
return storage.createReadStream(getStoragePath(ctx, strPath));
|
||||
};
|
||||
exports.putObject = function(strPath, buffer, contentLength) {
|
||||
return storage.putObject(getStoragePath(strPath), buffer, contentLength);
|
||||
exports.putObject = function(ctx, strPath, buffer, contentLength) {
|
||||
return storage.putObject(getStoragePath(ctx, strPath), buffer, contentLength);
|
||||
};
|
||||
exports.uploadObject = function(strPath, filePath) {
|
||||
return storage.uploadObject(strPath, filePath);
|
||||
exports.uploadObject = function(ctx, strPath, filePath) {
|
||||
return storage.uploadObject(getStoragePath(ctx, strPath), filePath);
|
||||
};
|
||||
exports.copyObject = function(sourceKey, destinationKey) {
|
||||
return storage.copyObject(sourceKey, destinationKey);
|
||||
exports.copyObject = function(ctx, sourceKey, destinationKey) {
|
||||
let storageSrc = getStoragePath(ctx, sourceKey);
|
||||
let storageDst = getStoragePath(ctx, destinationKey);
|
||||
return storage.copyObject(storageSrc, storageDst);
|
||||
};
|
||||
exports.copyPath = function(sourcePath, destinationPath) {
|
||||
return exports.listObjects(getStoragePath(sourcePath)).then(function(list) {
|
||||
exports.copyPath = function(ctx, sourcePath, destinationPath) {
|
||||
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 exports.copyObject(curValue, destinationPath + '/' + exports.getRelativePath(sourcePath, curValue));
|
||||
return storage.copyObject(curValue, storageDst + '/' + exports.getRelativePath(storageSrc, curValue));
|
||||
}));
|
||||
});
|
||||
};
|
||||
exports.listObjects = function(strPath) {
|
||||
return storage.listObjects(getStoragePath(strPath)).catch(function(e) {
|
||||
logger.error('storage.listObjects:\r\n%s', e.stack);
|
||||
exports.listObjects = function(ctx, strPath) {
|
||||
let prefix = getStoragePath(ctx, "");
|
||||
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 [];
|
||||
});
|
||||
};
|
||||
exports.deleteObject = function(strPath) {
|
||||
return storage.deleteObject(getStoragePath(strPath));
|
||||
exports.deleteObject = function(ctx, strPath) {
|
||||
return storage.deleteObject(getStoragePath(ctx, strPath));
|
||||
};
|
||||
exports.deleteObjects = function(strPaths) {
|
||||
exports.deleteObjects = function(ctx, strPaths) {
|
||||
var StoragePaths = strPaths.map(function(curValue) {
|
||||
return getStoragePath(curValue);
|
||||
return getStoragePath(ctx, curValue);
|
||||
});
|
||||
return storage.deleteObjects(StoragePaths);
|
||||
};
|
||||
exports.deletePath = function(strPath) {
|
||||
return exports.listObjects(getStoragePath(strPath)).then(function(list) {
|
||||
return exports.deleteObjects(list);
|
||||
exports.deletePath = function(ctx, strPath) {
|
||||
let storageSrc = getStoragePath(ctx, strPath);
|
||||
return storage.listObjects(storageSrc).then(function(list) {
|
||||
return storage.deleteObjects(list);
|
||||
});
|
||||
};
|
||||
exports.getSignedUrl = function(baseUrl, strPath, urlType, optFilename, opt_creationDate) {
|
||||
return storage.getSignedUrl(baseUrl, getStoragePath(strPath), urlType, optFilename, opt_creationDate);
|
||||
exports.getSignedUrl = function(ctx, baseUrl, strPath, urlType, optFilename, opt_creationDate) {
|
||||
return storage.getSignedUrl(baseUrl, getStoragePath(ctx, strPath), urlType, optFilename, opt_creationDate);
|
||||
};
|
||||
exports.getSignedUrls = function(baseUrl, strPath, urlType, opt_creationDate) {
|
||||
return exports.listObjects(getStoragePath(strPath)).then(function(list) {
|
||||
exports.getSignedUrls = function(ctx, baseUrl, strPath, urlType, opt_creationDate) {
|
||||
let storageSrc = getStoragePath(ctx, strPath);
|
||||
return storage.listObjects(storageSrc).then(function(list) {
|
||||
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) {
|
||||
var outputMap = {};
|
||||
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;
|
||||
});
|
||||
});
|
||||
};
|
||||
exports.getSignedUrlsArrayByArray = function(baseUrl, list, urlType) {
|
||||
return Promise.all(list.map(function(curValue) {
|
||||
return exports.getSignedUrl(baseUrl, curValue, urlType, undefined);
|
||||
exports.getSignedUrlsArrayByArray = function(ctx, baseUrl, list, urlType) {
|
||||
return Promise.all(list.map(function(curValue) {
|
||||
let storageSrc = getStoragePath(ctx, curValue);
|
||||
return storage.getSignedUrl(baseUrl, storageSrc, urlType, undefined);
|
||||
}));
|
||||
};
|
||||
exports.getSignedUrlsByArray = function(baseUrl, list, optPath, urlType) {
|
||||
return exports.getSignedUrlsArrayByArray(baseUrl, list, urlType).then(function(urls) {
|
||||
exports.getSignedUrlsByArray = function(ctx, baseUrl, list, optPath, urlType) {
|
||||
return exports.getSignedUrlsArrayByArray(ctx, baseUrl, list, urlType).then(function(urls) {
|
||||
var outputMap = {};
|
||||
for (var i = 0; i < list.length && i < urls.length; ++i) {
|
||||
if (optPath) {
|
||||
outputMap[exports.getRelativePath(optPath, list[i])] = urls[i];
|
||||
let storageSrc = getStoragePath(ctx, optPath);
|
||||
outputMap[exports.getRelativePath(storageSrc, list[i])] = urls[i];
|
||||
} else {
|
||||
outputMap[list[i]] = urls[i];
|
||||
}
|
||||
|
||||
@ -41,6 +41,7 @@ var rabbitMQCore = require('./rabbitMQCore');
|
||||
var activeMQCore = require('./activeMQCore');
|
||||
const logger = require('./logger');
|
||||
const commonDefines = require('./commondefines');
|
||||
const operationContext = require('./operationContext');
|
||||
|
||||
const cfgMaxRedeliveredCount = config.get('FileConverter.converter.maxRedeliveredCount');
|
||||
const cfgQueueType = config.get('queue.type');
|
||||
@ -253,7 +254,7 @@ function clear(taskqueue) {
|
||||
function* pushBackRedeliveredRabbit(taskqueue, message, ack) {
|
||||
if (message?.fields?.redelivered) {
|
||||
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
|
||||
let data = message.content.toString();
|
||||
let redeliveredCount = message.properties.headers['x-redelivered-count'];
|
||||
@ -264,7 +265,7 @@ function* pushBackRedeliveredRabbit(taskqueue, message, ack) {
|
||||
yield taskqueue.addResponse(taskqueue.simulateErrorResponse(data));
|
||||
}
|
||||
} catch (err) {
|
||||
logger.error('checkRedelivered error: %s', err.stack);
|
||||
operationContext.global.logger.error('checkRedelivered error: %s', err.stack);
|
||||
} finally{
|
||||
ack();
|
||||
}
|
||||
@ -274,14 +275,14 @@ function* pushBackRedeliveredRabbit(taskqueue, message, ack) {
|
||||
}
|
||||
function* pushBackRedeliveredActive(taskqueue, context, ack) {
|
||||
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) {
|
||||
try {
|
||||
if (taskqueue.simulateErrorResponse) {
|
||||
yield taskqueue.addResponse(taskqueue.simulateErrorResponse(context.message.body));
|
||||
}
|
||||
} catch (err) {
|
||||
logger.error('checkRedelivered error: %s', err.stack);
|
||||
operationContext.global.logger.error('checkRedelivered error: %s', err.stack);
|
||||
} finally {
|
||||
ack();
|
||||
}
|
||||
|
||||
138
Common/sources/tenantManager.js
Normal file
138
Common/sources/tenantManager.js
Normal 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;
|
||||
@ -72,7 +72,6 @@ var cfgTokenOutboxHeader = config.get('services.CoAuthoring.token.outbox.header'
|
||||
var cfgTokenOutboxPrefix = config.get('services.CoAuthoring.token.outbox.prefix');
|
||||
var cfgTokenOutboxAlgorithm = config.get('services.CoAuthoring.token.outbox.algorithm');
|
||||
var cfgTokenOutboxExpires = config.get('services.CoAuthoring.token.outbox.expires');
|
||||
var cfgSignatureSecretOutbox = config.get('services.CoAuthoring.secret.outbox');
|
||||
var cfgVisibilityTimeout = config.get('queue.visibilityTimeout');
|
||||
var cfgQueueRetentionPeriod = config.get('queue.retentionPeriod');
|
||||
var cfgRequestDefaults = config.get('services.CoAuthoring.requestDefaults');
|
||||
@ -261,13 +260,13 @@ function raiseErrorObj(ro, error) {
|
||||
function isRedirectResponse(response) {
|
||||
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
|
||||
const maxRedirects = (undefined !== cfgRequestDefaults.maxRedirects) ? cfgRequestDefaults.maxRedirects : 10;
|
||||
const followRedirect = (undefined !== cfgRequestDefaults.followRedirect) ? cfgRequestDefaults.followRedirect : true;
|
||||
var redirectsFollowed = 0;
|
||||
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) {
|
||||
let response = err.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)
|
||||
}
|
||||
|
||||
logger.debug('downloadUrlPromise redirectsFollowed:%d redirectTo: %s', redirectsFollowed, redirectTo);
|
||||
ctx.logger.debug('downloadUrlPromise redirectsFollowed:%d redirectTo: %s', redirectsFollowed, redirectTo);
|
||||
redirectsFollowed++;
|
||||
return doRequest(redirectTo);
|
||||
}
|
||||
@ -287,7 +286,7 @@ function downloadUrlPromise(uri, optTimeout, optLimit, opt_Authorization, opt_fi
|
||||
};
|
||||
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) {
|
||||
//IRI to URI
|
||||
uri = URI.serialize(URI.parse(uri));
|
||||
@ -323,7 +322,7 @@ function downloadUrlPromiseWithoutRedirect(uri, optTimeout, optLimit, opt_Author
|
||||
} else {
|
||||
var contentLength = response.caseless.get('content-length');
|
||||
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});
|
||||
}
|
||||
@ -654,6 +653,9 @@ function containsAllAsciiNP(str) {
|
||||
return /^[\040-\176]*$/.test(str);//non-printing characters
|
||||
}
|
||||
exports.containsAllAsciiNP = containsAllAsciiNP;
|
||||
function getDomain(hostHeader, forwardedHostHeader) {
|
||||
return forwardedHostHeader || hostHeader || 'localhost';
|
||||
};
|
||||
function getBaseUrl(protocol, hostHeader, forwardedProtoHeader, forwardedHostHeader, forwardedPrefixHeader) {
|
||||
var url = '';
|
||||
if (forwardedProtoHeader) {
|
||||
@ -664,13 +666,7 @@ function getBaseUrl(protocol, hostHeader, forwardedProtoHeader, forwardedHostHea
|
||||
url += 'http';
|
||||
}
|
||||
url += '://';
|
||||
if (forwardedHostHeader) {
|
||||
url += forwardedHostHeader;
|
||||
} else if (hostHeader) {
|
||||
url += hostHeader;
|
||||
} else {
|
||||
url += 'localhost';
|
||||
}
|
||||
url += getDomain(hostHeader, forwardedHostHeader);
|
||||
if (forwardedPrefixHeader) {
|
||||
url += forwardedPrefixHeader;
|
||||
}
|
||||
@ -684,6 +680,20 @@ function getBaseUrlByRequest(req) {
|
||||
}
|
||||
exports.getBaseUrlByConnection = getBaseUrlByConnection;
|
||||
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) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
if (!stream.readable) {
|
||||
@ -761,14 +771,14 @@ function checkIpFilter(ipString, opt_hostname) {
|
||||
return status;
|
||||
}
|
||||
exports.checkIpFilter = checkIpFilter;
|
||||
function* checkHostFilter(hostname) {
|
||||
function* checkHostFilter(ctx, hostname) {
|
||||
let status = 0;
|
||||
let hostIp;
|
||||
try {
|
||||
hostIp = yield dnsLookup(hostname);
|
||||
} catch (e) {
|
||||
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) {
|
||||
status = checkIpFilter(hostIp, hostname);
|
||||
@ -832,29 +842,7 @@ function getSecretByElem(secretElem) {
|
||||
return secret;
|
||||
}
|
||||
exports.getSecretByElem = getSecretByElem;
|
||||
function getSecret(docId, secretElem, opt_iss, opt_token) {
|
||||
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) {
|
||||
function fillJwtForRequest(payload, secret, opt_inBody) {
|
||||
//todo refuse prototypes in payload(they are simple getter/setter).
|
||||
//JSON.parse/stringify is more universal but Object.assign is enough for our inputs
|
||||
payload = Object.assign(Object.create(null), payload);
|
||||
@ -866,7 +854,6 @@ function fillJwtForRequest(payload, opt_inBody) {
|
||||
}
|
||||
|
||||
let options = {algorithm: cfgTokenOutboxAlgorithm, expiresIn: cfgTokenOutboxExpires};
|
||||
let secret = getSecretByElem(cfgSignatureSecretOutbox);
|
||||
return jwt.sign(data, secret, options);
|
||||
}
|
||||
exports.fillJwtForRequest = fillJwtForRequest;
|
||||
@ -874,13 +861,13 @@ exports.forwarded = forwarded;
|
||||
exports.getIndexFromUserId = function(userId, userIdOriginal){
|
||||
return parseInt(userId.substring(userIdOriginal.length));
|
||||
};
|
||||
exports.checkPathTraversal = function(docId, rootDirectory, filename) {
|
||||
exports.checkPathTraversal = function(ctx, docId, rootDirectory, filename) {
|
||||
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;
|
||||
}
|
||||
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 true;
|
||||
@ -905,14 +892,14 @@ exports.getConnectionInfoStr = function(conn){
|
||||
exports.isLiveViewer = function(conn){
|
||||
return conn.user?.view && "fast" === conn.coEditingMode;
|
||||
};
|
||||
exports.canIncludeOutboxAuthorization = function (url) {
|
||||
exports.canIncludeOutboxAuthorization = function (ctx, url) {
|
||||
if (cfgTokenEnableRequestOutbox) {
|
||||
if (!outboxUrlExclusionRegex) {
|
||||
return true;
|
||||
} else if (!outboxUrlExclusionRegex.test(url)) {
|
||||
return true;
|
||||
} 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;
|
||||
@ -981,8 +968,7 @@ exports.checkBaseUrl = function(baseUrl) {
|
||||
};
|
||||
exports.resolvePath = function(object, path, defaultValue) {
|
||||
return path.split('.').reduce((o, p) => o ? o[p] : defaultValue, object);
|
||||
}
|
||||
|
||||
};
|
||||
Date.isLeapYear = function (year) {
|
||||
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);
|
||||
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
@ -40,7 +40,6 @@ var sqlDataBaseType = {
|
||||
|
||||
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 logger = require('./../../Common/sources/logger');
|
||||
let constants = require('./../../Common/sources/constants');
|
||||
|
||||
const tableChanges = config.get('tableChanges'),
|
||||
@ -52,9 +51,9 @@ let addSqlParam = baseConnector.addSqlParameter;
|
||||
var maxPacketSize = config.get('max_allowed_packet'); // Размер по умолчанию для запроса в базу данных 1Mb - 1 (т.к. он не пишет 1048575, а пишет 1048574)
|
||||
|
||||
exports.baseConnector = baseConnector;
|
||||
exports.insertChangesPromiseCompatibility = function (objChanges, docId, index, user) {
|
||||
exports.insertChangesPromiseCompatibility = function (ctx, objChanges, docId, index, user) {
|
||||
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) {
|
||||
reject(error);
|
||||
} 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) {
|
||||
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;
|
||||
if (error) {
|
||||
if (!isSupportFastInsert) {
|
||||
resolve(exports.insertChangesPromiseCompatibility(objChanges, docId, index, user));
|
||||
resolve(exports.insertChangesPromiseCompatibility(ctx, objChanges, docId, index, user));
|
||||
} else {
|
||||
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) {
|
||||
return exports.insertChangesPromiseFast(objChanges, docId, index, user);
|
||||
return exports.insertChangesPromiseFast(ctx, objChanges, docId, index, user);
|
||||
} 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;
|
||||
|
||||
function _insertChangesCallback (startIndex, objChanges, docId, index, user, callback) {
|
||||
function _insertChangesCallback (ctx, startIndex, objChanges, docId, index, user, callback) {
|
||||
var sqlCommand = `INSERT INTO ${tableChanges} VALUES`;
|
||||
var i = startIndex, l = objChanges.length, lengthUtf8Current = sqlCommand.length, lengthUtf8Row = 0, values = [];
|
||||
if (i === l)
|
||||
@ -106,13 +105,14 @@ function _insertChangesCallback (startIndex, objChanges, docId, index, user, cal
|
||||
if (lengthUtf8Row + lengthUtf8Current >= maxPacketSize && i > startIndex) {
|
||||
sqlCommand += ';';
|
||||
(function(tmpStart, tmpIndex) {
|
||||
baseConnector.sqlQuery(sqlCommand, function() {
|
||||
baseConnector.sqlQuery(ctx, sqlCommand, function() {
|
||||
// lock не снимаем, а продолжаем добавлять
|
||||
_insertChangesCallback(tmpStart, objChanges, docId, tmpIndex, user, callback);
|
||||
_insertChangesCallback(ctx, tmpStart, objChanges, docId, tmpIndex, user, callback);
|
||||
}, undefined, undefined, values);
|
||||
})(i, index);
|
||||
return;
|
||||
}
|
||||
let p0 = addSqlParam(ctx.tenant, values);
|
||||
let p1 = addSqlParam(docId, values);
|
||||
let p2 = addSqlParam(index, values);
|
||||
let p3 = addSqlParam(user.id, values);
|
||||
@ -123,27 +123,28 @@ function _insertChangesCallback (startIndex, objChanges, docId, index, user, cal
|
||||
if (i > startIndex) {
|
||||
sqlCommand += ',';
|
||||
}
|
||||
sqlCommand += `(${p1},${p2},${p3},${p4},${p5},${p6},${p7})`;
|
||||
sqlCommand += `(${p0},${p1},${p2},${p3},${p4},${p5},${p6},${p7})`;
|
||||
lengthUtf8Current += lengthUtf8Row;
|
||||
}
|
||||
|
||||
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 sqlParam1 = addSqlParam(docId, values);
|
||||
let p1 = addSqlParam(ctx.tenant, values);
|
||||
let p2 = addSqlParam(docId, values);
|
||||
if (null !== deleteIndex) {
|
||||
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 {
|
||||
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) {
|
||||
exports.deleteChangesCallback(docId, deleteIndex, function(error, result) {
|
||||
exports.deleteChangesCallback(ctx, docId, deleteIndex, function(error, result) {
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
@ -152,21 +153,22 @@ exports.deleteChangesPromise = function (docId, deleteIndex) {
|
||||
});
|
||||
});
|
||||
};
|
||||
exports.deleteChanges = function (docId, deleteIndex) {
|
||||
lockCriticalSection(docId, function () {_deleteChanges(docId, deleteIndex);});
|
||||
exports.deleteChanges = function (ctx, docId, deleteIndex) {
|
||||
lockCriticalSection(docId, function () {_deleteChanges(ctx, docId, deleteIndex);});
|
||||
};
|
||||
function _deleteChanges (docId, deleteIndex) {
|
||||
exports.deleteChangesCallback(docId, deleteIndex, function () {unLockCriticalSection(docId);});
|
||||
function _deleteChanges (ctx, docId, deleteIndex) {
|
||||
exports.deleteChangesCallback(ctx, docId, deleteIndex, function () {unLockCriticalSection(docId);});
|
||||
}
|
||||
exports.getChangesIndex = function(docId, callback) {
|
||||
exports.getChangesIndex = function(ctx, docId, callback) {
|
||||
let values = [];
|
||||
let sqlParam = addSqlParam(docId, values);
|
||||
var sqlCommand = `SELECT MAX(change_id) as change_id FROM ${tableChanges} WHERE id=${sqlParam};`;
|
||||
baseConnector.sqlQuery(sqlCommand, callback, undefined, undefined, values);
|
||||
let p1 = addSqlParam(ctx.tenant, values);
|
||||
let p2 = addSqlParam(docId, 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) {
|
||||
exports.getChangesIndex(docId, function(error, result) {
|
||||
exports.getChangesIndex(ctx, docId, function(error, result) {
|
||||
if (error) {
|
||||
reject(error);
|
||||
} 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) {
|
||||
let values = [];
|
||||
let sqlParam = addSqlParam(docId, values);
|
||||
let sqlWhere = `id=${sqlParam}`;
|
||||
let sqlParam = addSqlParam(ctx.tenant, values);
|
||||
let sqlWhere = `tenant=${sqlParam}`;
|
||||
sqlParam = addSqlParam(docId, values);
|
||||
sqlWhere += ` AND id=${sqlParam}`;
|
||||
if (null != optStartIndex) {
|
||||
sqlParam = addSqlParam(optStartIndex, values);
|
||||
sqlWhere += ` AND change_id>=${sqlParam}`;
|
||||
@ -198,7 +202,7 @@ exports.getChangesPromise = function (docId, optStartIndex, optEndIndex, opt_tim
|
||||
sqlWhere += ' ORDER BY change_id ASC';
|
||||
var sqlCommand = `SELECT * FROM ${tableChanges} WHERE ${sqlWhere};`;
|
||||
|
||||
baseConnector.sqlQuery(sqlCommand, function(error, result) {
|
||||
baseConnector.sqlQuery(ctx, sqlCommand, function(error, result) {
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
@ -207,23 +211,6 @@ exports.getChangesPromise = function (docId, optStartIndex, optEndIndex, opt_tim
|
||||
}, 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) {
|
||||
return !!(g_oCriticalSection[id]);
|
||||
@ -249,11 +236,11 @@ function unLockCriticalSection (id) {
|
||||
else
|
||||
delete g_oCriticalSection[id];
|
||||
}
|
||||
exports.healthCheck = function () {
|
||||
exports.healthCheck = function (ctx) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
//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
|
||||
baseConnector.sqlQuery('SELECT 1;', function(error, result) {
|
||||
baseConnector.sqlQuery(ctx, 'SELECT 1;', function(error, result) {
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
@ -263,10 +250,22 @@ exports.healthCheck = function () {
|
||||
});
|
||||
};
|
||||
|
||||
exports.getEmptyCallbacks = function() {
|
||||
exports.getEmptyCallbacks = function(ctx) {
|
||||
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 = '';";
|
||||
baseConnector.sqlQuery(sqlCommand, function(error, result) {
|
||||
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(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) {
|
||||
reject(error);
|
||||
} else {
|
||||
@ -291,8 +290,8 @@ UserCallback.prototype.delimiter = constants.CHAR_DELIMITER;
|
||||
UserCallback.prototype.toSQLInsert = function(){
|
||||
return this.delimiter + JSON.stringify(this);
|
||||
};
|
||||
UserCallback.prototype.getCallbackByUserIndex = function(docId, callbacksStr, opt_userIndex) {
|
||||
logger.debug("getCallbackByUserIndex: docId = %s userIndex = %s callbacks = %s", docId, opt_userIndex, callbacksStr);
|
||||
UserCallback.prototype.getCallbackByUserIndex = function(ctx, callbacksStr, opt_userIndex) {
|
||||
ctx.logger.debug("getCallbackByUserIndex: userIndex = %s callbacks = %s", opt_userIndex, callbacksStr);
|
||||
if (!callbacksStr || !callbacksStr.startsWith(UserCallback.prototype.delimiter)) {
|
||||
let index = callbacksStr.indexOf(UserCallback.prototype.delimiter);
|
||||
if (-1 === index) {
|
||||
@ -314,8 +313,8 @@ UserCallback.prototype.getCallbackByUserIndex = function(docId, callbacksStr, op
|
||||
}
|
||||
return callbackUrl;
|
||||
};
|
||||
UserCallback.prototype.getCallbacks = function(docId, callbacksStr) {
|
||||
logger.debug("getCallbacks: docId = %s callbacks = %s", docId, callbacksStr);
|
||||
UserCallback.prototype.getCallbacks = function(ctx, callbacksStr) {
|
||||
ctx.logger.debug("getCallbacks: callbacks = %s", callbacksStr);
|
||||
if (!callbacksStr || !callbacksStr.startsWith(UserCallback.prototype.delimiter)) {
|
||||
let index = callbacksStr.indexOf(UserCallback.prototype.delimiter);
|
||||
if (-1 === index) {
|
||||
@ -359,10 +358,10 @@ DocumentPassword.prototype.toSQLInsert = function(){
|
||||
DocumentPassword.prototype.isInitial = function(){
|
||||
return !this.change;
|
||||
};
|
||||
DocumentPassword.prototype.getDocPassword = function(docId, docPasswordStr) {
|
||||
DocumentPassword.prototype.getDocPassword = function(ctx, docPasswordStr) {
|
||||
let res = {initial: undefined, current: undefined, change: undefined};
|
||||
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);
|
||||
|
||||
for (let i = 1; i < passwords.length; ++i) {
|
||||
@ -378,12 +377,12 @@ DocumentPassword.prototype.getDocPassword = function(docId, docPasswordStr) {
|
||||
}
|
||||
return res;
|
||||
};
|
||||
DocumentPassword.prototype.getCurPassword = function(docId, docPasswordStr) {
|
||||
let docPassword = this.getDocPassword(docId, docPasswordStr);
|
||||
DocumentPassword.prototype.getCurPassword = function(ctx, docPasswordStr) {
|
||||
let docPassword = this.getDocPassword(ctx, docPasswordStr);
|
||||
return docPassword.current;
|
||||
};
|
||||
DocumentPassword.prototype.hasPasswordChanges = function(docId, docPasswordStr) {
|
||||
let docPassword = this.getDocPassword(docId, docPasswordStr);
|
||||
DocumentPassword.prototype.hasPasswordChanges = function(ctx, docPasswordStr) {
|
||||
let docPassword = this.getDocPassword(ctx, docPasswordStr);
|
||||
return docPassword.initial !== docPassword.current;
|
||||
};
|
||||
exports.DocumentPassword = DocumentPassword;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -37,7 +37,6 @@ var config = require('config');
|
||||
var co = require('co');
|
||||
const locale = require('windows-locale');
|
||||
var taskResult = require('./taskresult');
|
||||
var logger = require('./../../Common/sources/logger');
|
||||
var utils = require('./../../Common/sources/utils');
|
||||
var constants = require('./../../Common/sources/constants');
|
||||
var commonDefines = require('./../../Common/sources/commondefines');
|
||||
@ -47,18 +46,19 @@ var storage = require('./../../Common/sources/storage-base');
|
||||
var formatChecker = require('./../../Common/sources/formatchecker');
|
||||
var statsDClient = require('./../../Common/sources/statsdclient');
|
||||
var storageBase = require('./../../Common/sources/storage-base');
|
||||
var operationContext = require('./../../Common/sources/operationContext');
|
||||
const sqlBase = require('./baseConnector');
|
||||
|
||||
var CONVERT_ASYNC_DELAY = 1000;
|
||||
|
||||
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);
|
||||
if (selectRes.length > 0) {
|
||||
var docId = cmd.getDocId();
|
||||
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) {
|
||||
case taskResult.FileStatus.Ok:
|
||||
if (password) {
|
||||
@ -70,10 +70,10 @@ function* getConvertStatus(cmd, selectRes, opt_checkPassword) {
|
||||
isCorrectPassword = decryptedPassword === userPassword;
|
||||
}
|
||||
if (isCorrectPassword) {
|
||||
logger.debug("getConvertStatus password match: docId = %s", docId);
|
||||
ctx.logger.debug("getConvertStatus password match");
|
||||
status.end = true;
|
||||
} else {
|
||||
logger.debug("getConvertStatus password mismatch: docId = %s", docId);
|
||||
ctx.logger.debug("getConvertStatus password mismatch");
|
||||
status.err = constants.CONVERT_PASSWORD;
|
||||
}
|
||||
} else {
|
||||
@ -85,7 +85,7 @@ function* getConvertStatus(cmd, selectRes, opt_checkPassword) {
|
||||
case taskResult.FileStatus.NeedPassword:
|
||||
status.err = row.status_info;
|
||||
if (taskResult.FileStatus.ErrToReload == row.status || taskResult.FileStatus.NeedPassword == row.status) {
|
||||
yield canvasService.cleanupCache(docId);
|
||||
yield canvasService.cleanupCache(ctx);
|
||||
}
|
||||
break;
|
||||
case taskResult.FileStatus.NeedParams:
|
||||
@ -103,9 +103,9 @@ function* getConvertStatus(cmd, selectRes, opt_checkPassword) {
|
||||
}
|
||||
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) {
|
||||
let list = yield storage.listObjects(docId);
|
||||
let list = yield storage.listObjects(ctx, docId);
|
||||
let baseName = path.basename(fileTo, path.extname(fileTo));
|
||||
for (let i = 0; i < list.length; ++i) {
|
||||
if (path.basename(list[i], path.extname(list[i])) === baseName) {
|
||||
@ -115,28 +115,29 @@ function* getConvertPath(docId, fileTo, formatTo) {
|
||||
}
|
||||
return docId + '/' + fileTo;
|
||||
}
|
||||
function* getConvertUrl(baseUrl, fileToPath, title) {
|
||||
function* getConvertUrl(ctx, baseUrl, fileToPath, title) {
|
||||
if (title) {
|
||||
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 startDate = null;
|
||||
if (clientStatsD) {
|
||||
startDate = new Date();
|
||||
}
|
||||
logger.debug('Start convert request docId = %s', docId);
|
||||
ctx.logger.debug('Start convert request');
|
||||
|
||||
let bCreate = false;
|
||||
if (!opt_taskExist) {
|
||||
let task = new taskResult.TaskResultData();
|
||||
task.tenant = ctx.tenant;
|
||||
task.key = docId;
|
||||
task.status = taskResult.FileStatus.WaitQueue;
|
||||
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
|
||||
//http://dev.mysql.com/doc/refman/5.7/en/insert-on-duplicate.html
|
||||
bCreate = upsertRes.affectedRows == 1;
|
||||
@ -144,10 +145,11 @@ function* convertByCmd(cmd, async, opt_fileTo, opt_taskExist, opt_priority, opt_
|
||||
var selectRes;
|
||||
var status;
|
||||
if (!bCreate) {
|
||||
selectRes = yield taskResult.select(docId);
|
||||
status = yield* getConvertStatus(cmd, selectRes, opt_checkPassword);
|
||||
selectRes = yield taskResult.select(ctx, docId);
|
||||
status = yield* getConvertStatus(ctx, cmd, selectRes, opt_checkPassword);
|
||||
} else {
|
||||
var queueData = new commonDefines.TaskQueueData();
|
||||
queueData.setCtx(ctx);
|
||||
queueData.setCmd(cmd);
|
||||
if (opt_fileTo) {
|
||||
queueData.setToFile(opt_fileTo);
|
||||
@ -165,22 +167,22 @@ function* convertByCmd(cmd, async, opt_fileTo, opt_taskExist, opt_priority, opt_
|
||||
break;
|
||||
}
|
||||
yield utils.sleep(CONVERT_ASYNC_DELAY);
|
||||
selectRes = yield taskResult.select(docId);
|
||||
status = yield* getConvertStatus(cmd, selectRes, opt_checkPassword);
|
||||
selectRes = yield taskResult.select(ctx, docId);
|
||||
status = yield* getConvertStatus(ctx, cmd, selectRes, opt_checkPassword);
|
||||
waitTime += CONVERT_ASYNC_DELAY;
|
||||
if (waitTime > utils.CONVERTION_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) {
|
||||
clientStatsD.timing('coauth.convertservice', new Date() - startDate);
|
||||
}
|
||||
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) {
|
||||
var cmd = new commonDefines.InputCommand();
|
||||
cmd.setCommand('sfcm');
|
||||
@ -204,50 +206,56 @@ function* convertFromChanges(docId, baseUrl, forceSave, externalChangeInfo, opt_
|
||||
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;
|
||||
let outputExt = formatChecker.getStringFromFormat(cmd.getOutputFormat());
|
||||
if (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) {
|
||||
let fileToPath = yield* getConvertPath(docId, fileTo, cmd.getOutputFormat());
|
||||
let fileToPath = yield* getConvertPath(ctx, docId, fileTo, cmd.getOutputFormat());
|
||||
status.setExtName(path.extname(fileToPath));
|
||||
status.setUrl(yield* getConvertUrl(baseUrl, fileToPath, cmd.getTitle()));
|
||||
status.setUrl(yield* getConvertUrl(ctx, baseUrl, fileToPath, cmd.getTitle()));
|
||||
}
|
||||
return status;
|
||||
}
|
||||
});
|
||||
function parseIntParam(val){
|
||||
return (typeof val === 'string') ? parseInt(val) : val;
|
||||
}
|
||||
|
||||
function convertRequest(req, res, isJson) {
|
||||
return co(function* () {
|
||||
var docId = 'convertRequest';
|
||||
let ctx = new operationContext.Context();
|
||||
try {
|
||||
ctx.initFromRequest(req);
|
||||
ctx.logger.info('convertRequest start');
|
||||
let params;
|
||||
let authRes = docsCoServer.getRequestParams(docId, req);
|
||||
let authRes = yield docsCoServer.getRequestParams(ctx, req);
|
||||
if(authRes.code === constants.NO_ERROR){
|
||||
params = authRes.params;
|
||||
} else {
|
||||
ctx.logger.warn('convertRequest auth failed %j', authRes);
|
||||
utils.fillResponse(req, res, new commonDefines.ConvertStatus(authRes.code), isJson);
|
||||
return;
|
||||
}
|
||||
let outputtype = params.outputtype || '';
|
||||
let docId = 'conv_' + params.key + '_' + outputtype;
|
||||
ctx.setDocId(docId);
|
||||
|
||||
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);
|
||||
return;
|
||||
}
|
||||
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);
|
||||
return;
|
||||
}
|
||||
let outputtype = params.outputtype || '';
|
||||
let outputFormat = formatChecker.getFormatFromString(outputtype);
|
||||
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);
|
||||
return;
|
||||
}
|
||||
@ -256,7 +264,6 @@ function convertRequest(req, res, isJson) {
|
||||
cmd.setUrl(params.url);
|
||||
cmd.setEmbeddedFonts(false);//params.embeddedfonts'];
|
||||
cmd.setFormat(params.filetype);
|
||||
docId = 'conv_' + params.key + '_' + outputtype;
|
||||
cmd.setDocId(docId);
|
||||
cmd.setOutputFormat(outputFormat);
|
||||
|
||||
@ -275,7 +282,7 @@ function convertRequest(req, res, isJson) {
|
||||
}
|
||||
if (params.password) {
|
||||
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);
|
||||
return;
|
||||
}
|
||||
@ -340,23 +347,24 @@ function convertRequest(req, res, isJson) {
|
||||
|
||||
if (constants.AVS_OFFICESTUDIO_FILE_UNKNOWN !== cmd.getOutputFormat()) {
|
||||
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) {
|
||||
let fileToPath = yield* getConvertPath(docId, fileTo, cmd.getOutputFormat());
|
||||
let fileToPath = yield* getConvertPath(ctx, docId, fileTo, cmd.getOutputFormat());
|
||||
status.setExtName(path.extname(fileToPath));
|
||||
status.setUrl(yield* getConvertUrl(utils.getBaseUrlByRequest(req), fileToPath, cmd.getTitle()));
|
||||
logger.debug('convertRequest: url = %s docId = %s', status.url, docId);
|
||||
status.setUrl(yield* getConvertUrl(ctx, utils.getBaseUrlByRequest(req), fileToPath, cmd.getTitle()));
|
||||
ctx.logger.debug('convertRequest: url = %s', status.url);
|
||||
}
|
||||
utils.fillResponse(req, res, status, isJson);
|
||||
} else {
|
||||
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);
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
logger.error('Error convert: docId = %s\r\n%s', docId, e.stack);
|
||||
} catch (e) {
|
||||
ctx.logger.error('convertRequest error: %s', e.stack);
|
||||
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) {
|
||||
return co(function* () {
|
||||
let docId = 'builderRequest';
|
||||
let ctx = new operationContext.Context();
|
||||
try {
|
||||
ctx.initFromRequest(req);
|
||||
ctx.logger.info('builderRequest start');
|
||||
let authRes;
|
||||
if (!utils.isEmptyObject(req.query)) {
|
||||
//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 {
|
||||
authRes = docsCoServer.getRequestParams(docId, req);
|
||||
authRes = yield docsCoServer.getRequestParams(ctx, req);
|
||||
}
|
||||
|
||||
let params = authRes.params;
|
||||
let docId = params.key;
|
||||
ctx.setDocId(docId);
|
||||
|
||||
let error = authRes.code;
|
||||
let urls;
|
||||
let end = false;
|
||||
if (error === constants.NO_ERROR &&
|
||||
(params.key || params.url || (req.body && Buffer.isBuffer(req.body) && req.body.length > 0))) {
|
||||
docId = params.key;
|
||||
let cmd = new commonDefines.InputCommand();
|
||||
cmd.setCommand('builder');
|
||||
cmd.setIsBuilder(true);
|
||||
cmd.setWithAuthorization(true);
|
||||
cmd.setDocId(docId);
|
||||
if (!docId) {
|
||||
let task = yield* taskResult.addRandomKeyTask(undefined, 'bld_', 8);
|
||||
let task = yield* taskResult.addRandomKeyTask(ctx, undefined, 'bld_', 8);
|
||||
docId = task.key;
|
||||
cmd.setDocId(docId);
|
||||
if (params.url) {
|
||||
cmd.setUrl(params.url);
|
||||
cmd.setFormat('docbuilder');
|
||||
} 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();
|
||||
queueData.setCtx(ctx);
|
||||
queueData.setCmd(cmd);
|
||||
yield* docsCoServer.addTask(queueData, constants.QUEUE_PRIORITY_LOW);
|
||||
}
|
||||
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;
|
||||
error = status.err;
|
||||
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);
|
||||
}
|
||||
} else if (error === constants.NO_ERROR) {
|
||||
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);
|
||||
}
|
||||
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);
|
||||
} finally {
|
||||
ctx.logger.info('builderRequest end');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -35,6 +35,7 @@ const config = require('config');
|
||||
const ms = require('ms');
|
||||
const utils = require('./../../Common/sources/utils');
|
||||
const commonDefines = require('./../../Common/sources/commondefines');
|
||||
const tenantManager = require('./../../Common/sources/tenantManager');
|
||||
|
||||
const cfgExpMonthUniqueUsers = ms(config.get('services.CoAuthoring.expire.monthUniqueUsers'));
|
||||
|
||||
@ -46,18 +47,22 @@ function EditorData() {
|
||||
this.uniqueViewUser = {};
|
||||
this.uniqueViewUsersOfMonth = {};
|
||||
this.shutdown = {};
|
||||
this.stat = [];
|
||||
this.stat = {};
|
||||
}
|
||||
|
||||
EditorData.prototype._getDocumentData = function(docId) {
|
||||
let options = this.data[docId];
|
||||
EditorData.prototype._getDocumentData = function(ctx, docId) {
|
||||
let tenantData = this.data[ctx.tenant];
|
||||
if (!tenantData) {
|
||||
this.data[ctx.tenant] = tenantData = {};
|
||||
}
|
||||
let options = tenantData[docId];
|
||||
if (!options) {
|
||||
this.data[docId] = options = {};
|
||||
tenantData[docId] = options = {};
|
||||
}
|
||||
return options;
|
||||
};
|
||||
EditorData.prototype._checkAndLock = function(name, docId, fencingToken, ttl) {
|
||||
let data = this._getDocumentData(docId);
|
||||
EditorData.prototype._checkAndLock = function(ctx, name, docId, fencingToken, ttl) {
|
||||
let data = this._getDocumentData(ctx, docId);
|
||||
const now = Date.now();
|
||||
let res = true;
|
||||
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);
|
||||
};
|
||||
EditorData.prototype._checkAndUnlock = function(name, docId, fencingToken) {
|
||||
let data = this._getDocumentData(docId);
|
||||
EditorData.prototype._checkAndUnlock = function(ctx, name, docId, fencingToken) {
|
||||
let data = this._getDocumentData(ctx, docId);
|
||||
const now = Date.now();
|
||||
let res;
|
||||
if (data[name] && now < data[name].expireAt) {
|
||||
@ -86,100 +91,101 @@ EditorData.prototype._checkAndUnlock = function(name, docId, fencingToken) {
|
||||
return Promise.resolve(res);
|
||||
};
|
||||
|
||||
EditorData.prototype.addPresence = function(docId, userId, userInfo) {
|
||||
EditorData.prototype.addPresence = function(ctx, docId, userId, userInfo) {
|
||||
return Promise.resolve();
|
||||
};
|
||||
EditorData.prototype.removePresence = function(docId, userId) {
|
||||
EditorData.prototype.removePresence = function(ctx, docId, userId) {
|
||||
return Promise.resolve();
|
||||
};
|
||||
EditorData.prototype.getPresence = function(docId, connections) {
|
||||
EditorData.prototype.getPresence = function(ctx, docId, connections) {
|
||||
let hvals = [];
|
||||
for (let i = 0; i < connections.length; ++i) {
|
||||
if (connections[i].docId === docId) {
|
||||
hvals.push(utils.getConnectionInfoStr(connections[i]));
|
||||
let conn = connections[i];
|
||||
if (conn.docId === docId && ctx.tenant === tenantManager.getTenantByConnection(ctx, conn)) {
|
||||
hvals.push(utils.getConnectionInfoStr(conn));
|
||||
}
|
||||
}
|
||||
return Promise.resolve(hvals);
|
||||
};
|
||||
|
||||
EditorData.prototype.lockSave = function(docId, userId, ttl) {
|
||||
return this._checkAndLock('lockSave', docId, userId, ttl);
|
||||
EditorData.prototype.lockSave = function(ctx, docId, userId, ttl) {
|
||||
return this._checkAndLock(ctx, 'lockSave', docId, userId, ttl);
|
||||
};
|
||||
EditorData.prototype.unlockSave = function(docId, userId) {
|
||||
return this._checkAndUnlock('lockSave', docId, userId);
|
||||
EditorData.prototype.unlockSave = function(ctx, docId, userId) {
|
||||
return this._checkAndUnlock(ctx, 'lockSave', docId, userId);
|
||||
};
|
||||
EditorData.prototype.lockAuth = function(docId, userId, ttl) {
|
||||
return this._checkAndLock('lockAuth', docId, userId, ttl);
|
||||
EditorData.prototype.lockAuth = function(ctx, docId, userId, ttl) {
|
||||
return this._checkAndLock(ctx, 'lockAuth', docId, userId, ttl);
|
||||
};
|
||||
EditorData.prototype.unlockAuth = function(docId, userId) {
|
||||
return this._checkAndUnlock('lockAuth', docId, userId);
|
||||
EditorData.prototype.unlockAuth = function(ctx, docId, userId) {
|
||||
return this._checkAndUnlock(ctx, 'lockAuth', docId, userId);
|
||||
};
|
||||
|
||||
EditorData.prototype.getDocumentPresenceExpired = function(now) {
|
||||
return Promise.resolve([]);
|
||||
};
|
||||
EditorData.prototype.removePresenceDocument = function(docId) {
|
||||
EditorData.prototype.removePresenceDocument = function(ctx, docId) {
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
EditorData.prototype.addLocks = function(docId, locks) {
|
||||
let data = this._getDocumentData(docId);
|
||||
EditorData.prototype.addLocks = function(ctx, docId, locks) {
|
||||
let data = this._getDocumentData(ctx, docId);
|
||||
if (!data.locks) {
|
||||
data.locks = [];
|
||||
}
|
||||
data.locks = data.locks.concat(locks);
|
||||
return Promise.resolve();
|
||||
};
|
||||
EditorData.prototype.removeLocks = function(docId) {
|
||||
let data = this._getDocumentData(docId);
|
||||
EditorData.prototype.removeLocks = function(ctx, docId) {
|
||||
let data = this._getDocumentData(ctx, docId);
|
||||
data.locks = undefined;
|
||||
return Promise.resolve();
|
||||
};
|
||||
EditorData.prototype.getLocks = function(docId) {
|
||||
let data = this._getDocumentData(docId);
|
||||
EditorData.prototype.getLocks = function(ctx, docId) {
|
||||
let data = this._getDocumentData(ctx, docId);
|
||||
return Promise.resolve(data.locks || []);
|
||||
};
|
||||
|
||||
EditorData.prototype.addMessage = function(docId, msg) {
|
||||
let data = this._getDocumentData(docId);
|
||||
EditorData.prototype.addMessage = function(ctx, docId, msg) {
|
||||
let data = this._getDocumentData(ctx, docId);
|
||||
if (!data.messages) {
|
||||
data.messages = [];
|
||||
}
|
||||
data.messages.push(msg);
|
||||
return Promise.resolve();
|
||||
};
|
||||
EditorData.prototype.removeMessages = function(docId) {
|
||||
let data = this._getDocumentData(docId);
|
||||
EditorData.prototype.removeMessages = function(ctx, docId) {
|
||||
let data = this._getDocumentData(ctx, docId);
|
||||
data.messages = undefined;
|
||||
return Promise.resolve();
|
||||
};
|
||||
EditorData.prototype.getMessages = function(docId) {
|
||||
let data = this._getDocumentData(docId);
|
||||
EditorData.prototype.getMessages = function(ctx, docId) {
|
||||
let data = this._getDocumentData(ctx, docId);
|
||||
return Promise.resolve(data.messages || []);
|
||||
};
|
||||
|
||||
EditorData.prototype.setSaved = function(docId, status) {
|
||||
let data = this._getDocumentData(docId);
|
||||
EditorData.prototype.setSaved = function(ctx, docId, status) {
|
||||
let data = this._getDocumentData(ctx, docId);
|
||||
data.saved = status;
|
||||
return Promise.resolve();
|
||||
};
|
||||
EditorData.prototype.getdelSaved = function(docId) {
|
||||
let data = this._getDocumentData(docId);
|
||||
EditorData.prototype.getdelSaved = function(ctx, docId) {
|
||||
let data = this._getDocumentData(ctx, docId);
|
||||
let res = data.saved;
|
||||
data.saved = undefined;
|
||||
return Promise.resolve(res);
|
||||
};
|
||||
EditorData.prototype.setForceSave = function(docId, time, index, baseUrl, changeInfo) {
|
||||
let data = this._getDocumentData(docId);
|
||||
EditorData.prototype.setForceSave = function(ctx, docId, time, index, baseUrl, changeInfo) {
|
||||
let data = this._getDocumentData(ctx, docId);
|
||||
data.forceSave = {time: time, index: index, baseUrl: baseUrl, changeInfo: changeInfo, started: false, ended: false};
|
||||
return Promise.resolve();
|
||||
};
|
||||
EditorData.prototype.getForceSave = function(docId) {
|
||||
let data = this._getDocumentData(docId);
|
||||
EditorData.prototype.getForceSave = function(ctx, docId) {
|
||||
let data = this._getDocumentData(ctx, docId);
|
||||
return Promise.resolve(data.forceSave || null);
|
||||
};
|
||||
EditorData.prototype.checkAndStartForceSave = function(docId) {
|
||||
let data = this._getDocumentData(docId);
|
||||
EditorData.prototype.checkAndStartForceSave = function(ctx, docId) {
|
||||
let data = this._getDocumentData(ctx, docId);
|
||||
let res;
|
||||
if (data.forceSave && !data.forceSave.started) {
|
||||
data.forceSave.started = true;
|
||||
@ -188,8 +194,8 @@ EditorData.prototype.checkAndStartForceSave = function(docId) {
|
||||
}
|
||||
return Promise.resolve(res);
|
||||
};
|
||||
EditorData.prototype.checkAndSetForceSave = function(docId, time, index, started, ended) {
|
||||
let data = this._getDocumentData(docId);
|
||||
EditorData.prototype.checkAndSetForceSave = function(ctx, docId, time, index, started, ended) {
|
||||
let data = this._getDocumentData(ctx, docId);
|
||||
let res;
|
||||
if (data.forceSave && time === data.forceSave.time && index === data.forceSave.index) {
|
||||
data.forceSave.started = started;
|
||||
@ -198,180 +204,235 @@ EditorData.prototype.checkAndSetForceSave = function(docId, time, index, started
|
||||
}
|
||||
return Promise.resolve(res);
|
||||
};
|
||||
EditorData.prototype.removeForceSave = function(docId) {
|
||||
let data = this._getDocumentData(docId);
|
||||
EditorData.prototype.removeForceSave = function(ctx, docId) {
|
||||
let data = this._getDocumentData(ctx, docId);
|
||||
data.forceSave = undefined;
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
EditorData.prototype.cleanDocumentOnExit = function(docId) {
|
||||
delete this.data[docId];
|
||||
delete this.forceSaveTimer[docId];
|
||||
EditorData.prototype.cleanDocumentOnExit = function(ctx, docId) {
|
||||
let tenantData = this.data[ctx.tenant];
|
||||
if (tenantData) {
|
||||
delete tenantData[docId];
|
||||
}
|
||||
let tenantTimer = this.forceSaveTimer[ctx.tenant];
|
||||
if (tenantTimer) {
|
||||
delete tenantTimer[docId];
|
||||
}
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
EditorData.prototype.addForceSaveTimerNX = function(docId, expireAt) {
|
||||
if (!this.forceSaveTimer[docId]) {
|
||||
this.forceSaveTimer[docId] = expireAt;
|
||||
EditorData.prototype.addForceSaveTimerNX = function(ctx, docId, expireAt) {
|
||||
let tenantTimer = this.forceSaveTimer[ctx.tenant];
|
||||
if (!tenantTimer) {
|
||||
this.forceSaveTimer[ctx.tenant] = tenantTimer = {};
|
||||
}
|
||||
if (!tenantTimer[docId]) {
|
||||
tenantTimer[docId] = expireAt;
|
||||
}
|
||||
return Promise.resolve();
|
||||
};
|
||||
EditorData.prototype.getForceSaveTimer = function(now) {
|
||||
let res = [];
|
||||
for (let docId in this.forceSaveTimer) {
|
||||
if (this.forceSaveTimer.hasOwnProperty(docId)) {
|
||||
if (this.forceSaveTimer[docId] < now) {
|
||||
res.push(docId);
|
||||
delete this.forceSaveTimer[docId];
|
||||
for (let tenant in this.forceSaveTimer) {
|
||||
if (this.forceSaveTimer.hasOwnProperty(tenant)) {
|
||||
let tenantTimer = this.forceSaveTimer[tenant];
|
||||
for (let docId in tenantTimer) {
|
||||
if (tenantTimer.hasOwnProperty(docId)) {
|
||||
if (tenantTimer[docId] < now) {
|
||||
res.push([tenant, docId]);
|
||||
delete tenantTimer[docId];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return Promise.resolve(res);
|
||||
};
|
||||
|
||||
EditorData.prototype.addPresenceUniqueUser = function(userId, expireAt, userInfo) {
|
||||
this.uniqueUser[userId] = {expireAt: expireAt, userInfo: userInfo};
|
||||
EditorData.prototype.addPresenceUniqueUser = function(ctx, userId, expireAt, userInfo) {
|
||||
let tenantUser = this.uniqueUser[ctx.tenant];
|
||||
if (!tenantUser) {
|
||||
this.uniqueUser[ctx.tenant] = tenantUser = {};
|
||||
}
|
||||
tenantUser[userId] = {expireAt: expireAt, userInfo: userInfo};
|
||||
return Promise.resolve();
|
||||
};
|
||||
EditorData.prototype.getPresenceUniqueUser = function(nowUTC) {
|
||||
EditorData.prototype.getPresenceUniqueUser = function(ctx, nowUTC) {
|
||||
let res = [];
|
||||
for (let userId in this.uniqueUser) {
|
||||
if (this.uniqueUser.hasOwnProperty(userId)) {
|
||||
if (this.uniqueUser[userId].expireAt > nowUTC) {
|
||||
let elem = this.uniqueUser[userId];
|
||||
let tenantUser = this.uniqueUser[ctx.tenant];
|
||||
if (!tenantUser) {
|
||||
this.uniqueUser[ctx.tenant] = tenantUser = {};
|
||||
}
|
||||
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)};
|
||||
Object.assign(newElem, elem.userInfo);
|
||||
res.push(newElem);
|
||||
} else {
|
||||
delete this.uniqueUser[userId];
|
||||
delete tenantUser[userId];
|
||||
}
|
||||
}
|
||||
}
|
||||
return Promise.resolve(res);
|
||||
};
|
||||
EditorData.prototype.addPresenceUniqueUsersOfMonth = function(userId, period, userInfo) {
|
||||
if(!this.uniqueUsersOfMonth[period]) {
|
||||
let expireAt = Date.now() + cfgExpMonthUniqueUsers;
|
||||
this.uniqueUsersOfMonth[period] = {expireAt: expireAt, data: {}};
|
||||
EditorData.prototype.addPresenceUniqueUsersOfMonth = function(ctx, userId, period, userInfo) {
|
||||
let tenantUser = this.uniqueUsersOfMonth[ctx.tenant];
|
||||
if (!tenantUser) {
|
||||
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();
|
||||
};
|
||||
EditorData.prototype.getPresenceUniqueUsersOfMonth = function() {
|
||||
EditorData.prototype.getPresenceUniqueUsersOfMonth = function(ctx) {
|
||||
let res = {};
|
||||
let nowUTC = Date.now();
|
||||
for (let periodId in this.uniqueUsersOfMonth) {
|
||||
if (this.uniqueUsersOfMonth.hasOwnProperty(periodId)) {
|
||||
if (this.uniqueUsersOfMonth[periodId].expireAt <= nowUTC) {
|
||||
delete this.uniqueUsersOfMonth[periodId];
|
||||
let tenantUser = this.uniqueUsersOfMonth[ctx.tenant];
|
||||
if (!tenantUser) {
|
||||
this.uniqueUsersOfMonth[ctx.tenant] = tenantUser = {};
|
||||
}
|
||||
for (let periodId in tenantUser) {
|
||||
if (tenantUser.hasOwnProperty(periodId)) {
|
||||
if (tenantUser[periodId].expireAt <= nowUTC) {
|
||||
delete tenantUser[periodId];
|
||||
} else {
|
||||
let date = new Date(parseInt(periodId)).toISOString();
|
||||
res[date] = this.uniqueUsersOfMonth[periodId].data;
|
||||
res[date] = tenantUser[periodId].data;
|
||||
}
|
||||
}
|
||||
}
|
||||
return Promise.resolve(res);
|
||||
};
|
||||
|
||||
EditorData.prototype.addPresenceUniqueViewUser = function(userId, expireAt, userInfo) {
|
||||
this.uniqueViewUser[userId] = {expireAt: expireAt, userInfo: userInfo};
|
||||
EditorData.prototype.addPresenceUniqueViewUser = function(ctx, userId, expireAt, userInfo) {
|
||||
let tenantUser = this.uniqueViewUser[ctx.tenant];
|
||||
if (!tenantUser) {
|
||||
this.uniqueViewUser[ctx.tenant] = tenantUser = {};
|
||||
}
|
||||
tenantUser[userId] = {expireAt: expireAt, userInfo: userInfo};
|
||||
return Promise.resolve();
|
||||
};
|
||||
EditorData.prototype.getPresenceUniqueViewUser = function(nowUTC) {
|
||||
EditorData.prototype.getPresenceUniqueViewUser = function(ctx, nowUTC) {
|
||||
let res = [];
|
||||
for (let userId in this.uniqueViewUser) {
|
||||
if (this.uniqueViewUser.hasOwnProperty(userId)) {
|
||||
if (this.uniqueViewUser[userId].expireAt > nowUTC) {
|
||||
let elem = this.uniqueViewUser[userId];
|
||||
let tenantUser = this.uniqueViewUser[ctx.tenant];
|
||||
if (!tenantUser) {
|
||||
this.uniqueViewUser[ctx.tenant] = tenantUser = {};
|
||||
}
|
||||
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)};
|
||||
Object.assign(newElem, elem.userInfo);
|
||||
res.push(newElem);
|
||||
} else {
|
||||
delete this.uniqueViewUser[userId];
|
||||
delete tenantUser[userId];
|
||||
}
|
||||
}
|
||||
}
|
||||
return Promise.resolve(res);
|
||||
};
|
||||
EditorData.prototype.addPresenceUniqueViewUsersOfMonth = function(userId, period, userInfo) {
|
||||
if(!this.uniqueViewUsersOfMonth[period]) {
|
||||
let expireAt = Date.now() + cfgExpMonthUniqueUsers;
|
||||
this.uniqueViewUsersOfMonth[period] = {expireAt: expireAt, data: {}};
|
||||
EditorData.prototype.addPresenceUniqueViewUsersOfMonth = function(ctx, userId, period, userInfo) {
|
||||
let tenantUser = this.uniqueViewUsersOfMonth[ctx.tenant];
|
||||
if (!tenantUser) {
|
||||
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();
|
||||
};
|
||||
EditorData.prototype.getPresenceUniqueViewUsersOfMonth = function() {
|
||||
EditorData.prototype.getPresenceUniqueViewUsersOfMonth = function(ctx) {
|
||||
let res = {};
|
||||
let nowUTC = Date.now();
|
||||
for (let periodId in this.uniqueViewUsersOfMonth) {
|
||||
if (this.uniqueViewUsersOfMonth.hasOwnProperty(periodId)) {
|
||||
if (this.uniqueViewUsersOfMonth[periodId].expireAt <= nowUTC) {
|
||||
delete this.uniqueViewUsersOfMonth[periodId];
|
||||
let tenantUser = this.uniqueViewUsersOfMonth[ctx.tenant];
|
||||
if (!tenantUser) {
|
||||
this.uniqueViewUsersOfMonth[ctx.tenant] = tenantUser = {};
|
||||
}
|
||||
for (let periodId in tenantUser) {
|
||||
if (tenantUser.hasOwnProperty(periodId)) {
|
||||
if (tenantUser[periodId].expireAt <= nowUTC) {
|
||||
delete tenantUser[periodId];
|
||||
} else {
|
||||
let date = new Date(parseInt(periodId)).toISOString();
|
||||
res[date] = this.uniqueViewUsersOfMonth[periodId].data;
|
||||
res[date] = tenantUser[periodId].data;
|
||||
}
|
||||
}
|
||||
}
|
||||
return Promise.resolve(res);
|
||||
};
|
||||
|
||||
EditorData.prototype.setEditorConnections = function(countEdit, countLiveView, countView, now, precision) {
|
||||
this.stat.push({time: now, edit: countEdit, liveview: countLiveView, view: countView});
|
||||
EditorData.prototype.setEditorConnections = function(ctx, countEdit, countLiveView, countView, now, precision) {
|
||||
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;
|
||||
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++;
|
||||
}
|
||||
this.stat.splice(0, i);
|
||||
tenantStat.splice(0, i);
|
||||
return Promise.resolve();
|
||||
};
|
||||
EditorData.prototype.getEditorConnections = function() {
|
||||
return Promise.resolve(this.stat);
|
||||
EditorData.prototype.getEditorConnections = function(ctx) {
|
||||
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();
|
||||
};
|
||||
EditorData.prototype.incrEditorConnectionsCountByShard = function(shardId, count) {
|
||||
EditorData.prototype.incrEditorConnectionsCountByShard = function(ctx, shardId, count) {
|
||||
return Promise.resolve();
|
||||
};
|
||||
EditorData.prototype.getEditorConnectionsCount = function(connections) {
|
||||
EditorData.prototype.getEditorConnectionsCount = function(ctx, connections) {
|
||||
let count = 0;
|
||||
for (let i = 0; i < connections.length; ++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++;
|
||||
}
|
||||
}
|
||||
return Promise.resolve(count);
|
||||
};
|
||||
EditorData.prototype.setViewerConnectionsCountByShard = function(shardId, count) {
|
||||
EditorData.prototype.setViewerConnectionsCountByShard = function(ctx, shardId, count) {
|
||||
return Promise.resolve();
|
||||
};
|
||||
EditorData.prototype.incrViewerConnectionsCountByShard = function(shardId, count) {
|
||||
EditorData.prototype.incrViewerConnectionsCountByShard = function(ctx, shardId, count) {
|
||||
return Promise.resolve();
|
||||
};
|
||||
EditorData.prototype.getViewerConnectionsCount = function(connections) {
|
||||
EditorData.prototype.getViewerConnectionsCount = function(ctx, connections) {
|
||||
let count = 0;
|
||||
for (let i = 0; i < connections.length; ++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++;
|
||||
}
|
||||
}
|
||||
return Promise.resolve(count);
|
||||
};
|
||||
EditorData.prototype.setLiveViewerConnectionsCountByShard = function(shardId, count) {
|
||||
EditorData.prototype.setLiveViewerConnectionsCountByShard = function(ctx, shardId, count) {
|
||||
return Promise.resolve();
|
||||
};
|
||||
EditorData.prototype.incrLiveViewerConnectionsCountByShard = function(shardId, count) {
|
||||
EditorData.prototype.incrLiveViewerConnectionsCountByShard = function(ctx, shardId, count) {
|
||||
return Promise.resolve();
|
||||
};
|
||||
EditorData.prototype.getLiveViewerConnectionsCount = function(connections) {
|
||||
EditorData.prototype.getLiveViewerConnectionsCount = function(ctx, connections) {
|
||||
let count = 0;
|
||||
for (let i = 0; i < connections.length; ++i) {
|
||||
let conn = connections[i];
|
||||
if (utils.isLiveViewer(conn)) {
|
||||
if (utils.isLiveViewer(conn) && ctx.tenant === tenantManager.getTenantByConnection(ctx, conn)) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
@ -41,8 +41,8 @@ var utils = require('./../../Common/sources/utils');
|
||||
var constants = require('./../../Common/sources/constants');
|
||||
var storageBase = require('./../../Common/sources/storage-base');
|
||||
var formatChecker = require('./../../Common/sources/formatchecker');
|
||||
var logger = require('./../../Common/sources/logger');
|
||||
const commonDefines = require('./../../Common/sources/commondefines');
|
||||
const operationContext = require('./../../Common/sources/operationContext');
|
||||
|
||||
var config = require('config');
|
||||
var configServer = config.get('services.CoAuthoring.server');
|
||||
@ -57,9 +57,12 @@ const PATTERN_ENCRYPTED = 'ENCRYPTED;';
|
||||
exports.uploadTempFile = function(req, res) {
|
||||
return co(function* () {
|
||||
var docId = 'uploadTempFile';
|
||||
let ctx = new operationContext.Context();
|
||||
try {
|
||||
ctx.initFromRequest(req);
|
||||
ctx.logger.info('uploadTempFile start');
|
||||
let params;
|
||||
let authRes = docsCoServer.getRequestParams(docId, req, true);
|
||||
let authRes = yield docsCoServer.getRequestParams(ctx, req, true);
|
||||
if(authRes.code === constants.NO_ERROR){
|
||||
params = authRes.params;
|
||||
} else {
|
||||
@ -67,33 +70,34 @@ exports.uploadTempFile = function(req, res) {
|
||||
return;
|
||||
}
|
||||
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)) {
|
||||
var task = yield* taskResult.addRandomKeyTask(docId);
|
||||
var task = yield* taskResult.addRandomKeyTask(ctx, docId);
|
||||
var strPath = task.key + '/' + docId + '.tmp';
|
||||
yield storageBase.putObject(strPath, req.body, req.body.length);
|
||||
var url = yield storageBase.getSignedUrl(utils.getBaseUrlByRequest(req), strPath,
|
||||
yield storageBase.putObject(ctx, strPath, req.body, req.body.length);
|
||||
var url = yield storageBase.getSignedUrl(ctx, utils.getBaseUrlByRequest(req), strPath,
|
||||
commonDefines.c_oAscUrlTypes.Temporary);
|
||||
utils.fillResponse(req, res, new commonDefines.ConvertStatus(constants.NO_ERROR, url), false);
|
||||
} else {
|
||||
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);
|
||||
}
|
||||
logger.debug('End uploadTempFile: docId = %s', docId);
|
||||
}
|
||||
catch (e) {
|
||||
logger.error('Error uploadTempFile: docId = %s\r\n%s', docId, e.stack);
|
||||
} catch (e) {
|
||||
ctx.logger.error('Error uploadTempFile: %s', e.stack);
|
||||
utils.fillResponse(req, res, new commonDefines.ConvertStatus(constants.UNKNOWN), false);
|
||||
} finally {
|
||||
ctx.logger.info('uploadTempFile end');
|
||||
}
|
||||
});
|
||||
};
|
||||
function checkJwtUpload(docId, errorName, token){
|
||||
let checkJwtRes = docsCoServer.checkJwt(docId, token, commonDefines.c_oAscSecretType.Session);
|
||||
return checkJwtUploadTransformRes(docId, errorName, checkJwtRes);
|
||||
function* checkJwtUpload(ctx, errorName, token){
|
||||
let checkJwtRes = yield docsCoServer.checkJwt(ctx, token, commonDefines.c_oAscSecretType.Session);
|
||||
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};
|
||||
if (checkJwtRes.decoded) {
|
||||
var doc = checkJwtRes.decoded.document;
|
||||
@ -106,94 +110,100 @@ function checkJwtUploadTransformRes(docId, errorName, checkJwtRes){
|
||||
res.userid = edit.user.id;
|
||||
}
|
||||
} 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 {
|
||||
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;
|
||||
}
|
||||
exports.uploadImageFileOld = function(req, res) {
|
||||
var docId = req.params.docid;
|
||||
logger.debug('Start uploadImageFileOld: docId = %s', docId);
|
||||
var userid = req.params.userid;
|
||||
if (cfgTokenEnableBrowser) {
|
||||
var checkJwtRes = checkJwtUpload(docId, 'uploadImageFileOld', req.query['token']);
|
||||
if(!checkJwtRes.err){
|
||||
docId = checkJwtRes.docId || docId;
|
||||
userid = checkJwtRes.userid || userid;
|
||||
} else {
|
||||
res.sendStatus(403);
|
||||
return;
|
||||
}
|
||||
}
|
||||
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);
|
||||
return co(function* () {
|
||||
let ctx = new operationContext.Context();
|
||||
ctx.initFromRequest(req);
|
||||
var docId = req.params.docid;
|
||||
var userid = req.params.userid;
|
||||
ctx.init(ctx.tenant, docId, userid);
|
||||
ctx.logger.debug('Start uploadImageFileOld');
|
||||
if (cfgTokenEnableBrowser) {
|
||||
var checkJwtRes = yield* checkJwtUpload(ctx, 'uploadImageFileOld', req.query['token']);
|
||||
if(!checkJwtRes.err){
|
||||
docId = checkJwtRes.docId || docId;
|
||||
userid = checkJwtRes.userid || userid;
|
||||
} else {
|
||||
storageBase.getSignedUrlsByArray(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);
|
||||
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);
|
||||
});
|
||||
res.sendStatus(403);
|
||||
return;
|
||||
}
|
||||
});
|
||||
form.parse(req);
|
||||
} else {
|
||||
logger.debug('Error params uploadImageFileOld: docId = %s', docId);
|
||||
res.sendStatus(400);
|
||||
}
|
||||
}
|
||||
ctx.init(ctx.tenant, docId, userid);
|
||||
var listImages = [];
|
||||
//todo userid
|
||||
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) {
|
||||
return co(function* () {
|
||||
@ -201,18 +211,21 @@ exports.uploadImageFile = function(req, res) {
|
||||
var docId = 'null';
|
||||
let output = {};
|
||||
let isValidJwt = true;
|
||||
let ctx = new operationContext.Context();
|
||||
try {
|
||||
ctx.initFromRequest(req);
|
||||
docId = req.params.docid;
|
||||
ctx.setDocId(docId);
|
||||
let encrypted = false;
|
||||
logger.debug('Start uploadImageFile: docId = %s', docId);
|
||||
ctx.logger.debug('Start uploadImageFile');
|
||||
|
||||
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) {
|
||||
//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) {
|
||||
docId = transformedRes.docId || docId;
|
||||
encrypted = transformedRes.encrypted;
|
||||
@ -220,11 +233,12 @@ exports.uploadImageFile = function(req, res) {
|
||||
isValidJwt = false;
|
||||
}
|
||||
}
|
||||
ctx.setDocId(docId);
|
||||
|
||||
if (isValidJwt && docId && req.body && Buffer.isBuffer(req.body)) {
|
||||
let buffer = req.body;
|
||||
if (buffer.length <= cfgImageSize) {
|
||||
var format = formatChecker.getImageFormat(buffer, undefined);
|
||||
var format = formatChecker.getImageFormat(ctx, buffer);
|
||||
var formatStr = formatChecker.getStringFromFormat(format);
|
||||
if (encrypted && PATTERN_ENCRYPTED === buffer.toString('utf8', 0, 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 strPathRel = 'media/' + strImageName + '.' + formatStr;
|
||||
var strPath = docId + '/' + strPathRel;
|
||||
yield storageBase.putObject(strPath, buffer, buffer.length);
|
||||
output[strPathRel] = yield storageBase.getSignedUrl(utils.getBaseUrlByRequest(req), strPath,
|
||||
yield storageBase.putObject(ctx, strPath, buffer, buffer.length);
|
||||
output[strPathRel] = yield storageBase.getSignedUrl(ctx, utils.getBaseUrlByRequest(req), strPath,
|
||||
commonDefines.c_oAscUrlTypes.Session);
|
||||
isError = false;
|
||||
} else {
|
||||
logger.debug('uploadImageFile format is not supported: docId = %s', docId);
|
||||
ctx.logger.debug('uploadImageFile format is not supported');
|
||||
}
|
||||
} 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) {
|
||||
isError = true;
|
||||
logger.error('Error uploadImageFile: docId = %s\r\n%s', docId, e.stack);
|
||||
ctx.logger.error('Error uploadImageFile:%s', e.stack);
|
||||
} finally {
|
||||
try {
|
||||
if (!isError) {
|
||||
@ -258,9 +272,9 @@ exports.uploadImageFile = function(req, res) {
|
||||
} else {
|
||||
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) {
|
||||
logger.error('Error uploadImageFile: docId = %s\r\n%s', docId, e.stack);
|
||||
ctx.logger.error('Error uploadImageFile:%s', e.stack);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -46,6 +46,7 @@ var logger = require('./../../Common/sources/logger');
|
||||
var constants = require('./../../Common/sources/constants');
|
||||
var commondefines = require('./../../Common/sources/commondefines');
|
||||
var queueService = require('./../../Common/sources/taskqueueRabbitMQ');
|
||||
var operationContext = require('./../../Common/sources/operationContext');
|
||||
var pubsubService = require('./pubsubRabbitMQ');
|
||||
|
||||
var cfgExpFilesCron = config.get('expire.filesCron');
|
||||
@ -65,31 +66,36 @@ let expDocumentsStep = getCronStep(cfgExpDocumentsCron);
|
||||
|
||||
var checkFileExpire = function() {
|
||||
return co(function* () {
|
||||
let ctx = new operationContext.Context();
|
||||
try {
|
||||
logger.debug('checkFileExpire start');
|
||||
ctx.logger.info('checkFileExpire start');
|
||||
let removedCount = 0;
|
||||
var expired;
|
||||
var removedCount = 0;
|
||||
var currentRemovedCount;
|
||||
do {
|
||||
currentRemovedCount = 0;
|
||||
expired = yield taskResult.getExpired(cfgExpFilesRemovedAtOnce, cfgExpFiles);
|
||||
expired = yield taskResult.getExpired(ctx, cfgExpFilesRemovedAtOnce, cfgExpFiles);
|
||||
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 (yield canvasService.cleanupCache(docId)) {
|
||||
if (yield canvasService.cleanupCache(ctx)) {
|
||||
currentRemovedCount++;
|
||||
}
|
||||
} 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;
|
||||
} while (currentRemovedCount > 0);
|
||||
logger.debug('checkFileExpire end: removedCount = %d', removedCount);
|
||||
ctx.initDefault();
|
||||
ctx.logger.info('checkFileExpire end: removedCount = %d', removedCount);
|
||||
} catch (e) {
|
||||
logger.error('checkFileExpire error:\r\n%s', e.stack);
|
||||
ctx.logger.error('checkFileExpire error: %s', e.stack);
|
||||
} finally {
|
||||
setTimeout(checkFileExpire, expFilesStep);
|
||||
}
|
||||
@ -100,8 +106,9 @@ var checkDocumentExpire = function() {
|
||||
var queue = null;
|
||||
var removedCount = 0;
|
||||
var startSaveCount = 0;
|
||||
let ctx = new operationContext.Context();
|
||||
try {
|
||||
logger.debug('checkDocumentExpire start');
|
||||
ctx.logger.info('checkDocumentExpire start');
|
||||
var now = (new Date()).getTime();
|
||||
let expiredKeys = yield docsCoServer.editorData.getDocumentPresenceExpired(now);
|
||||
if (expiredKeys.length > 0) {
|
||||
@ -109,30 +116,33 @@ var checkDocumentExpire = function() {
|
||||
yield queue.initPromise(true, false, false, false, false, false);
|
||||
|
||||
for (var i = 0; i < expiredKeys.length; ++i) {
|
||||
var docId = expiredKeys[i];
|
||||
let tenant = expiredKeys[i][0];
|
||||
let docId = expiredKeys[i][1];
|
||||
if (docId) {
|
||||
var hasChanges = yield docsCoServer.hasChanges(docId);
|
||||
ctx.init(tenant, docId, ctx.userId);
|
||||
var hasChanges = yield docsCoServer.hasChanges(ctx, docId);
|
||||
if (hasChanges) {
|
||||
yield docsCoServer.createSaveTimerPromise(docId, null, null, queue, true);
|
||||
yield docsCoServer.createSaveTimer(ctx, docId, null, null, queue, true);
|
||||
startSaveCount++;
|
||||
} else {
|
||||
yield docsCoServer.cleanDocumentOnExitNoChangesPromise(docId);
|
||||
yield docsCoServer.cleanDocumentOnExitNoChangesPromise(ctx, docId);
|
||||
removedCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ctx.initDefault();
|
||||
ctx.logger.info('checkDocumentExpire end: startSaveCount = %d, removedCount = %d', startSaveCount, removedCount);
|
||||
} catch (e) {
|
||||
logger.error('checkDocumentExpire error:\r\n%s', e.stack);
|
||||
ctx.logger.error('checkDocumentExpire error: %s', e.stack);
|
||||
} finally {
|
||||
try {
|
||||
if (queue) {
|
||||
yield queue.close();
|
||||
}
|
||||
} 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);
|
||||
}
|
||||
});
|
||||
@ -141,8 +151,9 @@ let forceSaveTimeout = function() {
|
||||
return co(function* () {
|
||||
let queue = null;
|
||||
let pubsub = null;
|
||||
let ctx = new operationContext.Context();
|
||||
try {
|
||||
logger.debug('forceSaveTimeout start');
|
||||
ctx.logger.info('forceSaveTimeout start');
|
||||
let now = (new Date()).getTime();
|
||||
let expiredKeys = yield docsCoServer.editorData.getForceSaveTimer(now);
|
||||
if (expiredKeys.length > 0) {
|
||||
@ -154,18 +165,21 @@ let forceSaveTimeout = function() {
|
||||
|
||||
let actions = [];
|
||||
for (let i = 0; i < expiredKeys.length; ++i) {
|
||||
let docId = expiredKeys[i];
|
||||
let tenant = expiredKeys[i][0];
|
||||
let docId = expiredKeys[i][1];
|
||||
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));
|
||||
}
|
||||
}
|
||||
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) {
|
||||
logger.error('forceSaveTimeout error:\r\n%s', e.stack);
|
||||
ctx.logger.error('forceSaveTimeout error: %s', e.stack);
|
||||
} finally {
|
||||
try {
|
||||
if (queue) {
|
||||
@ -175,7 +189,7 @@ let forceSaveTimeout = function() {
|
||||
yield pubsub.close();
|
||||
}
|
||||
} catch (e) {
|
||||
logger.error('checkDocumentExpire error:\r\n%s', e.stack);
|
||||
ctx.logger.error('checkDocumentExpire error: %s', e.stack);
|
||||
}
|
||||
setTimeout(forceSaveTimeout, cfgForceSaveStep);
|
||||
}
|
||||
|
||||
@ -46,22 +46,21 @@ var pool = mysql.createPool({
|
||||
timezone : 'Z',
|
||||
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) {
|
||||
if (err) {
|
||||
logger.error('pool.getConnection error: %s', err);
|
||||
ctx.logger.error('pool.getConnection error: %s', err);
|
||||
if (callbackFunction) callbackFunction(err, null);
|
||||
return;
|
||||
}
|
||||
let queryCallback = function (error, result) {
|
||||
connection.release();
|
||||
if (error) {
|
||||
logger.error('________________________error_____________________');
|
||||
logger.error('sqlQuery: %s sqlCommand: %s', error.code, sqlCommand);
|
||||
logger.error(error);
|
||||
logger.error('_____________________end_error_____________________');
|
||||
ctx.logger.error('________________________error_____________________');
|
||||
ctx.logger.error('sqlQuery: %s sqlCommand: %s', error.code, sqlCommand);
|
||||
ctx.logger.error(error);
|
||||
ctx.logger.error('_____________________end_error_____________________');
|
||||
}
|
||||
if (callbackFunction) callbackFunction(error, result);
|
||||
};
|
||||
@ -82,7 +81,7 @@ let concatParams = function (val1, val2) {
|
||||
};
|
||||
exports.concatParams = concatParams;
|
||||
|
||||
exports.upsert = function(task, opt_updateUserIndex) {
|
||||
exports.upsert = function(ctx, task, opt_updateUserIndex) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
task.completeDefaults();
|
||||
let dateNow = new Date();
|
||||
@ -93,6 +92,7 @@ exports.upsert = function(task, opt_updateUserIndex) {
|
||||
userCallback.fromValues(task.userIndex, task.callback);
|
||||
cbInsert = userCallback.toSQLInsert();
|
||||
}
|
||||
let p0 = addSqlParam(task.tenant, values);
|
||||
let p1 = addSqlParam(task.key, values);
|
||||
let p2 = addSqlParam(task.status, values);
|
||||
let p3 = addSqlParam(task.statusInfo, values);
|
||||
@ -102,8 +102,8 @@ exports.upsert = function(task, opt_updateUserIndex) {
|
||||
let p7 = addSqlParam(cbInsert, values);
|
||||
let p8 = addSqlParam(task.baseurl, 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)'+
|
||||
` VALUES (${p1}, ${p2}, ${p3}, ${p4}, ${p5}, ${p6}, ${p7}, ${p8}) ON DUPLICATE KEY UPDATE` +
|
||||
var sqlCommand = 'INSERT INTO task_result (tenant, id, status, status_info, last_open_date, user_index, change_id, callback, baseurl)'+
|
||||
` VALUES (${p0}, ${p1}, ${p2}, ${p3}, ${p4}, ${p5}, ${p6}, ${p7}, ${p8}) ON DUPLICATE KEY UPDATE` +
|
||||
` last_open_date = ${p9}`;
|
||||
if (task.callback) {
|
||||
let p10 = addSqlParam(JSON.stringify(task.callback), values);
|
||||
@ -118,7 +118,7 @@ exports.upsert = function(task, opt_updateUserIndex) {
|
||||
}
|
||||
sqlCommand += ';';
|
||||
|
||||
exports.sqlQuery(sqlCommand, function(error, result) {
|
||||
exports.sqlQuery(ctx, sqlCommand, function(error, result) {
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
|
||||
@ -62,11 +62,9 @@ types.setTypeParser(1184, function(stringValue) {
|
||||
return new Date(stringValue + '+0000');
|
||||
});
|
||||
|
||||
var logger = require('./../../Common/sources/logger');
|
||||
|
||||
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 *() {
|
||||
var result = null;
|
||||
var error = null;
|
||||
@ -75,7 +73,7 @@ exports.sqlQuery = function(sqlCommand, callbackFunction, opt_noModifyRes, opt_n
|
||||
} catch (err) {
|
||||
error = err;
|
||||
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 {
|
||||
if (callbackFunction) {
|
||||
@ -112,6 +110,7 @@ function getUpsertString(task, values) {
|
||||
userCallback.fromValues(task.userIndex, task.callback);
|
||||
cbInsert = userCallback.toSQLInsert();
|
||||
}
|
||||
let p0 = addSqlParam(task.tenant, values);
|
||||
let p1 = addSqlParam(task.key, values);
|
||||
let p2 = addSqlParam(task.status, values);
|
||||
let p3 = addSqlParam(task.statusInfo, values);
|
||||
@ -123,9 +122,9 @@ function getUpsertString(task, values) {
|
||||
if (isSupportOnConflict) {
|
||||
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
|
||||
let sqlCommand = "INSERT INTO task_result (id, status, status_info, last_open_date, user_index, change_id, callback, baseurl)";
|
||||
sqlCommand += ` VALUES (${p1}, ${p2}, ${p3}, ${p4}, ${p5}, ${p6}, ${p7}, ${p8})`;
|
||||
sqlCommand += ` ON CONFLICT (id) DO UPDATE SET last_open_date = ${p9}`;
|
||||
let sqlCommand = "INSERT INTO task_result (tenant, id, status, status_info, last_open_date, user_index, change_id, callback, baseurl)";
|
||||
sqlCommand += ` VALUES (${p0}, ${p1}, ${p2}, ${p3}, ${p4}, ${p5}, ${p6}, ${p7}, ${p8})`;
|
||||
sqlCommand += ` ON CONFLICT (tenant, id) DO UPDATE SET last_open_date = ${p9}`;
|
||||
if (task.callback) {
|
||||
let p10 = addSqlParam(JSON.stringify(task.callback), values);
|
||||
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;";
|
||||
return sqlCommand;
|
||||
} 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) {
|
||||
let values = [];
|
||||
var sqlCommand = getUpsertString(task, values);
|
||||
exports.sqlQuery(sqlCommand, function(error, result) {
|
||||
exports.sqlQuery(ctx, sqlCommand, function(error, result) {
|
||||
if (error) {
|
||||
if (isSupportOnConflict && '42601' === error.code) {
|
||||
//SYNTAX ERROR
|
||||
isSupportOnConflict = false;
|
||||
logger.warn('checkIsSupportOnConflict false');
|
||||
resolve(exports.upsert(task));
|
||||
ctx.logger.warn('checkIsSupportOnConflict false');
|
||||
resolve(exports.upsert(ctx, task));
|
||||
} else {
|
||||
reject(error);
|
||||
}
|
||||
@ -167,12 +166,13 @@ exports.upsert = function(task) {
|
||||
}, 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;
|
||||
if (i >= objChanges.length) {
|
||||
return;
|
||||
}
|
||||
let isSupported = true;
|
||||
let tenant = [];
|
||||
let id = [];
|
||||
let changeId = [];
|
||||
let userId = [];
|
||||
@ -181,27 +181,28 @@ exports.insertChanges = function(tableChanges, startIndex, objChanges, docId, in
|
||||
let change = [];
|
||||
let time = [];
|
||||
//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) `;
|
||||
sqlCommand += "SELECT * FROM UNNEST ($1::text[], $2::int[], $3::text[], $4::text[], $5::text[], $6::text[], $7::timestamp[]);";
|
||||
let values = [id, changeId, userId, userIdOriginal, username, change, time];
|
||||
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::text[], $3::int[], $4::text[], $5::text[], $6::text[], $7::text[], $8::timestamp[]);";
|
||||
let values = [tenant, id, changeId, userId, userIdOriginal, username, change, time];
|
||||
let curLength = sqlCommand.length;
|
||||
for (; i < objChanges.length; ++i) {
|
||||
//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;
|
||||
if (curLength >= maxPacketSize && i > startIndex) {
|
||||
exports.sqlQuery(sqlCommand, function(error, output) {
|
||||
exports.sqlQuery(ctx, sqlCommand, function(error, output) {
|
||||
if (error && '42883' == error.code) {
|
||||
isSupported = false;
|
||||
logger.warn('postgresql does not support UNNEST');
|
||||
ctx.logger.warn('postgresql does not support UNNEST');
|
||||
}
|
||||
if (error) {
|
||||
callback(error, output, isSupported);
|
||||
} else {
|
||||
exports.insertChanges(tableChanges, i, objChanges, docId, index, user, callback);
|
||||
exports.insertChanges(ctx, tableChanges, i, objChanges, docId, index, user, callback);
|
||||
}
|
||||
}, undefined, undefined, values);
|
||||
return;
|
||||
}
|
||||
tenant.push(ctx.tenant);
|
||||
id.push(docId);
|
||||
changeId.push(index++);
|
||||
userId.push(user.id);
|
||||
@ -210,10 +211,10 @@ exports.insertChanges = function(tableChanges, startIndex, objChanges, docId, in
|
||||
change.push(objChanges[i].change);
|
||||
time.push(objChanges[i].time);
|
||||
}
|
||||
exports.sqlQuery(sqlCommand, function(error, output) {
|
||||
exports.sqlQuery(ctx, sqlCommand, function(error, output) {
|
||||
if (error && '42883' == error.code) {
|
||||
isSupported = false;
|
||||
logger.warn('postgresql does not support UNNEST');
|
||||
ctx.logger.warn('postgresql does not support UNNEST');
|
||||
}
|
||||
callback(error, output, isSupported);
|
||||
}, undefined, undefined, values);
|
||||
|
||||
@ -40,7 +40,6 @@ const commonDefines = require('./../../Common/sources/commondefines');
|
||||
var utils = require('./../../Common/sources/utils');
|
||||
var rabbitMQCore = require('./../../Common/sources/rabbitMQCore');
|
||||
var activeMQCore = require('./../../Common/sources/activeMQCore');
|
||||
const logger = require('./../../Common/sources/logger');
|
||||
|
||||
const cfgQueueType = config.get('queue.type');
|
||||
var cfgRabbitExchangePubSub = config.get('rabbitmq.exchangepubsub');
|
||||
|
||||
@ -56,6 +56,8 @@ const wopiClient = require('./wopiClient');
|
||||
const constants = require('./../../Common/sources/constants');
|
||||
const utils = require('./../../Common/sources/utils');
|
||||
const commonDefines = require('./../../Common/sources/commondefines');
|
||||
const operationContext = require('./../../Common/sources/operationContext');
|
||||
const tenantManager = require('./../../Common/sources/tenantManager');
|
||||
const configStorage = configCommon.get('storage');
|
||||
|
||||
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 cfgTokenEnableRequestInbox = configCommon.get('services.CoAuthoring.token.enable.request.inbox');
|
||||
const cfgTokenEnableRequestOutbox = configCommon.get('services.CoAuthoring.token.enable.request.outbox');
|
||||
const cfgLicenseFile = configCommon.get('license.license_file');
|
||||
|
||||
const app = express();
|
||||
app.disable('x-powered-by');
|
||||
@ -74,43 +77,49 @@ const server = http.createServer(app);
|
||||
let licenseInfo, licenseOriginal, updatePluginsTime, userPlugins, pluginsLoaded;
|
||||
|
||||
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)) {
|
||||
return;
|
||||
}
|
||||
console.log('update Folder true: %s ; %s', eventType, filename);
|
||||
operationContext.global.logger.info('update Folder true: %s ; %s', eventType, filename);
|
||||
updatePluginsTime = new Date();
|
||||
pluginsLoaded = false;
|
||||
};
|
||||
const readLicense = function*() {
|
||||
[licenseInfo, licenseOriginal] = yield* license.readLicense();
|
||||
[licenseInfo, licenseOriginal] = yield* license.readLicense(cfgLicenseFile);
|
||||
};
|
||||
const updateLicense = () => {
|
||||
return co(function*() {
|
||||
try {
|
||||
yield* readLicense();
|
||||
docsCoServer.setLicenseInfo(licenseInfo, licenseOriginal);
|
||||
console.log('End updateLicense');
|
||||
operationContext.global.logger.info('End updateLicense');
|
||||
} 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)) {
|
||||
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 ' +
|
||||
'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')) {
|
||||
const staticContent = config.get('server.static_content');
|
||||
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);
|
||||
res.sendFile(realUrl, sendFileOptions, (err) => {
|
||||
if (err) {
|
||||
logger.error(err);
|
||||
operationContext.global.logger.error(err);
|
||||
res.status(400).end();
|
||||
}
|
||||
});
|
||||
@ -146,33 +155,36 @@ if (configStorage.has('fs.folderPath')) {
|
||||
try {
|
||||
fs.watch(config.get('plugins.path'), updatePlugins);
|
||||
} 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',
|
||||
// то с помощью app.settings.env (https://github.com/strongloop/express/issues/936)
|
||||
// Если нужна обработка ошибок, то теперь она такая https://github.com/expressjs/errorhandler
|
||||
docsCoServer.install(server, () => {
|
||||
console.log('Start callbackFunction');
|
||||
operationContext.global.logger.info('Start callbackFunction');
|
||||
|
||||
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) => {
|
||||
let buildVersion = commonDefines.buildVersion;
|
||||
let buildNumber = commonDefines.buildNumber;
|
||||
let buildDate, packageType, customerId = "";
|
||||
if (licenseInfo) {
|
||||
buildDate = licenseInfo.buildDate.toISOString();
|
||||
packageType = licenseInfo.packageType;
|
||||
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);
|
||||
return co(function*() {
|
||||
let ctx = new operationContext.Context();
|
||||
ctx.initFromRequest(req);
|
||||
let licenseInfo = yield tenantManager.getTenantLicense(ctx);
|
||||
let buildVersion = commonDefines.buildVersion;
|
||||
let buildNumber = commonDefines.buildNumber;
|
||||
let buildDate, packageType, customerId = "";
|
||||
if (licenseInfo) {
|
||||
buildDate = licenseInfo.buildDate.toISOString();
|
||||
packageType = licenseInfo.packageType;
|
||||
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(
|
||||
{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){
|
||||
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"));
|
||||
});
|
||||
|
||||
@ -292,8 +306,10 @@ docsCoServer.install(server, () => {
|
||||
app.get('/themes.json', apicache.middleware("5 minutes"), (req, res) => {
|
||||
return co(function*() {
|
||||
let themes = [];
|
||||
let ctx = new operationContext.Context();
|
||||
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')) {
|
||||
return;
|
||||
}
|
||||
@ -305,8 +321,8 @@ docsCoServer.install(server, () => {
|
||||
if (staticContent.hasOwnProperty(i) && themesUri.startsWith(i)) {
|
||||
let dir = staticContent[i].path + themesUri.substring(i.length);
|
||||
themesList = yield utils.listObjects(dir, true);
|
||||
logger.debug('themes.json dir:%s', dir);
|
||||
logger.debug('themes.json themesList:%j', themesList);
|
||||
ctx.logger.debug('themes.json dir:%s', dir);
|
||||
ctx.logger.debug('themes.json themesList:%j', themesList);
|
||||
for (let j = 0; j < themesList.length; ++j) {
|
||||
if (themesList[j].endsWith('.json')) {
|
||||
let data = yield utils.readFile(themesList[j], true);
|
||||
@ -317,7 +333,7 @@ docsCoServer.install(server, () => {
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
logger.error('themes.json error:%s', err.stack);
|
||||
ctx.logger.error('themes.json error:%s', err.stack);
|
||||
} finally {
|
||||
if (themes.length > 0) {
|
||||
res.setHeader('Content-Type', 'application/json');
|
||||
@ -325,15 +341,15 @@ docsCoServer.install(server, () => {
|
||||
} else {
|
||||
res.sendStatus(404);
|
||||
}
|
||||
logger.info('themes.json end');
|
||||
ctx.logger.info('themes.json end');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
process.on('uncaughtException', (err) => {
|
||||
logger.error((new Date).toUTCString() + ' uncaughtException:', err.message);
|
||||
logger.error(err.stack);
|
||||
operationContext.global.logger.error((new Date).toUTCString() + ' uncaughtException:', err.message);
|
||||
operationContext.global.logger.error(err.stack);
|
||||
logger.shutdown(() => {
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
@ -47,11 +47,11 @@ var WAIT_TIMEOUT = 30000;
|
||||
var LOOP_TIMEOUT = 1000;
|
||||
var EXEC_TIMEOUT = WAIT_TIMEOUT + utils.CONVERTION_TIMEOUT;
|
||||
|
||||
exports.shutdown = function(editorData, status) {
|
||||
exports.shutdown = function(ctx, editorData, status) {
|
||||
return co(function*() {
|
||||
var res = true;
|
||||
try {
|
||||
logger.debug('shutdown start:' + EXEC_TIMEOUT);
|
||||
ctx.logger.debug('shutdown start:' + EXEC_TIMEOUT);
|
||||
|
||||
//redisKeyShutdown не простой счетчик, чтобы его не уменьшала сборка, которая началась перед запуском Shutdown
|
||||
//сбрасываем redisKeyShutdown на всякий случай, если предыдущий запуск не дошел до конца
|
||||
@ -60,24 +60,24 @@ exports.shutdown = function(editorData, status) {
|
||||
var pubsub = new pubsubService();
|
||||
yield pubsub.initPromise();
|
||||
//inner ping to update presence
|
||||
logger.debug('shutdown pubsub shutdown message');
|
||||
pubsub.publish(JSON.stringify({type: commonDefines.c_oPublishType.shutdown, status: status}));
|
||||
ctx.logger.debug('shutdown pubsub shutdown message');
|
||||
pubsub.publish(JSON.stringify({type: commonDefines.c_oPublishType.shutdown, ctx: ctx, status: status}));
|
||||
//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 isStartWait = true;
|
||||
while (true) {
|
||||
var curTime = new Date().getTime() - startTime;
|
||||
if (isStartWait && curTime >= WAIT_TIMEOUT) {
|
||||
isStartWait = false;
|
||||
logger.debug('shutdown stop wait pubsub deliver');
|
||||
ctx.logger.debug('shutdown stop wait pubsub deliver');
|
||||
} else if (curTime >= EXEC_TIMEOUT) {
|
||||
res = false;
|
||||
logger.debug('shutdown timeout');
|
||||
ctx.logger.debug('shutdown timeout');
|
||||
break;
|
||||
}
|
||||
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) {
|
||||
break;
|
||||
}
|
||||
@ -88,10 +88,10 @@ exports.shutdown = function(editorData, status) {
|
||||
yield editorData.cleanupShutdown(redisKeyShutdown);
|
||||
yield pubsub.close();
|
||||
|
||||
logger.debug('shutdown end');
|
||||
ctx.logger.debug('shutdown end');
|
||||
} catch (e) {
|
||||
res = false;
|
||||
logger.error('shutdown error:\r\n%s', e.stack);
|
||||
ctx.logger.error('shutdown error: %s', e.stack);
|
||||
}
|
||||
return res;
|
||||
});
|
||||
|
||||
@ -34,9 +34,9 @@
|
||||
|
||||
const crypto = require('crypto');
|
||||
var sqlBase = require('./baseConnector');
|
||||
var logger = require('./../../Common/sources/logger');
|
||||
var utils = require('./../../Common/sources/utils');
|
||||
var constants = require('./../../Common/sources/constants');
|
||||
var tenantManager = require('./../../Common/sources/tenantManager');
|
||||
|
||||
let addSqlParam = sqlBase.baseConnector.addSqlParameter;
|
||||
let concatParams = sqlBase.baseConnector.concatParams;
|
||||
@ -56,6 +56,7 @@ var FileStatus = {
|
||||
};
|
||||
|
||||
function TaskResultData() {
|
||||
this.tenant = null;
|
||||
this.key = null;
|
||||
this.status = null;
|
||||
this.statusInfo = null;
|
||||
@ -71,6 +72,9 @@ function TaskResultData() {
|
||||
this.innerPasswordChange = null;//not a DB field
|
||||
}
|
||||
TaskResultData.prototype.completeDefaults = function() {
|
||||
if (!this.tenant) {
|
||||
this.tenant = tenantManager.getDefautTenant();
|
||||
}
|
||||
if (!this.key) {
|
||||
this.key = '';
|
||||
}
|
||||
@ -100,16 +104,17 @@ TaskResultData.prototype.completeDefaults = function() {
|
||||
}
|
||||
};
|
||||
|
||||
function upsert(task, opt_updateUserIndex) {
|
||||
return sqlBase.baseConnector.upsert(task, opt_updateUserIndex);
|
||||
function upsert(ctx, task, opt_updateUserIndex) {
|
||||
return sqlBase.baseConnector.upsert(ctx, task, opt_updateUserIndex);
|
||||
}
|
||||
|
||||
function select(docId) {
|
||||
function select(ctx, docId) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
let values = [];
|
||||
let sqlParam = addSqlParam(docId, values);
|
||||
let sqlCommand = `SELECT * FROM task_result WHERE id=${sqlParam};`;
|
||||
sqlBase.baseConnector.sqlQuery(sqlCommand, function(error, result) {
|
||||
let p1 = addSqlParam(ctx.tenant, values);
|
||||
let p2 = addSqlParam(docId, values);
|
||||
let sqlCommand = `SELECT * FROM task_result WHERE tenant=${p1} AND id=${p2};`;
|
||||
sqlBase.baseConnector.sqlQuery(ctx, sqlCommand, function(error, result) {
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
@ -170,14 +175,15 @@ function toUpdateArray(task, updateTime, isMask, values, setPassword) {
|
||||
return res;
|
||||
}
|
||||
|
||||
function update(task, setPassword) {
|
||||
function update(ctx, task, setPassword) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
let values = [];
|
||||
let updateElems = toUpdateArray(task, true, false, values, setPassword);
|
||||
let sqlSet = updateElems.join(', ');
|
||||
let sqlParam = addSqlParam(task.key, values);
|
||||
let sqlCommand = `UPDATE task_result SET ${sqlSet} WHERE id=${sqlParam};`;
|
||||
sqlBase.baseConnector.sqlQuery(sqlCommand, function(error, result) {
|
||||
let p1 = addSqlParam(task.tenant, values);
|
||||
let p2 = addSqlParam(task.key, values);
|
||||
let sqlCommand = `UPDATE task_result SET ${sqlSet} WHERE tenant=${p1} AND id=${p2};`;
|
||||
sqlBase.baseConnector.sqlQuery(ctx, sqlCommand, function(error, result) {
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
@ -187,16 +193,17 @@ function update(task, setPassword) {
|
||||
});
|
||||
}
|
||||
|
||||
function updateIf(task, mask) {
|
||||
function updateIf(ctx, task, mask) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
let values = [];
|
||||
let commandArg = toUpdateArray(task, true, false, values, false);
|
||||
let commandArgMask = toUpdateArray(mask, false, true, values, false);
|
||||
commandArgMask.push('tenant=' + addSqlParam(mask.tenant, values));
|
||||
commandArgMask.push('id=' + addSqlParam(mask.key, values));
|
||||
let sqlSet = commandArg.join(', ');
|
||||
let sqlWhere = commandArgMask.join(' AND ');
|
||||
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) {
|
||||
reject(error);
|
||||
} else {
|
||||
@ -205,28 +212,30 @@ function updateIf(task, mask) {
|
||||
}, undefined, undefined, values);
|
||||
});
|
||||
}
|
||||
function restoreInitialPassword(docId) {
|
||||
return select(docId).then(function(selectRes) {
|
||||
function restoreInitialPassword(ctx, docId) {
|
||||
return select(ctx, docId).then(function(selectRes) {
|
||||
if (selectRes.length > 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();
|
||||
updateTask.tenant = ctx.tenant;
|
||||
updateTask.key = docId;
|
||||
if (docPassword.initial) {
|
||||
var documentPassword = new sqlBase.DocumentPassword();
|
||||
documentPassword.fromValues(docPassword.initial);
|
||||
updateTask.password = documentPassword.toSQLInsert();
|
||||
return update(updateTask, true);
|
||||
return update(ctx, updateTask, true);
|
||||
} else if (docPassword.current) {
|
||||
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) {
|
||||
task.tenant = ctx.tenant;
|
||||
if (undefined !== opt_prefix && undefined !== opt_size) {
|
||||
task.key = opt_prefix + crypto.randomBytes(opt_size).toString("hex");
|
||||
} else {
|
||||
@ -234,6 +243,7 @@ function addRandomKey(task, opt_prefix, opt_size) {
|
||||
}
|
||||
task.completeDefaults();
|
||||
let values = [];
|
||||
let p0 = addSqlParam(task.tenant, values);
|
||||
let p1 = addSqlParam(task.key, values);
|
||||
let p2 = addSqlParam(task.status, 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 p7 = addSqlParam(task.callback, 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)' +
|
||||
` VALUES (${p1}, ${p2}, ${p3}, ${p4}, ${p5}, ${p6}, ${p7}, ${p8});`;
|
||||
sqlBase.baseConnector.sqlQuery(sqlCommand, function(error, result) {
|
||||
let sqlCommand = 'INSERT INTO task_result (tenant, id, status, status_info, last_open_date, user_index, change_id, callback, baseurl)' +
|
||||
` VALUES (${p0}, ${p1}, ${p2}, ${p3}, ${p4}, ${p5}, ${p6}, ${p7}, ${p8});`;
|
||||
sqlBase.baseConnector.sqlQuery(ctx, sqlCommand, function(error, result) {
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
@ -253,8 +263,9 @@ function addRandomKey(task, opt_prefix, opt_size) {
|
||||
}, undefined, undefined, values);
|
||||
});
|
||||
}
|
||||
function* addRandomKeyTask(key, opt_prefix, opt_size) {
|
||||
function* addRandomKeyTask(ctx, key, opt_prefix, opt_size) {
|
||||
var task = new TaskResultData();
|
||||
task.tenant = ctx.tenant;
|
||||
task.key = key;
|
||||
task.status = FileStatus.WaitQueue;
|
||||
//nTryCount чтобы не зависнуть если реально будут проблемы с DB
|
||||
@ -262,7 +273,7 @@ function* addRandomKeyTask(key, opt_prefix, opt_size) {
|
||||
var addRes = null;
|
||||
while (nTryCount-- > 0) {
|
||||
try {
|
||||
addRes = yield addRandomKey(task, opt_prefix, opt_size);
|
||||
addRes = yield addRandomKey(ctx, task, opt_prefix, opt_size);
|
||||
} catch (e) {
|
||||
addRes = null;
|
||||
//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) {
|
||||
let values = [];
|
||||
let sqlParam = addSqlParam(docId, values);
|
||||
const sqlCommand = `DELETE FROM task_result WHERE id=${sqlParam};`;
|
||||
sqlBase.baseConnector.sqlQuery(sqlCommand, function(error, result) {
|
||||
let p1 = addSqlParam(ctx.tenant, values);
|
||||
let p2 = addSqlParam(docId, values);
|
||||
const sqlCommand = `DELETE FROM task_result WHERE tenant=${p1} AND id=${p2};`;
|
||||
sqlBase.baseConnector.sqlQuery(ctx, sqlCommand, function(error, result) {
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
@ -292,14 +304,15 @@ function remove(docId) {
|
||||
}, undefined, undefined, values);
|
||||
});
|
||||
}
|
||||
function removeIf(mask) {
|
||||
function removeIf(ctx, mask) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
let values = [];
|
||||
let commandArgMask = toUpdateArray(mask, false, true, values, false);
|
||||
commandArgMask.push('tenant=' + addSqlParam(mask.tenant, values));
|
||||
commandArgMask.push('id=' + addSqlParam(mask.key, values));
|
||||
let sqlWhere = commandArgMask.join(' AND ');
|
||||
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) {
|
||||
reject(error);
|
||||
} else {
|
||||
@ -308,7 +321,7 @@ function removeIf(mask) {
|
||||
}, undefined, undefined, values);
|
||||
});
|
||||
}
|
||||
function getExpired(maxCount, expireSeconds) {
|
||||
function getExpired(ctx, maxCount, expireSeconds) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
let values = [];
|
||||
let expireDate = new Date();
|
||||
@ -316,8 +329,8 @@ function getExpired(maxCount, expireSeconds) {
|
||||
let sqlParam1 = addSqlParam(expireDate, values);
|
||||
let sqlParam2 = addSqlParam(maxCount, values);
|
||||
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};`;
|
||||
sqlBase.baseConnector.sqlQuery(sqlCommand, function(error, result) {
|
||||
` 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(ctx, sqlCommand, function(error, result) {
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
|
||||
@ -44,13 +44,14 @@ const logger = require('./../../Common/sources/logger');
|
||||
const utils = require('./../../Common/sources/utils');
|
||||
const constants = require('./../../Common/sources/constants');
|
||||
const commonDefines = require('./../../Common/sources/commondefines');
|
||||
const operationContext = require('./../../Common/sources/operationContext');
|
||||
const tenantManager = require('./../../Common/sources/tenantManager');
|
||||
const sqlBase = require('./baseConnector');
|
||||
const taskResult = require('./taskresult');
|
||||
const canvasService = require('./canvasservice');
|
||||
|
||||
const cfgTokenOutboxAlgorithm = config.get('services.CoAuthoring.token.outbox.algorithm');
|
||||
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 cfgCallbackRequestTimeout = config.get('services.CoAuthoring.server.callbackRequestTimeout');
|
||||
const cfgDownloadTimeout = config.get('FileConverter.converter.downloadTimeout');
|
||||
@ -97,8 +98,10 @@ let mimeTypesByExt = (function() {
|
||||
function discovery(req, res) {
|
||||
return co(function*() {
|
||||
let output = '';
|
||||
let ctx = new operationContext.Context();
|
||||
try {
|
||||
logger.info('wopiDiscovery start');
|
||||
ctx.initFromRequest(req);
|
||||
ctx.logger.info('wopiDiscovery start');
|
||||
let baseUrl = cfgWopiHost || utils.getBaseUrlByRequest(req);
|
||||
let names = ['Word','Excel','PowerPoint'];
|
||||
let favIconUrls = [cfgWopiFavIconUrlWord, cfgWopiFavIconUrlCell, cfgWopiFavIconUrlSlide];
|
||||
@ -179,11 +182,11 @@ function discovery(req, res) {
|
||||
}
|
||||
output += `</net-zone>${proofKey}</wopi-discovery>`;
|
||||
} catch (err) {
|
||||
logger.error('wopiDiscovery error:%s', err.stack);
|
||||
ctx.logger.error('wopiDiscovery error:%s', err.stack);
|
||||
} finally {
|
||||
res.setHeader('Content-Type', 'text/xml');
|
||||
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,
|
||||
"hasTemplateSource": true, "productVersion": commonDefines.buildVersion
|
||||
};
|
||||
let ctx = new operationContext.Context();
|
||||
try {
|
||||
logger.info('collaboraCapabilities start');
|
||||
ctx.initFromRequest(req);
|
||||
ctx.logger.info('collaboraCapabilities start');
|
||||
} catch (err) {
|
||||
logger.error('collaboraCapabilities error:%s', err.stack);
|
||||
ctx.logger.error('collaboraCapabilities error:%s', err.stack);
|
||||
} finally {
|
||||
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) {
|
||||
return undefined !== userAuth.wopiSrc;
|
||||
}
|
||||
function parseWopiCallback(docId, userAuthStr, opt_url) {
|
||||
function parseWopiCallback(ctx, userAuthStr, opt_url) {
|
||||
let wopiParams = null;
|
||||
if (isWopiCallback(userAuthStr)) {
|
||||
let userAuth = JSON.parse(userAuthStr);
|
||||
@ -243,58 +248,59 @@ function parseWopiCallback(docId, userAuthStr, opt_url) {
|
||||
let commonInfo = null;
|
||||
let lastModifiedTime = null;
|
||||
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)) {
|
||||
commonInfo = JSON.parse(commonInfoStr);
|
||||
lastModifiedTime = commonInfo.fileInfo.LastModifiedTime;
|
||||
if (lastModifiedTime) {
|
||||
let callbacks = sqlBase.UserCallback.prototype.getCallbacks(docId, opt_url);
|
||||
let callbacks = sqlBase.UserCallback.prototype.getCallbacks(ctx, opt_url);
|
||||
lastModifiedTime = getLastModifiedTimeFromCallbacks(callbacks);
|
||||
}
|
||||
}
|
||||
}
|
||||
wopiParams = {commonInfo: commonInfo, userAuth: userAuth, LastModifiedTime: lastModifiedTime};
|
||||
logger.debug('parseWopiCallback wopiParams:%j', wopiParams);
|
||||
ctx.logger.debug('parseWopiCallback wopiParams:%j', wopiParams);
|
||||
}
|
||||
return wopiParams;
|
||||
}
|
||||
function checkAndInvalidateCache(docId, fileInfo) {
|
||||
function checkAndInvalidateCache(ctx, docId, fileInfo) {
|
||||
return co(function*() {
|
||||
let res = {success: true, lockId: undefined};
|
||||
let selectRes = yield taskResult.select(docId);
|
||||
let selectRes = yield taskResult.select(ctx, docId);
|
||||
if (selectRes.length > 0) {
|
||||
let row = selectRes[0];
|
||||
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)) {
|
||||
let commonInfo = JSON.parse(commonInfoStr);
|
||||
res.lockId = commonInfo.lockId;
|
||||
logger.debug('wopiEditor lockId from DB lockId=%s', res.lockId);
|
||||
let unlockMarkStr = sqlBase.UserCallback.prototype.getCallbackByUserIndex(docId, row.callback);
|
||||
logger.debug('wopiEditor commonInfoStr=%s', commonInfoStr);
|
||||
logger.debug('wopiEditor unlockMarkStr=%s', unlockMarkStr);
|
||||
ctx.logger.debug('wopiEditor lockId from DB lockId=%s', res.lockId);
|
||||
let unlockMarkStr = sqlBase.UserCallback.prototype.getCallbackByUserIndex(ctx, row.callback);
|
||||
ctx.logger.debug('wopiEditor commonInfoStr=%s', commonInfoStr);
|
||||
ctx.logger.debug('wopiEditor unlockMarkStr=%s', unlockMarkStr);
|
||||
let hasUnlockMarker = isWopiUnlockMarker(unlockMarkStr);
|
||||
logger.debug('wopiEditor hasUnlockMarker=%s', hasUnlockMarker);
|
||||
ctx.logger.debug('wopiEditor hasUnlockMarker=%s', hasUnlockMarker);
|
||||
if (hasUnlockMarker) {
|
||||
let fileInfoVersion = fileInfo.Version;
|
||||
let cacheVersion = commonInfo.fileInfo.Version;
|
||||
let fileInfoModified = fileInfo.LastModifiedTime;
|
||||
let cacheModified = commonInfo.fileInfo.LastModifiedTime;
|
||||
logger.debug('wopiEditor version fileInfo=%s; cache=%s', fileInfoVersion, cacheVersion);
|
||||
logger.debug('wopiEditor LastModifiedTime fileInfo=%s; cache=%s', fileInfoModified, cacheModified);
|
||||
ctx.logger.debug('wopiEditor version fileInfo=%s; cache=%s', fileInfoVersion, cacheVersion);
|
||||
ctx.logger.debug('wopiEditor LastModifiedTime fileInfo=%s; cache=%s', fileInfoModified, cacheModified);
|
||||
if (fileInfoVersion !== cacheVersion || (fileInfoModified !== cacheModified)) {
|
||||
var mask = new taskResult.TaskResultData();
|
||||
mask.tenant = ctx.tenant;
|
||||
mask.key = docId;
|
||||
mask.last_open_date = row.last_open_date;
|
||||
//cleanupRes can be false in case of simultaneous opening. it is OK
|
||||
let cleanupRes = yield canvasService.cleanupCacheIf(mask);
|
||||
logger.debug('wopiEditor cleanupRes=%s', cleanupRes);
|
||||
let cleanupRes = yield canvasService.cleanupCacheIf(ctx, mask);
|
||||
ctx.logger.debug('wopiEditor cleanupRes=%s', cleanupRes);
|
||||
res.lockId = undefined;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
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) {
|
||||
return co(function*() {
|
||||
let params = {key: undefined, fileInfo: {}, userAuth: {}, queryParams: req.query, token: undefined, documentType: undefined};
|
||||
let ctx = new operationContext.Context();
|
||||
try {
|
||||
logger.info('wopiEditor start');
|
||||
logger.debug(`wopiEditor req.url:%s`, req.url);
|
||||
logger.debug(`wopiEditor req.query:%j`, req.query);
|
||||
logger.debug(`wopiEditor req.body:%j`, req.body);
|
||||
ctx.initFromRequest(req);
|
||||
let wopiSrc = req.query['wopisrc'];
|
||||
let fileId = wopiSrc.substring(wopiSrc.lastIndexOf('/') + 1);
|
||||
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;
|
||||
let mode = req.params.mode;
|
||||
let wopiSrc = req.query['wopisrc'];
|
||||
let sc = req.query['sc'];
|
||||
let hostSessionId = req.query['hid'];
|
||||
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 fileInfo = params.fileInfo = yield checkFileInfo(uri, access_token, sc);
|
||||
let fileInfo = params.fileInfo = yield checkFileInfo(ctx, uri, access_token, sc);
|
||||
if (!fileInfo) {
|
||||
params.fileInfo = {};
|
||||
return;
|
||||
@ -330,7 +341,6 @@ function getEditorHtml(req, res) {
|
||||
}
|
||||
//docId
|
||||
let docId = undefined;
|
||||
let fileId = wopiSrc.substring(wopiSrc.lastIndexOf('/') + 1);
|
||||
if ('view' !== mode) {
|
||||
docId = `${fileId}`;
|
||||
} else {
|
||||
@ -344,7 +354,7 @@ function getEditorHtml(req, res) {
|
||||
}
|
||||
}
|
||||
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;
|
||||
let userAuth = params.userAuth = {
|
||||
wopiSrc: wopiSrc, access_token: access_token, access_token_ttl: access_token_ttl,
|
||||
@ -352,7 +362,7 @@ function getEditorHtml(req, res) {
|
||||
};
|
||||
|
||||
//check and invalidate cache
|
||||
let checkRes = yield checkAndInvalidateCache(docId, fileInfo);
|
||||
let checkRes = yield checkAndInvalidateCache(ctx, docId, fileInfo);
|
||||
let lockId = checkRes.lockId;
|
||||
if (!checkRes.success) {
|
||||
params.fileInfo = {};
|
||||
@ -364,12 +374,12 @@ function getEditorHtml(req, res) {
|
||||
fileType = fileInfo.FileExtension ? fileInfo.FileExtension.substr(1) : fileType;
|
||||
lockId = crypto.randomBytes(16).toString('base64');
|
||||
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
|
||||
if ('view' !== mode) {
|
||||
let lockRes = yield lock('LOCK', lockId, fileInfo, userAuth);
|
||||
let lockRes = yield lock(ctx, 'LOCK', lockId, fileInfo, userAuth);
|
||||
if (!lockRes) {
|
||||
params.fileInfo = {};
|
||||
return;
|
||||
@ -384,36 +394,36 @@ function getEditorHtml(req, res) {
|
||||
|
||||
if (cfgTokenEnableBrowser) {
|
||||
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);
|
||||
}
|
||||
} catch (err) {
|
||||
logger.error('wopiEditor error:%s', err.stack);
|
||||
ctx.logger.error('wopiEditor error:%s', err.stack);
|
||||
params.fileInfo = {};
|
||||
} finally {
|
||||
logger.debug('wopiEditor render params=%j', params);
|
||||
ctx.logger.debug('wopiEditor render params=%j', params);
|
||||
try {
|
||||
res.render("editor-wopi", params);
|
||||
} catch (err) {
|
||||
logger.error('wopiEditor error:%s', err.stack);
|
||||
ctx.logger.error('wopiEditor error:%s', err.stack);
|
||||
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* () {
|
||||
let postRes = null;
|
||||
try {
|
||||
logger.info('wopi PutFile start');
|
||||
ctx.logger.info('wopi PutFile start');
|
||||
if (!wopiParams.userAuth) {
|
||||
return postRes;
|
||||
}
|
||||
let fileInfo = wopiParams.commonInfo.fileInfo;
|
||||
let userAuth = wopiParams.userAuth;
|
||||
let uri = `${userAuth.wopiSrc}/contents?access_token=${userAuth.access_token}`;
|
||||
let filterStatus = yield checkIpFilter(uri);
|
||||
let filterStatus = yield checkIpFilter(ctx, uri);
|
||||
if (0 !== filterStatus) {
|
||||
return postRes;
|
||||
}
|
||||
@ -432,33 +442,33 @@ function putFile(wopiParams, data, dataStream, dataSize, userLastChangeId, isMod
|
||||
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);
|
||||
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 headers=%j', postRes.response.headers);
|
||||
ctx.logger.debug('wopi PutFile response body:%s', postRes.body);
|
||||
} else {
|
||||
logger.warn('wopi SupportsUpdate = false or UserCanWrite = false');
|
||||
ctx.logger.warn('wopi SupportsUpdate = false or UserCanWrite = false');
|
||||
}
|
||||
} catch (err) {
|
||||
logger.error('wopi error PutFile:%s', err.stack);
|
||||
ctx.logger.error('wopi error PutFile:%s', err.stack);
|
||||
} finally {
|
||||
logger.info('wopi PutFile end');
|
||||
ctx.logger.info('wopi PutFile end');
|
||||
}
|
||||
return postRes;
|
||||
});
|
||||
}
|
||||
function renameFile(wopiParams, name) {
|
||||
function renameFile(ctx, wopiParams, name) {
|
||||
return co(function* () {
|
||||
let res;
|
||||
let res = undefined;
|
||||
try {
|
||||
logger.info('wopi RenameFile start');
|
||||
ctx.logger.info('wopi RenameFile start');
|
||||
if (!wopiParams.userAuth) {
|
||||
return res;
|
||||
}
|
||||
let fileInfo = wopiParams.commonInfo.fileInfo;
|
||||
let userAuth = wopiParams.userAuth;
|
||||
let uri = `${userAuth.wopiSrc}?access_token=${userAuth.access_token}`;
|
||||
let filterStatus = yield checkIpFilter(uri);
|
||||
let filterStatus = yield checkIpFilter(ctx, uri);
|
||||
if (0 !== filterStatus) {
|
||||
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)};
|
||||
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);
|
||||
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) {
|
||||
res = JSON.parse(postRes.body);
|
||||
} else {
|
||||
@ -481,22 +491,22 @@ function renameFile(wopiParams, name) {
|
||||
res = {"Name": name};
|
||||
}
|
||||
} else {
|
||||
logger.info('wopi SupportsRename = false');
|
||||
ctx.logger.info('wopi SupportsRename = false');
|
||||
}
|
||||
} catch (err) {
|
||||
logger.error('wopi error RenameFile:%s', err.stack);
|
||||
ctx.logger.error('wopi error RenameFile:%s', err.stack);
|
||||
} finally {
|
||||
logger.info('wopi RenameFile end');
|
||||
ctx.logger.info('wopi RenameFile end');
|
||||
}
|
||||
return res;
|
||||
});
|
||||
}
|
||||
function checkFileInfo(uri, access_token, sc) {
|
||||
function checkFileInfo(ctx, uri, access_token, sc) {
|
||||
return co(function* () {
|
||||
let fileInfo;
|
||||
let fileInfo = undefined;
|
||||
try {
|
||||
logger.info('wopi checkFileInfo start');
|
||||
let filterStatus = yield checkIpFilter(uri);
|
||||
ctx.logger.info('wopi checkFileInfo start');
|
||||
let filterStatus = yield checkIpFilter(ctx, uri);
|
||||
if (0 !== filterStatus) {
|
||||
return fileInfo;
|
||||
}
|
||||
@ -505,23 +515,23 @@ function checkFileInfo(uri, access_token, sc) {
|
||||
headers['X-WOPI-SessionContext'] = sc;
|
||||
}
|
||||
fillStandardHeaders(headers, uri, access_token);
|
||||
logger.debug('wopi checkFileInfo request uri=%s headers=%j', uri, headers);
|
||||
let getRes = yield utils.downloadUrlPromise(uri, cfgDownloadTimeout, undefined, undefined, false, headers);
|
||||
logger.debug(`wopi checkFileInfo headers=%j body=%s`, getRes.response.headers, getRes.body);
|
||||
ctx.logger.debug('wopi checkFileInfo request uri=%s headers=%j', uri, headers);
|
||||
let getRes = yield utils.downloadUrlPromise(ctx, uri, cfgDownloadTimeout, undefined, undefined, false, headers);
|
||||
ctx.logger.debug(`wopi checkFileInfo headers=%j body=%s`, getRes.response.headers, getRes.body);
|
||||
fileInfo = JSON.parse(getRes.body);
|
||||
} catch (err) {
|
||||
logger.error('wopi error checkFileInfo:%s', err.stack);
|
||||
ctx.logger.error('wopi error checkFileInfo:%s', err.stack);
|
||||
} finally {
|
||||
logger.info('wopi checkFileInfo end');
|
||||
ctx.logger.info('wopi checkFileInfo end');
|
||||
}
|
||||
return fileInfo;
|
||||
});
|
||||
}
|
||||
function lock(command, lockId, fileInfo, userAuth) {
|
||||
function lock(ctx, command, lockId, fileInfo, userAuth) {
|
||||
return co(function* () {
|
||||
let res = true;
|
||||
try {
|
||||
logger.info('wopi %s start', command);
|
||||
ctx.logger.info('wopi %s start', command);
|
||||
if (fileInfo && fileInfo.SupportsLocks) {
|
||||
if (!userAuth) {
|
||||
return false;
|
||||
@ -529,32 +539,32 @@ function lock(command, lockId, fileInfo, userAuth) {
|
||||
let wopiSrc = userAuth.wopiSrc;
|
||||
let access_token = userAuth.access_token;
|
||||
let uri = `${wopiSrc}?access_token=${access_token}`;
|
||||
let filterStatus = yield checkIpFilter(uri);
|
||||
let filterStatus = yield checkIpFilter(ctx, uri);
|
||||
if (0 !== filterStatus) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let headers = {"X-WOPI-Override": command, "X-WOPI-Lock": lockId};
|
||||
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);
|
||||
logger.debug('wopi %s response headers=%j', command, postRes.response.headers);
|
||||
ctx.logger.debug('wopi %s response headers=%j', command, postRes.response.headers);
|
||||
} else {
|
||||
logger.info('wopi %s SupportsLocks = false', command);
|
||||
ctx.logger.info('wopi %s SupportsLocks = false', command);
|
||||
}
|
||||
} catch (err) {
|
||||
res = false;
|
||||
logger.error('wopi error %s:%s', command, err.stack);
|
||||
ctx.logger.error('wopi error %s:%s', command, err.stack);
|
||||
} finally {
|
||||
logger.info('wopi %s end', command);
|
||||
ctx.logger.info('wopi %s end', command);
|
||||
}
|
||||
return res;
|
||||
});
|
||||
}
|
||||
function unlock(wopiParams) {
|
||||
function unlock(ctx, wopiParams) {
|
||||
return co(function* () {
|
||||
try {
|
||||
logger.info('wopi Unlock start');
|
||||
ctx.logger.info('wopi Unlock start');
|
||||
let fileInfo = wopiParams.commonInfo.fileInfo;
|
||||
if (fileInfo && fileInfo.SupportsLocks) {
|
||||
if (!wopiParams.userAuth) {
|
||||
@ -564,23 +574,23 @@ function unlock(wopiParams) {
|
||||
let lockId = wopiParams.commonInfo.lockId;
|
||||
let access_token = wopiParams.userAuth.access_token;
|
||||
let uri = `${wopiSrc}?access_token=${access_token}`;
|
||||
let filterStatus = yield checkIpFilter(uri);
|
||||
let filterStatus = yield checkIpFilter(ctx, uri);
|
||||
if (0 !== filterStatus) {
|
||||
return;
|
||||
}
|
||||
|
||||
let headers = {"X-WOPI-Override": "UNLOCK", "X-WOPI-Lock": lockId};
|
||||
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);
|
||||
logger.debug('wopi Unlock response headers=%j', postRes.response.headers);
|
||||
ctx.logger.debug('wopi Unlock response headers=%j', postRes.response.headers);
|
||||
} else {
|
||||
logger.info('wopi SupportsLocks = false');
|
||||
ctx.logger.info('wopi SupportsLocks = false');
|
||||
}
|
||||
} catch (err) {
|
||||
logger.error('wopi error Unlock:%s', err.stack);
|
||||
ctx.logger.error('wopi error Unlock:%s', err.stack);
|
||||
} 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}`;
|
||||
}
|
||||
|
||||
function checkIpFilter(uri){
|
||||
function checkIpFilter(ctx, uri){
|
||||
return co(function* () {
|
||||
let urlParsed = new URL(uri);
|
||||
let filterStatus = yield* utils.checkHostFilter(urlParsed.hostname);
|
||||
let filterStatus = yield* utils.checkHostFilter(ctx, urlParsed.hostname);
|
||||
if (0 !== filterStatus) {
|
||||
logger.warn('wopi checkIpFilter error: url = %s', uri);
|
||||
ctx.logger.warn('wopi checkIpFilter error: url = %s', uri);
|
||||
}
|
||||
return filterStatus;
|
||||
});
|
||||
|
||||
@ -53,6 +53,8 @@ const wopiClient = require('./../../DocService/sources/wopiClient');
|
||||
var statsDClient = require('./../../Common/sources/statsdclient');
|
||||
var queueService = require('./../../Common/sources/taskqueueRabbitMQ');
|
||||
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 cfgDownloadTimeout = configConverter.has('downloadTimeout') ? configConverter.get('downloadTimeout') : 60;
|
||||
@ -278,7 +280,7 @@ function getTempDir() {
|
||||
fs.mkdirSync(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) {
|
||||
let locale = 'en-US';
|
||||
if (_lcid) {
|
||||
@ -288,11 +290,11 @@ function* replaceEmptyFile(docId, fileFrom, ext, _lcid) {
|
||||
if (fs.existsSync(path.join(cfgNewFileTemplate, localeNew))) {
|
||||
locale = localeNew;
|
||||
} 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);
|
||||
if (formatChecker.isDocumentFormat(format)) {
|
||||
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 data = null;
|
||||
var downloadAttemptCount = 0;
|
||||
var urlParsed = url.parse(uri);
|
||||
var filterStatus = yield* utils.checkHostFilter(urlParsed.hostname);
|
||||
var filterStatus = yield* utils.checkHostFilter(ctx, urlParsed.hostname);
|
||||
if (0 == filterStatus) {
|
||||
while (constants.NO_ERROR !== res && downloadAttemptCount++ < cfgDownloadAttemptMaxCount) {
|
||||
try {
|
||||
let authorization;
|
||||
if (utils.canIncludeOutboxAuthorization(uri) && withAuthorization) {
|
||||
authorization = utils.fillJwtForRequest({url: uri}, false);
|
||||
if (utils.canIncludeOutboxAuthorization(ctx, uri) && withAuthorization) {
|
||||
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;
|
||||
res = constants.NO_ERROR;
|
||||
} catch (err) {
|
||||
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
|
||||
if (err.code === 'ETIMEDOUT' || err.code === 'ESOCKETTIMEDOUT') {
|
||||
break;
|
||||
@ -334,18 +337,18 @@ function* downloadFile(docId, uri, fileFrom, withAuthorization, filterPrivate, o
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
} 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;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
function* downloadFileFromStorage(id, strPath, dir) {
|
||||
var list = yield storage.listObjects(strPath);
|
||||
logger.debug('downloadFileFromStorage list %s (id=%s)', list.toString(), id);
|
||||
function* downloadFileFromStorage(ctx, strPath, dir) {
|
||||
var list = yield storage.listObjects(ctx, strPath);
|
||||
ctx.logger.debug('downloadFileFromStorage list %s', list.toString());
|
||||
//create dirs
|
||||
var dirsToCreate = [];
|
||||
var dirStruct = {};
|
||||
@ -371,36 +374,36 @@ function* downloadFileFromStorage(id, strPath, dir) {
|
||||
for (var i = 0; i < list.length; ++i) {
|
||||
var file = list[i];
|
||||
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);
|
||||
}
|
||||
}
|
||||
function* processDownloadFromStorage(dataConvert, cmd, task, tempDirs, authorProps) {
|
||||
function* processDownloadFromStorage(ctx, dataConvert, cmd, task, tempDirs, authorProps) {
|
||||
let res = constants.NO_ERROR;
|
||||
let needConcatFiles = false;
|
||||
if (task.getFromOrigin() || task.getFromSettings()) {
|
||||
dataConvert.fileFrom = path.join(tempDirs.source, 'origin.' + cmd.getFormat());
|
||||
} else {
|
||||
//перезаписываем некоторые файлы из 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';
|
||||
dataConvert.fileFrom = path.join(tempDirs.source, 'Editor.' + format);
|
||||
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;
|
||||
}
|
||||
//mail merge
|
||||
let mailMergeSend = cmd.getMailMergeSend();
|
||||
if (mailMergeSend) {
|
||||
yield* downloadFileFromStorage(mailMergeSend.getJsonKey(), mailMergeSend.getJsonKey(), tempDirs.source);
|
||||
yield* downloadFileFromStorage(ctx, mailMergeSend.getJsonKey(), tempDirs.source);
|
||||
needConcatFiles = true;
|
||||
}
|
||||
if (needConcatFiles) {
|
||||
yield* concatFiles(tempDirs.source);
|
||||
}
|
||||
if (task.getFromChanges()) {
|
||||
res = yield* processChanges(tempDirs, cmd, authorProps);
|
||||
res = yield* processChanges(ctx, tempDirs, cmd, authorProps);
|
||||
}
|
||||
//todo rework
|
||||
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 changesDir = path.join(tempDirs.source, constants.CHANGES_NAME);
|
||||
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 curIndexEnd = Math.min(curIndexStart + cfgMaxRequestChanges, forceSaveIndex);
|
||||
while (curIndexStart < curIndexEnd || extChanges) {
|
||||
let changes = [];
|
||||
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) {
|
||||
changes = extChanges;
|
||||
@ -487,7 +490,7 @@ function* processChanges(tempDirs, cmd, authorProps) {
|
||||
for (let i = 0; i < changes.length; ++i) {
|
||||
let change = changes[i];
|
||||
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?
|
||||
res = constants.EDITOR_CHANGES;
|
||||
break;
|
||||
@ -495,7 +498,7 @@ function* processChanges(tempDirs, cmd, authorProps) {
|
||||
if (null === changesAuthor || changesAuthor !== change.user_id_original) {
|
||||
if (null !== changesAuthor) {
|
||||
yield* streamEnd(streamObj, ']');
|
||||
streamObj = yield* streamCreate(cmd.getDocId(), changesDir, indexFile++);
|
||||
streamObj = yield* streamCreate(ctx, changesDir, indexFile++);
|
||||
}
|
||||
let strDate = baseConnector.getDateTime(change.change_date);
|
||||
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;
|
||||
}
|
||||
|
||||
function* streamCreate(docId, changesDir, indexFile, opt_options) {
|
||||
function* streamCreate(ctx, changesDir, indexFile, opt_options) {
|
||||
let fileName = constants.CHANGES_NAME + indexFile + '.json';
|
||||
let filePath = path.join(changesDir, fileName);
|
||||
let writeStream = yield utils.promiseCreateWriteStream(filePath, opt_options);
|
||||
writeStream.on('error', function(err) {
|
||||
//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};
|
||||
}
|
||||
@ -558,41 +561,41 @@ function* streamEnd(streamObj, text) {
|
||||
streamObj.writeStream.end(text, 'utf8');
|
||||
yield utils.promiseWaitClose(streamObj.writeStream);
|
||||
}
|
||||
function* processUploadToStorage(dir, storagePath) {
|
||||
function* processUploadToStorage(ctx, dir, storagePath) {
|
||||
var list = yield utils.listObjects(dir);
|
||||
if (list.length < MAX_OPEN_FILES) {
|
||||
yield* processUploadToStorageChunk(list, dir, storagePath);
|
||||
yield* processUploadToStorageChunk(ctx, list, dir, storagePath);
|
||||
} else {
|
||||
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) {
|
||||
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 (undefined !== childRes.stdout) {
|
||||
if (isDebug) {
|
||||
logger.debug('stdout (id=%s):%s', docId, childRes.stdout);
|
||||
ctx.logger.debug('stdout:%s', childRes.stdout);
|
||||
} else {
|
||||
logger.error('stdout (id=%s):%s', docId, childRes.stdout);
|
||||
ctx.logger.error('stdout:%s', childRes.stdout);
|
||||
}
|
||||
}
|
||||
if (undefined !== childRes.stderr) {
|
||||
if (isDebug) {
|
||||
logger.debug('stderr (id=%s):%s', docId, childRes.stderr);
|
||||
ctx.logger.debug('stderr:%s', childRes.stderr);
|
||||
} 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 exitSignal = null;
|
||||
if(childRes) {
|
||||
@ -608,23 +611,23 @@ function* postProcess(cmd, dataConvert, tempDirs, childRes, error, isTimeout) {
|
||||
error = constants.CONVERT;
|
||||
}
|
||||
if (-1 !== exitCodesMinorError.indexOf(error)) {
|
||||
writeProcessOutputToLog(dataConvert.key, childRes, true);
|
||||
logger.debug('ExitCode (code=%d;signal=%s;error:%d;id=%s)', exitCode, exitSignal, error, dataConvert.key);
|
||||
writeProcessOutputToLog(ctx, childRes, true);
|
||||
ctx.logger.debug('ExitCode (code=%d;signal=%s;error:%d)', exitCode, exitSignal, error);
|
||||
} else {
|
||||
writeProcessOutputToLog(dataConvert.key, childRes, false);
|
||||
logger.error('ExitCode (code=%d;signal=%s;error:%d;id=%s)', exitCode, exitSignal, error, dataConvert.key);
|
||||
writeProcessOutputToLog(ctx, childRes, false);
|
||||
ctx.logger.error('ExitCode (code=%d;signal=%s;error:%d)', exitCode, exitSignal, error);
|
||||
if (cfgErrorFiles) {
|
||||
yield* processUploadToStorage(tempDirs.temp, cfgErrorFiles + '/' + dataConvert.key);
|
||||
logger.debug('processUploadToStorage error complete(id=%s)', dataConvert.key);
|
||||
yield* processUploadToStorage(ctx, tempDirs.temp, cfgErrorFiles + '/' + dataConvert.key);
|
||||
ctx.logger.debug('processUploadToStorage error complete(id=%s)', dataConvert.key);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
writeProcessOutputToLog(dataConvert.key, childRes, true);
|
||||
logger.debug('ExitCode (code=%d;signal=%s;error:%d;id=%s)', exitCode, exitSignal, error, dataConvert.key);
|
||||
writeProcessOutputToLog(ctx, childRes, true);
|
||||
ctx.logger.debug('ExitCode (code=%d;signal=%s;error:%d)', exitCode, exitSignal, error);
|
||||
}
|
||||
if (-1 !== exitCodesUpload.indexOf(error)) {
|
||||
yield* processUploadToStorage(tempDirs.result, dataConvert.key);
|
||||
logger.debug('processUploadToStorage complete(id=%s)', dataConvert.key);
|
||||
yield* processUploadToStorage(ctx, tempDirs.result, dataConvert.key);
|
||||
ctx.logger.debug('processUploadToStorage complete');
|
||||
}
|
||||
cmd.setStatusInfo(error);
|
||||
var existFile = false;
|
||||
@ -651,13 +654,14 @@ function* postProcess(cmd, dataConvert, tempDirs, childRes, error, isTimeout) {
|
||||
cmd.setTitle(cmd.getOutputPath());
|
||||
}
|
||||
|
||||
var res = new commonDefines.TaskQueueData();
|
||||
res.setCmd(cmd);
|
||||
logger.debug('output (data=%s;id=%s)', JSON.stringify(res), dataConvert.key);
|
||||
return res;
|
||||
var queueData = new commonDefines.TaskQueueData();
|
||||
queueData.setCtx(ctx);
|
||||
queueData.setCmd(cmd);
|
||||
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 childArgs;
|
||||
if (cfgArgs.length > 0) {
|
||||
@ -707,8 +711,11 @@ function* spawnProcess(isBuilder, tempDirs, dataConvert, authorProps, getTaskTim
|
||||
}, waitMS);
|
||||
childRes = yield spawnAsyncPromise;
|
||||
} catch (err) {
|
||||
let fLog = null === err.status ? logger.error : logger.debug;
|
||||
fLog.call(logger, 'error spawnAsync(id=%s)\r\n%s', cmd.getDocId(), err.stack);
|
||||
if (null === err.status) {
|
||||
ctx.logger.error('error spawnAsync %s', err.stack);
|
||||
} else {
|
||||
ctx.logger.debug('error spawnAsync %s', err.stack);
|
||||
}
|
||||
childRes = err;
|
||||
}
|
||||
if (undefined !== timeoutId) {
|
||||
@ -717,7 +724,7 @@ function* spawnProcess(isBuilder, tempDirs, dataConvert, authorProps, getTaskTim
|
||||
return {childRes: childRes, isTimeout: isTimeout};
|
||||
}
|
||||
|
||||
function* ExecuteTask(task) {
|
||||
function* ExecuteTask(ctx, task) {
|
||||
var startDate = null;
|
||||
var curDate = null;
|
||||
if(clientStatsD) {
|
||||
@ -728,7 +735,7 @@ function* ExecuteTask(task) {
|
||||
var getTaskTime = new Date();
|
||||
var cmd = task.getCmd();
|
||||
var dataConvert = new TaskQueueDataConvert(task);
|
||||
logger.debug('Start Task(id=%s)', dataConvert.key);
|
||||
ctx.logger.info('Start Task');
|
||||
var error = constants.NO_ERROR;
|
||||
tempDirs = getTempDir();
|
||||
let fileTo = task.getToFile();
|
||||
@ -738,7 +745,7 @@ function* ExecuteTask(task) {
|
||||
if (cmd.getUrl()) {
|
||||
let format = cmd.getFormat();
|
||||
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 withAuthorization = cmd.getWithAuthorization();
|
||||
let filterPrivate = !withAuthorization;
|
||||
@ -760,13 +767,13 @@ function* ExecuteTask(task) {
|
||||
headers = {'X-WOPI-MaxExpectedSize': cfgDownloadMaxBytes, 'X-WOPI-ItemVersion': fileInfo.Version};
|
||||
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) {
|
||||
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) {
|
||||
yield* replaceEmptyFile(dataConvert.key, dataConvert.fileFrom, format, cmd.getLCID());
|
||||
yield* replaceEmptyFile(ctx, dataConvert.fileFrom, format, cmd.getLCID());
|
||||
}
|
||||
if(clientStatsD) {
|
||||
clientStatsD.timing('conv.downloadFile', new Date() - curDate);
|
||||
@ -776,16 +783,16 @@ function* ExecuteTask(task) {
|
||||
error = constants.CONVERT_PARAMS;
|
||||
}
|
||||
} else if (cmd.getSaveKey()) {
|
||||
yield* downloadFileFromStorage(cmd.getDocId(), cmd.getDocId(), tempDirs.source);
|
||||
logger.debug('downloadFileFromStorage complete(id=%s)', dataConvert.key);
|
||||
yield* downloadFileFromStorage(ctx, cmd.getDocId(), tempDirs.source);
|
||||
ctx.logger.debug('downloadFileFromStorage complete');
|
||||
if(clientStatsD) {
|
||||
clientStatsD.timing('conv.downloadFileFromStorage', new Date() - curDate);
|
||||
curDate = new Date();
|
||||
}
|
||||
error = yield* processDownloadFromStorage(dataConvert, cmd, task, tempDirs, authorProps);
|
||||
error = yield* processDownloadFromStorage(ctx, dataConvert, cmd, task, tempDirs, authorProps);
|
||||
} else if (cmd.getForgotten()) {
|
||||
yield* downloadFileFromStorage(cmd.getDocId(), cmd.getForgotten(), tempDirs.source);
|
||||
logger.debug('downloadFileFromStorage complete(id=%s)', dataConvert.key);
|
||||
yield* downloadFileFromStorage(ctx, cmd.getForgotten(), tempDirs.source);
|
||||
ctx.logger.debug('downloadFileFromStorage complete');
|
||||
let list = yield utils.listObjects(tempDirs.source, false);
|
||||
if (list.length > 0) {
|
||||
dataConvert.fileFrom = list[0];
|
||||
@ -797,8 +804,8 @@ function* ExecuteTask(task) {
|
||||
}
|
||||
} else if (isBuilder) {
|
||||
//in cause script in POST body
|
||||
yield* downloadFileFromStorage(cmd.getDocId(), cmd.getDocId(), tempDirs.source);
|
||||
logger.debug('downloadFileFromStorage complete(id=%s)', dataConvert.key);
|
||||
yield* downloadFileFromStorage(ctx, cmd.getDocId(), tempDirs.source);
|
||||
ctx.logger.debug('downloadFileFromStorage complete');
|
||||
let list = yield utils.listObjects(tempDirs.source, false);
|
||||
if (list.length > 0) {
|
||||
dataConvert.fileFrom = list[0];
|
||||
@ -813,16 +820,16 @@ function* ExecuteTask(task) {
|
||||
//todo заглушка.вся конвертация на клиенте, но нет простого механизма сохранения на клиенте
|
||||
yield utils.pipeFiles(dataConvert.fileFrom, dataConvert.fileTo);
|
||||
} 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()
|
||||
&& constants.AVS_OFFICESTUDIO_FILE_OTHER_OOXML !== dataConvert.formatTo
|
||||
&& !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 extNew = '.' + formatChecker.getStringFromFormat(constants.AVS_OFFICESTUDIO_FILE_OTHER_OOXML);
|
||||
dataConvert.formatTo = constants.AVS_OFFICESTUDIO_FILE_OTHER_OOXML;
|
||||
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) {
|
||||
@ -830,15 +837,15 @@ function* ExecuteTask(task) {
|
||||
curDate = new Date();
|
||||
}
|
||||
}
|
||||
resData = yield* postProcess(cmd, dataConvert, tempDirs, childRes, error, isTimeout);
|
||||
logger.debug('postProcess (id=%s)', dataConvert.key);
|
||||
resData = yield* postProcess(ctx, cmd, dataConvert, tempDirs, childRes, error, isTimeout);
|
||||
ctx.logger.debug('postProcess');
|
||||
if(clientStatsD) {
|
||||
clientStatsD.timing('conv.postProcess', new Date() - curDate);
|
||||
curDate = new Date();
|
||||
}
|
||||
if (tempDirs) {
|
||||
fs.rmSync(tempDirs.temp, { recursive: true, force: true });
|
||||
logger.debug('deleteFolderRecursive (id=%s)', dataConvert.key);
|
||||
ctx.logger.debug('deleteFolderRecursive');
|
||||
if(clientStatsD) {
|
||||
clientStatsD.timing('conv.deleteFolderRecursive', new Date() - curDate);
|
||||
curDate = new Date();
|
||||
@ -847,6 +854,7 @@ function* ExecuteTask(task) {
|
||||
if(clientStatsD) {
|
||||
clientStatsD.timing('conv.allconvert', new Date() - startDate);
|
||||
}
|
||||
ctx.logger.info('End Task');
|
||||
return resData;
|
||||
}
|
||||
|
||||
@ -854,13 +862,15 @@ function receiveTask(data, ack) {
|
||||
return co(function* () {
|
||||
var res = null;
|
||||
var task = null;
|
||||
let ctx = new operationContext.Context();
|
||||
try {
|
||||
task = new commonDefines.TaskQueueData(JSON.parse(data));
|
||||
if (task) {
|
||||
res = yield* ExecuteTask(task);
|
||||
ctx.initFromTaskQueueData(task);
|
||||
res = yield* ExecuteTask(ctx, task);
|
||||
}
|
||||
} catch (err) {
|
||||
logger.error(err);
|
||||
ctx.logger.error(err);
|
||||
} finally {
|
||||
try {
|
||||
if (!res && task) {
|
||||
@ -868,13 +878,14 @@ function receiveTask(data, ack) {
|
||||
var cmd = task.getCmd();
|
||||
cmd.setStatusInfo(constants.CONVERT);
|
||||
res = new commonDefines.TaskQueueData();
|
||||
res.setCtx(ctx);
|
||||
res.setCmd(cmd);
|
||||
}
|
||||
if (res) {
|
||||
yield queue.addResponse(res);
|
||||
}
|
||||
} catch (err) {
|
||||
logger.error(err);
|
||||
ctx.logger.error(err);
|
||||
} finally {
|
||||
ack();
|
||||
}
|
||||
@ -883,10 +894,13 @@ function receiveTask(data, ack) {
|
||||
}
|
||||
function simulateErrorResponse(data){
|
||||
let task = new commonDefines.TaskQueueData(JSON.parse(data));
|
||||
let ctx = new operationContext.Context();
|
||||
ctx.initFromTaskQueueData(task);
|
||||
//simulate error response
|
||||
let cmd = task.getCmd();
|
||||
cmd.setStatusInfo(constants.CONVERT);
|
||||
let res = new commonDefines.TaskQueueData();
|
||||
task.setCtx(ctx);
|
||||
res.setCmd(cmd);
|
||||
return res;
|
||||
}
|
||||
@ -895,7 +909,7 @@ function run() {
|
||||
queue.on('task', receiveTask);
|
||||
queue.init(true, true, true, false, false, false, function(err) {
|
||||
if (null != err) {
|
||||
logger.error('createTaskQueue error :\r\n%s', err.stack);
|
||||
operationContext.global.logger.error('createTaskQueue error: %s', err.stack);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -34,6 +34,7 @@
|
||||
|
||||
const cluster = require('cluster');
|
||||
const logger = require('./../../Common/sources/logger');
|
||||
const operationContext = require('./../../Common/sources/operationContext');
|
||||
|
||||
if (cluster.isMaster) {
|
||||
const fs = require('fs');
|
||||
@ -42,12 +43,18 @@ if (cluster.isMaster) {
|
||||
const configCommon = require('config');
|
||||
const config = configCommon.get('FileConverter.converter');
|
||||
const license = require('./../../Common/sources/license');
|
||||
const tenantManager = require('./../../Common/sources/tenantManager');
|
||||
|
||||
const cfgLicenseFile = configCommon.get('license.license_file');
|
||||
|
||||
const cfgMaxProcessCount = config.get('maxprocesscount');
|
||||
var licenseInfo, workersCount = 0;
|
||||
var workersCount = 0;
|
||||
const readLicense = function* () {
|
||||
[licenseInfo] = yield* license.readLicense();
|
||||
workersCount = Math.min(licenseInfo.count, Math.ceil(numCPUs * cfgMaxProcessCount));
|
||||
workersCount = Math.ceil(numCPUs * cfgMaxProcessCount);
|
||||
if (!tenantManager.isMultitenantMode()) {
|
||||
let [licenseInfo] = yield* license.readLicense(cfgLicenseFile);
|
||||
workersCount = Math.min(licenseInfo.count, workersCount);
|
||||
}
|
||||
};
|
||||
const updateWorkers = () => {
|
||||
var i;
|
||||
@ -55,7 +62,7 @@ if (cluster.isMaster) {
|
||||
if (arrKeyWorkers.length < workersCount) {
|
||||
for (i = arrKeyWorkers.length; i < workersCount; ++i) {
|
||||
const newWorker = cluster.fork();
|
||||
logger.warn('worker %s started.', newWorker.process.pid);
|
||||
operationContext.global.logger.warn('worker %s started.', newWorker.process.pid);
|
||||
}
|
||||
} else {
|
||||
for (i = workersCount; i < arrKeyWorkers.length; ++i) {
|
||||
@ -70,31 +77,33 @@ if (cluster.isMaster) {
|
||||
return co(function*() {
|
||||
try {
|
||||
yield* readLicense();
|
||||
logger.warn('update cluster with %s workers', workersCount);
|
||||
operationContext.global.logger.warn('update cluster with %s workers', workersCount);
|
||||
updateWorkers();
|
||||
} 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) => {
|
||||
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();
|
||||
});
|
||||
|
||||
updateLicense();
|
||||
|
||||
fs.watchFile(configCommon.get('license').get('license_file'), updateLicense);
|
||||
setInterval(updateLicense, 86400000);
|
||||
if (!tenantManager.isMultitenantMode()) {
|
||||
fs.watchFile(cfgLicenseFile, updateLicense);
|
||||
setInterval(updateLicense, 86400000);
|
||||
}
|
||||
} else {
|
||||
const converter = require('./converter');
|
||||
converter.run();
|
||||
}
|
||||
|
||||
process.on('uncaughtException', (err) => {
|
||||
logger.error((new Date).toUTCString() + ' uncaughtException:', err.message);
|
||||
logger.error(err.stack);
|
||||
operationContext.global.logger.error((new Date).toUTCString() + ' uncaughtException:', err.message);
|
||||
operationContext.global.logger.error(err.stack);
|
||||
logger.shutdown(() => {
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
@ -26,6 +26,7 @@ USE onlyoffice;
|
||||
--
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `doc_changes` (
|
||||
`tenant` varchar(255) NOT NULL,
|
||||
`id` varchar(255) NOT NULL,
|
||||
`change_id` int(10) unsigned 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,
|
||||
`change_data` longtext NOT NULL,
|
||||
`change_date` datetime NOT NULL,
|
||||
PRIMARY KEY (`id`,`change_id`)
|
||||
PRIMARY KEY (`tenant`, `id`,`change_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
--
|
||||
@ -48,6 +49,7 @@ CREATE TABLE IF NOT EXISTS `doc_changes` (
|
||||
--
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `task_result` (
|
||||
`tenant` varchar(255) NOT NULL,
|
||||
`id` varchar(255) NOT NULL,
|
||||
`status` tinyint(3) NOT NULL,
|
||||
`status_info` int(10) NOT NULL,
|
||||
@ -59,7 +61,7 @@ CREATE TABLE IF NOT EXISTS `task_result` (
|
||||
`baseurl` text NOT NULL,
|
||||
`password` longtext NULL,
|
||||
`additional` longtext NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
PRIMARY KEY (`tenant`, `id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
--
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
-- Table structure for doc_changes
|
||||
-- ----------------------------
|
||||
CREATE TABLE IF NOT EXISTS "public"."doc_changes" (
|
||||
"tenant" varchar(255) COLLATE "default" NOT NULL,
|
||||
"id" varchar(255) COLLATE "default" NOT NULL,
|
||||
"change_id" int4 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,
|
||||
"change_data" text COLLATE "default" NOT NULL,
|
||||
"change_date" timestamp without time zone NOT NULL,
|
||||
PRIMARY KEY ("id", "change_id")
|
||||
PRIMARY KEY ("tenant", "id", "change_id")
|
||||
)
|
||||
WITH (OIDS=FALSE);
|
||||
|
||||
@ -23,6 +24,7 @@ WITH (OIDS=FALSE);
|
||||
-- Table structure for task_result
|
||||
-- ----------------------------
|
||||
CREATE TABLE IF NOT EXISTS "public"."task_result" (
|
||||
"tenant" varchar(255) COLLATE "default" NOT NULL,
|
||||
"id" varchar(255) COLLATE "default" NOT NULL,
|
||||
"status" int2 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,
|
||||
"password" text COLLATE "default" NULL,
|
||||
"additional" text COLLATE "default" NULL,
|
||||
PRIMARY KEY ("id")
|
||||
PRIMARY KEY ("tenant", "id")
|
||||
)
|
||||
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
|
||||
t_var "public"."task_result"."user_index"%TYPE;
|
||||
@ -47,9 +49,9 @@ BEGIN
|
||||
-- first try to update the key
|
||||
-- note that "a" must be unique
|
||||
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
|
||||
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;
|
||||
IF found THEN
|
||||
isupdate := 'true';
|
||||
@ -59,7 +61,7 @@ BEGIN
|
||||
-- if someone else inserts the same key concurrently,
|
||||
-- we could get a unique-key failure
|
||||
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';
|
||||
RETURN;
|
||||
EXCEPTION WHEN unique_violation THEN
|
||||
|
||||
Reference in New Issue
Block a user