mirror of
https://github.com/ONLYOFFICE/server.git
synced 2026-04-07 14:04:35 +08:00
Merge branch 'release/v4.1.0'
This commit is contained in:
@ -52,7 +52,8 @@
|
||||
"limits_image_download_timeout": 120,
|
||||
"editor_settings_spellchecker_url": "/spellchecker",
|
||||
"callbackRequestTimeout": 120,
|
||||
"healthcheckfilepath": "../public/healthcheck.docx"
|
||||
"healthcheckfilepath": "../public/healthcheck.docx",
|
||||
"savetimeoutdelay": 5000
|
||||
},
|
||||
"utils": {
|
||||
"utils_common_fontdir": "null",
|
||||
@ -62,16 +63,17 @@
|
||||
"limits_image_types_copy": "jpg;png;gif;bmp;emf;wmf;svg;txt;bin"
|
||||
},
|
||||
"sql": {
|
||||
"type": "mysql",
|
||||
"type": "postgres",
|
||||
"tableChanges": "doc_changes",
|
||||
"tableCallbacks": "doc_callbacks",
|
||||
"tableResult": "task_result",
|
||||
"dbHost": "localhost",
|
||||
"dbPort": 3306,
|
||||
"dbPort": 5432,
|
||||
"dbName": "onlyoffice",
|
||||
"dbUser": "root",
|
||||
"dbUser": "onlyoffice",
|
||||
"dbPass": "onlyoffice",
|
||||
"charset": "utf8",
|
||||
"connectionlimit": 10,
|
||||
"max_allowed_packet": 1048575
|
||||
},
|
||||
"redis": {
|
||||
@ -98,6 +100,11 @@
|
||||
"files": 604800,
|
||||
"filesCron": "00 00 */1 * * *",
|
||||
"filesremovedatonce": 10
|
||||
},
|
||||
"ipfilter": {
|
||||
"rules": [{"address": "*", "allowed": true}],
|
||||
"useforrequest": false,
|
||||
"errorcode": 401
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -32,6 +32,12 @@
|
||||
},
|
||||
"utils": {
|
||||
"utils_common_fontdir": "/Library/Fonts"
|
||||
},
|
||||
"sql": {
|
||||
"type": "mysql",
|
||||
"dbPort": 3306,
|
||||
"dbUser": "root",
|
||||
"dbPass": "onlyoffice"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -32,6 +32,12 @@
|
||||
},
|
||||
"utils": {
|
||||
"utils_common_fontdir": "C:\\Windows\\Fonts"
|
||||
},
|
||||
"sql": {
|
||||
"type": "mysql",
|
||||
"dbPort": 3306,
|
||||
"dbUser": "root",
|
||||
"dbPass": "onlyoffice"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -1,22 +0,0 @@
|
||||
{
|
||||
"services": {
|
||||
"CoAuthoring": {
|
||||
"utils": {
|
||||
"utils_common_fontdir": "C:\\Windows\\Fonts",
|
||||
}
|
||||
}
|
||||
},
|
||||
"FileConverter": {
|
||||
"converter": {
|
||||
"fontDir": "C:\\Windows\\Fonts",
|
||||
"presentationThemesDir": "../../../OfficeWeb/sdk/PowerPoint/themes",
|
||||
"filePath": "../Bin/x2t.exe"
|
||||
}
|
||||
},
|
||||
"license": {
|
||||
"license_file": "./../../license.lic"
|
||||
},
|
||||
"FileStorage": {
|
||||
"directory": "../App_Data"
|
||||
}
|
||||
}
|
||||
@ -5,14 +5,16 @@
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"amazon-s3-url-signer": "https://github.com/agolybev/amazon-s3-url-signer/archive/v0.0.9.tar.gz",
|
||||
"amqplib": "^0.4.0",
|
||||
"aws-sdk": "^2.2.33",
|
||||
"amqplib": "^0.4.1",
|
||||
"aws-sdk": "^2.4.12",
|
||||
"co": "^4.6.0",
|
||||
"config": "^1.19.0",
|
||||
"log4js": "^0.6.31",
|
||||
"config": "^1.21.0",
|
||||
"escape-string-regexp": "^1.0.5",
|
||||
"log4js": "^0.6.38",
|
||||
"mime": "^1.3.4",
|
||||
"mkdirp": "^0.5.1",
|
||||
"node-statsd": "^0.1.1",
|
||||
"request": "^2.69.0"
|
||||
"request": "^2.74.0",
|
||||
"uri-js": "^2.1.1"
|
||||
}
|
||||
}
|
||||
|
||||
@ -285,6 +285,7 @@ function CMailMergeSendData(obj) {
|
||||
this['recordFrom'] = obj['recordFrom'];
|
||||
this['recordTo'] = obj['recordTo'];
|
||||
this['recordCount'] = obj['recordCount'];
|
||||
this['recordErrorCount'] = obj['recordErrorCount'];
|
||||
this['userId'] = obj['userId'];
|
||||
this['url'] = obj['url'];
|
||||
this['baseUrl'] = obj['baseUrl'];
|
||||
@ -299,6 +300,7 @@ function CMailMergeSendData(obj) {
|
||||
this['recordFrom'] = null;
|
||||
this['recordTo'] = null;
|
||||
this['recordCount'] = null;
|
||||
this['recordErrorCount'] = null;
|
||||
this['userId'] = null;
|
||||
this['url'] = null;
|
||||
this['baseUrl'] = null;
|
||||
@ -359,6 +361,12 @@ CMailMergeSendData.prototype.getRecordCount = function() {
|
||||
CMailMergeSendData.prototype.setRecordCount = function(v) {
|
||||
this['recordCount'] = v;
|
||||
};
|
||||
CMailMergeSendData.prototype.getRecordErrorCount = function() {
|
||||
return this['recordErrorCount']
|
||||
};
|
||||
CMailMergeSendData.prototype.setRecordErrorCount = function(v) {
|
||||
this['recordErrorCount'] = v;
|
||||
};
|
||||
CMailMergeSendData.prototype.getUserId = function() {
|
||||
return this['userId']
|
||||
};
|
||||
@ -463,8 +471,8 @@ function OutputSfcData() {
|
||||
this['url'] = undefined;
|
||||
this['changesurl'] = undefined;
|
||||
this['changeshistory'] = undefined;
|
||||
this['users'] = [];
|
||||
this['actions'] = [];
|
||||
this['users'] = undefined;
|
||||
this['actions'] = undefined;
|
||||
this['mailMerge'] = undefined;
|
||||
this['userdata'] = undefined;
|
||||
}
|
||||
@ -544,6 +552,7 @@ function OutputMailMerge(mailMergeSendData) {
|
||||
break;
|
||||
}
|
||||
this['recordCount'] = mailMergeSendData.getRecordCount();
|
||||
this['recordErrorCount'] = mailMergeSendData.getRecordErrorCount();
|
||||
this['to'] = null;
|
||||
this['recordIndex'] = null;
|
||||
} else {
|
||||
@ -555,6 +564,7 @@ function OutputMailMerge(mailMergeSendData) {
|
||||
this['type'] = null;
|
||||
this['recordCount'] = null;
|
||||
this['recordIndex'] = null;
|
||||
this['recordErrorCount'] = null;
|
||||
}
|
||||
}
|
||||
OutputMailMerge.prototype.getRecordIndex = function() {
|
||||
@ -563,6 +573,12 @@ OutputMailMerge.prototype.getRecordIndex = function() {
|
||||
OutputMailMerge.prototype.setRecordIndex = function(data) {
|
||||
return this['recordIndex'] = data;
|
||||
};
|
||||
OutputMailMerge.prototype.getRecordErrorCount = function() {
|
||||
return this['recordErrorCount'];
|
||||
};
|
||||
OutputMailMerge.prototype.setRecordErrorCount = function(data) {
|
||||
return this['recordErrorCount'] = data;
|
||||
};
|
||||
OutputMailMerge.prototype.getTo = function() {
|
||||
return this['to'];
|
||||
};
|
||||
@ -670,8 +686,7 @@ var c_oAscEncodingsMap = {"437": 43, "720": 1, "737": 21, "775": 5, "850": 39, "
|
||||
var c_oAscCodePageUtf8 = 46;//65001
|
||||
var c_oAscUserAction = {
|
||||
Out: 0,
|
||||
In: 1,
|
||||
AllIn: 2
|
||||
In: 1
|
||||
};
|
||||
var c_oAscServerCommandErrors = {
|
||||
NoError: 0,
|
||||
|
||||
@ -41,7 +41,8 @@ exports.LICENSE_RESULT = {
|
||||
Expired : 2,
|
||||
Success : 3,
|
||||
UnknownUser : 4,
|
||||
Connections : 5
|
||||
Connections : 5,
|
||||
ExpiredTrial: 6
|
||||
};
|
||||
|
||||
exports.LICENSE_CONNECTIONS = 21;
|
||||
@ -127,7 +128,6 @@ exports.CONVERT_DOWNLOAD = -81;
|
||||
exports.CONVERT_UNKNOWN_FORMAT = -82;
|
||||
exports.CONVERT_TIMEOUT = -83;
|
||||
exports.CONVERT_READ_FILE = -84;
|
||||
exports.CONVERT_MS_OFFCRYPTO = -85;
|
||||
exports.CONVERT_CORRUPTED = -86;
|
||||
exports.CONVERT_LIBREOFFICE = -87;
|
||||
exports.CONVERT_PARAMS = -88;
|
||||
@ -156,6 +156,9 @@ exports.EDITOR_TYPE_SPREADSHEET = 1;
|
||||
exports.EDITOR_TYPE_PRESENTATION = 2;
|
||||
exports.EDITOR_TYPE_CONVERTATION = 3;
|
||||
|
||||
exports.PACKAGE_TYPE_OS = 0;
|
||||
exports.PACKAGE_TYPE_I = 1;
|
||||
|
||||
exports.REDIS_KEY_PUBSUB = 'pubsub';
|
||||
exports.REDIS_KEY_SAVE_LOCK = 'savelock:';
|
||||
exports.REDIS_KEY_PRESENCE_HASH = 'presence:hash:';
|
||||
@ -170,6 +173,7 @@ exports.REDIS_KEY_FORCE_SAVE = 'forcesave:';
|
||||
exports.REDIS_KEY_SAVED = 'saved:';
|
||||
exports.REDIS_KEY_SHUTDOWN = 'shutdown';
|
||||
exports.REDIS_KEY_LICENSE = 'license';
|
||||
exports.REDIS_KEY_LICENSE_T = 'licenseT';
|
||||
|
||||
exports.SHUTDOWN_CODE = 4001;
|
||||
exports.SHUTDOWN_REASON = 'server shutdown';
|
||||
|
||||
@ -40,17 +40,20 @@ const utils = require('./utils');
|
||||
const pubsubRedis = require('./../../DocService/sources/pubsubRedis');
|
||||
const redisClient = pubsubRedis.getClientRedis();
|
||||
|
||||
const cfgRedisPrefix = config.get('services.CoAuthoring.redis.prefix');
|
||||
const redisKeyLicense = cfgRedisPrefix + constants.REDIS_KEY_LICENSE;
|
||||
|
||||
const buildVersion = '4.0.0';
|
||||
const buildNumber = 19;
|
||||
const buildDate = '6/29/2016';
|
||||
const oBuildDate = new Date(buildDate);
|
||||
const oPackageType = constants.PACKAGE_TYPE_OS;
|
||||
|
||||
const cfgRedisPrefix = config.get('services.CoAuthoring.redis.prefix');
|
||||
const redisKeyLicense = cfgRedisPrefix + ((constants.PACKAGE_TYPE_OS === oPackageType) ? constants.REDIS_KEY_LICENSE :
|
||||
constants.REDIS_KEY_LICENSE_T);
|
||||
|
||||
exports.readLicense = function*() {
|
||||
const resMax = {count: 999999, type: constants.LICENSE_RESULT.Success};
|
||||
var res = {count: 1, type: constants.LICENSE_RESULT.Error, light: false};
|
||||
const c_LR = constants.LICENSE_RESULT;
|
||||
const resMax = {count: 999999, type: c_LR.Success};
|
||||
var res = {count: 1, type: c_LR.Error, light: false, packageType: oPackageType};
|
||||
var checkFile = false;
|
||||
try {
|
||||
var oFile = fs.readFileSync(configL.get('license_file')).toString();
|
||||
@ -64,39 +67,64 @@ exports.readLicense = function*() {
|
||||
const publicKey = '-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDRhGF7X4A0ZVlEg594WmODVVUI\niiPQs04aLmvfg8SborHss5gQXu0aIdUT6nb5rTh5hD2yfpF2WIW6M8z0WxRhwicg\nXwi80H1aLPf6lEPPLvN29EhQNjBpkFkAJUbS8uuhJEeKw0cE49g80eBBF4BCqSL6\nPFQbP9/rByxdxEoAIQIDAQAB\n-----END PUBLIC KEY-----\n';
|
||||
if (verify.verify(publicKey, sign, 'hex')) {
|
||||
const endDate = new Date(oLicense['end_date']);
|
||||
const checkDate = (true === oLicense['trial'] || 'true' === oLicense['trial']) ? new Date() : oBuildDate; // Someone who likes to put json string instead of bool
|
||||
const isTrial = (true === oLicense['trial'] || 'true' === oLicense['trial']);
|
||||
const checkDate = isTrial ? new Date() : oBuildDate; // Someone who likes to put json string instead of bool
|
||||
if (endDate >= checkDate && 2 <= oLicense['version']) {
|
||||
res.count = Math.min(Math.max(res.count, oLicense['process'] >> 0), resMax.count);
|
||||
res.type = constants.LICENSE_RESULT.Success;
|
||||
res.type = c_LR.Success;
|
||||
} else {
|
||||
res.type = constants.LICENSE_RESULT.Expired;
|
||||
res.type = isTrial ? c_LR.ExpiredTrial : c_LR.Expired;
|
||||
}
|
||||
|
||||
res.light = (true === oLicense['light'] || 'true' === oLicense['light']); // Someone who likes to put json string instead of bool
|
||||
}
|
||||
} catch (e) {
|
||||
res.count = 1;
|
||||
res.type = constants.LICENSE_RESULT.Error;
|
||||
res.type = c_LR.Error;
|
||||
|
||||
if (checkFile || (yield* _getFileState())) {
|
||||
res.type = constants.LICENSE_RESULT.Expired;
|
||||
if (checkFile) {
|
||||
res.type = c_LR.ExpiredTrial;
|
||||
} else {
|
||||
if (constants.PACKAGE_TYPE_OS === oPackageType) {
|
||||
if (yield* _getFileState()) {
|
||||
res.type = c_LR.ExpiredTrial;
|
||||
}
|
||||
} else {
|
||||
res.type = (yield* _getFileState()) ? c_LR.Success : c_LR.ExpiredTrial;
|
||||
if (res.type === c_LR.Success) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (res.type === constants.LICENSE_RESULT.Expired) {
|
||||
res.count = 0;
|
||||
if (res.type === c_LR.Expired || res.type === c_LR.ExpiredTrial) {
|
||||
res.count = 1;
|
||||
logger.error('License Expired!!!');
|
||||
}
|
||||
|
||||
if (checkFile) {
|
||||
yield* _updateFileState();
|
||||
yield* _updateFileState(true);
|
||||
}
|
||||
|
||||
return res;
|
||||
};
|
||||
|
||||
function* _getFileState() {
|
||||
return yield utils.promiseRedis(redisClient, redisClient.hget, redisKeyLicense, redisKeyLicense);
|
||||
const val = yield utils.promiseRedis(redisClient, redisClient.hget, redisKeyLicense, redisKeyLicense);
|
||||
if (constants.PACKAGE_TYPE_OS === oPackageType) {
|
||||
return val;
|
||||
}
|
||||
|
||||
if (null === val) {
|
||||
yield* _updateFileState(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
var now = new Date();
|
||||
now.setMonth(now.getMonth() - 1);
|
||||
return (0 >= (now - new Date(val)));
|
||||
}
|
||||
function* _updateFileState() {
|
||||
yield utils.promiseRedis(redisClient, redisClient.hset, redisKeyLicense, redisKeyLicense, redisKeyLicense);
|
||||
function* _updateFileState(state) {
|
||||
const val = constants.PACKAGE_TYPE_OS === oPackageType ? redisKeyLicense : (state ? new Date(1) : new Date());
|
||||
yield utils.promiseRedis(redisClient, redisClient.hset, redisKeyLicense, redisKeyLicense, val);
|
||||
}
|
||||
|
||||
@ -72,7 +72,7 @@ function connetPromise(closeCallback) {
|
||||
var closeEventCallback = function() {
|
||||
//in some case receive multiple close events
|
||||
conn.removeListener('close', closeEventCallback);
|
||||
console.debug("[AMQP] conn close");
|
||||
logger.debug('[AMQP] conn close');
|
||||
closeCallback();
|
||||
};
|
||||
conn.on('close', closeEventCallback);
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -119,24 +119,14 @@ function clear(taskqueue) {
|
||||
taskqueue.channelConvertResponseReceive = null;
|
||||
}
|
||||
function repeat(taskqueue) {
|
||||
var i;
|
||||
for (i = 0; i < taskqueue.addTaskStore.length; ++i) {
|
||||
//repeat addTask because they are lost after the reconnection
|
||||
//unlike unconfirmed task will come again
|
||||
//acknowledge data after reconnect raises an exception 'PRECONDITION_FAILED - unknown delivery tag'
|
||||
for (var i = 0; i < taskqueue.addTaskStore.length; ++i) {
|
||||
var elem = taskqueue.addTaskStore[i];
|
||||
addTask(taskqueue, elem.task, elem.priority, function () {});
|
||||
}
|
||||
for (i = 0; i < taskqueue.addResponseStore.length; ++i) {
|
||||
addResponse(taskqueue, taskqueue.addResponseStore[i], function () {});
|
||||
}
|
||||
for (i = 0; i < taskqueue.removeTaskStore.length; ++i) {
|
||||
removeTask(taskqueue, taskqueue.removeTaskStore[i]);
|
||||
}
|
||||
for (i = 0; i < taskqueue.removeResponseStore.length; ++i) {
|
||||
removeResponse(taskqueue, taskqueue.removeResponseStore[i]);
|
||||
}
|
||||
taskqueue.addTaskStore.length = 0;
|
||||
taskqueue.addResponseStore.length = 0;
|
||||
taskqueue.removeTaskStore.length = 0;
|
||||
taskqueue.removeResponseStore.length = 0;
|
||||
}
|
||||
function addTask(taskqueue, content, priority, callback) {
|
||||
var options = {persistent: true, priority: priority};
|
||||
@ -161,9 +151,6 @@ function TaskQueueRabbitMQ() {
|
||||
this.channelConvertResponse = null;
|
||||
this.channelConvertResponseReceive = null;
|
||||
this.addTaskStore = [];
|
||||
this.addResponseStore = [];
|
||||
this.removeTaskStore = [];
|
||||
this.removeResponseStore = [];
|
||||
}
|
||||
util.inherits(TaskQueueRabbitMQ, events.EventEmitter);
|
||||
TaskQueueRabbitMQ.prototype.init = function (isAddTask, isAddResponse, isAddTaskReceive, isAddResponseReceive, callback) {
|
||||
@ -214,7 +201,6 @@ TaskQueueRabbitMQ.prototype.addResponse = function (task) {
|
||||
}
|
||||
});
|
||||
} else {
|
||||
t.addResponseStore.push(content);
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
@ -224,8 +210,6 @@ TaskQueueRabbitMQ.prototype.removeTask = function (data) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
if (null != t.channelConvertTaskReceive) {
|
||||
removeTask(t, data);
|
||||
} else {
|
||||
t.removeTaskStore.push(data);
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
@ -235,8 +219,6 @@ TaskQueueRabbitMQ.prototype.removeResponse = function (data) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
if (null != t.channelConvertResponseReceive) {
|
||||
removeResponse(t, data);
|
||||
} else {
|
||||
t.removeResponseStore.push(data);
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
|
||||
@ -29,16 +29,33 @@
|
||||
* terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
*
|
||||
*/
|
||||
|
||||
var config = require('config');
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var url = require('url');
|
||||
var request = require('request');
|
||||
var co = require('co');
|
||||
var URI = require("uri-js");
|
||||
const escapeStringRegexp = require('escape-string-regexp');
|
||||
var constants = require('./constants');
|
||||
|
||||
var configIpFilter = config.get('services.CoAuthoring.ipfilter');
|
||||
var cfgIpFilterRules = configIpFilter.get('rules');
|
||||
var cfgIpFilterErrorCode = configIpFilter.get('errorcode');
|
||||
|
||||
var ANDROID_SAFE_FILENAME = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ._-+,@£$€!½§~\'=()[]{}0123456789';
|
||||
|
||||
var g_oIpFilterRules = function() {
|
||||
var res = [];
|
||||
for (var i = 0; i < cfgIpFilterRules.length; ++i) {
|
||||
var rule = cfgIpFilterRules[i];
|
||||
var regExpStr = rule['address'].split('*').map(escapeStringRegexp).join('.*');
|
||||
var exp = new RegExp('^' + regExpStr + '$', 'i');
|
||||
res.push({allow: rule['allowed'], exp: exp});
|
||||
}
|
||||
return res;
|
||||
}();
|
||||
|
||||
exports.addSeconds = function(date, sec) {
|
||||
date.setSeconds(date.getSeconds() + sec);
|
||||
};
|
||||
@ -192,19 +209,16 @@ exports.getContentDisposition = getContentDisposition;
|
||||
exports.getContentDispositionS3 = getContentDispositionS3;
|
||||
function downloadUrlPromise(uri, optTimeout, optLimit) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
//todo может стоит делать url.parse, а потом с каждой частью отдельно работать
|
||||
//для ссылок с руссикими буквами приходит 404
|
||||
if (!containsAllAsciiNP(uri)) {
|
||||
uri = encodeURI(uri);
|
||||
}
|
||||
//IRI to URI
|
||||
uri = URI.serialize(URI.parse(uri));
|
||||
var urlParsed = url.parse(uri);
|
||||
//if you expect binary data, you should set encoding: null
|
||||
var options = {uri: urlParsed, encoding: null, timeout: optTimeout};
|
||||
if (urlParsed.protocol === 'https:') {
|
||||
//TODO: Check how to correct handle a ssl link
|
||||
urlParsed.rejectUnauthorized = false;
|
||||
options.rejectUnauthorized = false;
|
||||
}
|
||||
|
||||
//TODO: Check how to correct handle a ssl link
|
||||
urlParsed.rejectUnauthorized = false;
|
||||
options.rejectUnauthorized = false;
|
||||
|
||||
request.get(options, function (err, response, body) {
|
||||
if (err) {
|
||||
reject(err);
|
||||
@ -227,18 +241,15 @@ function downloadUrlPromise(uri, optTimeout, optLimit) {
|
||||
}
|
||||
function postRequestPromise(uri, postData, optTimeout) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
//todo может стоит делать url.parse, а потом с каждой частью отдельно работать
|
||||
//для ссылок с руссикими буквами приходит 404
|
||||
if (!containsAllAsciiNP(uri)) {
|
||||
uri = encodeURI(uri);
|
||||
}
|
||||
//IRI to URI
|
||||
uri = URI.serialize(URI.parse(uri));
|
||||
var urlParsed = url.parse(uri);
|
||||
var options = {uri: urlParsed, body: postData, encoding: 'utf8', headers: {'Content-Type': 'application/json'}, timeout: optTimeout};
|
||||
if (urlParsed.protocol === 'https:') {
|
||||
//TODO: Check how to correct handle a ssl link
|
||||
urlParsed.rejectUnauthorized = false;
|
||||
options.rejectUnauthorized = false;
|
||||
}
|
||||
|
||||
//TODO: Check how to correct handle a ssl link
|
||||
urlParsed.rejectUnauthorized = false;
|
||||
options.rejectUnauthorized = false;
|
||||
|
||||
request.post(options, function(err, response, body) {
|
||||
if (err) {
|
||||
reject(err);
|
||||
@ -337,7 +348,7 @@ exports.fillXmlResponse = function(res, uri, error) {
|
||||
res.setHeader('Content-Length', body.length);
|
||||
res.send(body);
|
||||
};
|
||||
exports.promiseCreateWriteStream = function(strPath, optOptions) {
|
||||
function promiseCreateWriteStream(strPath, optOptions) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
var file = fs.createWriteStream(strPath, optOptions);
|
||||
var errorCallback = function(e) {
|
||||
@ -350,7 +361,8 @@ exports.promiseCreateWriteStream = function(strPath, optOptions) {
|
||||
});
|
||||
});
|
||||
};
|
||||
exports.promiseCreateReadStream = function(strPath) {
|
||||
exports.promiseCreateWriteStream = promiseCreateWriteStream;
|
||||
function promiseCreateReadStream(strPath) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
var file = fs.createReadStream(strPath);
|
||||
var errorCallback = function(e) {
|
||||
@ -363,6 +375,7 @@ exports.promiseCreateReadStream = function(strPath) {
|
||||
});
|
||||
});
|
||||
};
|
||||
exports.promiseCreateReadStream = promiseCreateReadStream;
|
||||
exports.compareStringByLength = function(x, y) {
|
||||
if (x && y) {
|
||||
if (x.length == y.length) {
|
||||
@ -480,3 +493,35 @@ function changeOnlyOfficeUrl(inputUrl, strPath, optFilename) {
|
||||
return inputUrl + constants.ONLY_OFFICE_URL_PARAM + '=' + constants.OUTPUT_NAME + path.extname(optFilename || strPath);
|
||||
}
|
||||
exports.changeOnlyOfficeUrl = changeOnlyOfficeUrl;
|
||||
function pipeStreams(from, to, isEnd) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
from.pipe(to, {end: isEnd});
|
||||
from.on('end', function() {
|
||||
resolve();
|
||||
});
|
||||
from.on('error', function(e) {
|
||||
reject(e);
|
||||
});
|
||||
});
|
||||
}
|
||||
exports.pipeStreams = pipeStreams;
|
||||
function* pipeFiles(from, to) {
|
||||
var fromStream = yield promiseCreateReadStream(from);
|
||||
var toStream = yield promiseCreateWriteStream(to);
|
||||
yield pipeStreams(fromStream, toStream, true);
|
||||
}
|
||||
exports.pipeFiles = co.wrap(pipeFiles);
|
||||
function checkIpFilter(hostname) {
|
||||
var status = 0;
|
||||
for (var i = 0; i < g_oIpFilterRules.length; ++i) {
|
||||
var rule = g_oIpFilterRules[i];
|
||||
if (rule.exp.test(hostname)) {
|
||||
if (!rule.allow) {
|
||||
status = cfgIpFilterErrorCode;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
exports.checkIpFilter = checkIpFilter;
|
||||
|
||||
@ -5,17 +5,20 @@
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"base64-stream": "^0.1.3",
|
||||
"body-parser": "^1.14.2",
|
||||
"body-parser": "^1.15.2",
|
||||
"co": "^4.6.0",
|
||||
"config": "^1.19.0",
|
||||
"config": "^1.21.0",
|
||||
"cron": "^1.1.0",
|
||||
"express": "^4.13.4",
|
||||
"fakeredis": "^1.0.2",
|
||||
"express": "^4.14.0",
|
||||
"fakeredis": "^1.0.3",
|
||||
"forwarded": "^0.1.0",
|
||||
"ipaddr.js": "^1.2.0",
|
||||
"mime": "^1.3.4",
|
||||
"multiparty": "^4.1.2",
|
||||
"mysql": "^2.10.2",
|
||||
"pg": "^4.4.4",
|
||||
"redis": "^2.4.2",
|
||||
"mysql": "^2.11.1",
|
||||
"pg": "^6.1.0",
|
||||
"pg-escape": "^0.2.0",
|
||||
"redis": "^2.6.2",
|
||||
"sockjs": "http://d2ettrnqo7v976.cloudfront.net/npm/sockjs-node/v0.3.15.14.tar.gz",
|
||||
"underscore": "^1.8.3"
|
||||
}
|
||||
|
||||
@ -49,7 +49,7 @@
|
||||
* d) Когда пользователь остается один, после принятия чужих изменений начинается пункт 'а'
|
||||
*-----------------------------------------------------------------------------------------------------------------------
|
||||
*--------------------------------------------Схема работы с сервером----------------------------------------------------
|
||||
* а) Когда все уходят, спустя время c_oAscSaveTimeOutDelay на сервер документов шлется команда на сборку.
|
||||
* а) Когда все уходят, спустя время cfgAscSaveTimeOutDelay на сервер документов шлется команда на сборку.
|
||||
* b) Если приходит статус '1' на CommandService.ashx, то удалось сохранить и поднять версию. Очищаем callback-и и
|
||||
* изменения из базы и из памяти.
|
||||
* с) Если приходит статус, отличный от '1'(сюда можно отнести как генерацию файла, так и работа внешнего подписчика
|
||||
@ -95,6 +95,8 @@ var pubsubService = require('./' + config.get('pubsub.name'));
|
||||
var queueService = require('./../../Common/sources/taskqueueRabbitMQ');
|
||||
var cfgSpellcheckerUrl = config.get('server.editor_settings_spellchecker_url');
|
||||
var cfgCallbackRequestTimeout = config.get('server.callbackRequestTimeout');
|
||||
//The waiting time to document assembly when all out(not 0 in case of F5 in the browser)
|
||||
var cfgAscSaveTimeOutDelay = config.get('server.savetimeoutdelay');
|
||||
|
||||
var cfgPubSubMaxChanges = config.get('pubsub.maxChanges');
|
||||
|
||||
@ -183,7 +185,6 @@ var c_oAscChangeBase = {
|
||||
All: 2
|
||||
};
|
||||
|
||||
var c_oAscSaveTimeOutDelay = 5000; // Время ожидания для сохранения на сервере (для отработки F5 в браузере)
|
||||
var c_oAscLockTimeOutDelay = 500; // Время ожидания для сохранения, когда зажата база данных
|
||||
|
||||
var c_oAscRecalcIndexTypes = {
|
||||
@ -531,7 +532,10 @@ function* sendServerRequest(docId, uri, postData) {
|
||||
function parseUrl(callbackUrl) {
|
||||
var result = null;
|
||||
try {
|
||||
var parseObject = url.parse(decodeURIComponent(callbackUrl));
|
||||
//делать decodeURIComponent не нужно http://expressjs.com/en/4x/api.html#app.settings.table
|
||||
//по умолчанию express использует 'query parser' = 'extended', но даже в 'simple' версии делается decode
|
||||
//percent-encoded characters within the query string will be assumed to use UTF-8 encoding
|
||||
var parseObject = url.parse(callbackUrl);
|
||||
var isHttps = 'https:' === parseObject.protocol;
|
||||
var port = parseObject.port;
|
||||
if (!port) {
|
||||
@ -601,11 +605,14 @@ function* setForceSave(docId, lastSave, savePathDoc) {
|
||||
* @param callback
|
||||
* @param baseUrl
|
||||
*/
|
||||
function* sendStatusDocument(docId, bChangeBase, userAction, callback, baseUrl) {
|
||||
function* sendStatusDocument(docId, bChangeBase, userAction, callback, baseUrl, opt_userData) {
|
||||
if (!callback) {
|
||||
var getRes = yield* getCallback(docId);
|
||||
if(getRes) {
|
||||
if (getRes) {
|
||||
callback = getRes.server;
|
||||
if (!baseUrl) {
|
||||
baseUrl = getRes.baseUrl;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (null == callback) {
|
||||
@ -634,21 +641,14 @@ function* sendStatusDocument(docId, bChangeBase, userAction, callback, baseUrl)
|
||||
var sendData = new commonDefines.OutputSfcData();
|
||||
sendData.setKey(docId);
|
||||
sendData.setStatus(status);
|
||||
if(c_oAscServerStatus.Closed !== status){
|
||||
if (c_oAscServerStatus.Closed !== status) {
|
||||
sendData.setUsers(participants);
|
||||
} else {
|
||||
sendData.setUsers(undefined);
|
||||
}
|
||||
if (userAction) {
|
||||
var actions = [];
|
||||
if (commonDefines.c_oAscUserAction.AllIn === userAction.type) {
|
||||
for (var i = 0; i < participants.length; ++i) {
|
||||
actions.push(new commonDefines.OutputAction(commonDefines.c_oAscUserAction.In, participants[i]));
|
||||
}
|
||||
} else {
|
||||
actions.push(userAction);
|
||||
}
|
||||
sendData.setActions(actions);
|
||||
sendData.setActions([userAction]);
|
||||
}
|
||||
if (opt_userData) {
|
||||
sendData.setUserData(opt_userData);
|
||||
}
|
||||
var uri = callback.href;
|
||||
var replyData = null;
|
||||
@ -705,26 +705,34 @@ function dropUserFromDocument(docId, userId, description) {
|
||||
}
|
||||
|
||||
// Подписка на эвенты:
|
||||
function* bindEvents(docId, callback, baseUrl, opt_userAction) {
|
||||
function* bindEvents(docId, callback, baseUrl, opt_userAction, opt_userData) {
|
||||
// Подписка на эвенты:
|
||||
// - если пользователей нет и изменений нет, то отсылаем статус "закрыто" и в базу не добавляем
|
||||
// - если пользователей нет, а изменения есть, то отсылаем статус "редактируем" без пользователей, но добавляем в базу
|
||||
// - если есть пользователи, то просто добавляем в базу
|
||||
var bChangeBase = c_oAscChangeBase.Delete;
|
||||
var getRes = yield* getCallback(docId);
|
||||
var bChangeBase;
|
||||
var oCallbackUrl;
|
||||
var getRes = yield* getCallback(docId);
|
||||
if (getRes) {
|
||||
oCallbackUrl = getRes.server;
|
||||
bChangeBase = c_oAscChangeBase.Delete;
|
||||
} else {
|
||||
oCallbackUrl = parseUrl(callback);
|
||||
if (null === oCallbackUrl) {
|
||||
return commonDefines.c_oAscServerCommandErrors.ParseError;
|
||||
}
|
||||
bChangeBase = c_oAscChangeBase.All;
|
||||
if (null !== oCallbackUrl) {
|
||||
if (utils.checkIpFilter(oCallbackUrl.host) > 0) {
|
||||
logger.error('checkIpFilter error: docId = %s;url = %s', docId, callback);
|
||||
//todo add new error type
|
||||
oCallbackUrl = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (null === oCallbackUrl) {
|
||||
return commonDefines.c_oAscServerCommandErrors.ParseError;
|
||||
} else {
|
||||
yield* sendStatusDocument(docId, bChangeBase, opt_userAction, oCallbackUrl, baseUrl, opt_userData);
|
||||
return commonDefines.c_oAscServerCommandErrors.NoError;
|
||||
}
|
||||
var userAction = opt_userAction ? opt_userAction : new commonDefines.OutputAction(commonDefines.c_oAscUserAction.AllIn, null);
|
||||
yield* sendStatusDocument(docId, bChangeBase, userAction, oCallbackUrl, baseUrl);
|
||||
return commonDefines.c_oAscServerCommandErrors.NoError;
|
||||
}
|
||||
|
||||
function* cleanDocumentOnExit(docId, deleteChanges) {
|
||||
@ -742,7 +750,7 @@ function* cleanDocumentOnExit(docId, deleteChanges) {
|
||||
function* cleanDocumentOnExitNoChanges(docId, opt_userId) {
|
||||
var userAction = opt_userId ? new commonDefines.OutputAction(commonDefines.c_oAscUserAction.Out, opt_userId) : null;
|
||||
// Отправляем, что все ушли и нет изменений (чтобы выставить статус на сервере об окончании редактирования)
|
||||
yield* sendStatusDocument(docId, c_oAscChangeBase.All, userAction);
|
||||
yield* sendStatusDocument(docId, c_oAscChangeBase.No, userAction);
|
||||
//если пользователь зашел в документ, соединение порвалось, на сервере удалилась вся информация,
|
||||
//при восстановлении соединения userIndex сохранится и он совпадет с userIndex следующего пользователя
|
||||
yield* cleanDocumentOnExit(docId, false);
|
||||
@ -758,7 +766,7 @@ function* _createSaveTimer(docId, opt_userId, opt_queue, opt_noDelay) {
|
||||
var updateIfRes = yield taskResult.updateIf(updateTask, updateMask);
|
||||
if (updateIfRes.affectedRows > 0) {
|
||||
if(!opt_noDelay){
|
||||
yield utils.sleep(c_oAscSaveTimeOutDelay);
|
||||
yield utils.sleep(cfgAscSaveTimeOutDelay);
|
||||
}
|
||||
while (true) {
|
||||
if (!sqlBase.isLockCriticalSection(docId)) {
|
||||
@ -825,6 +833,10 @@ exports.install = function(server, callbackFunction) {
|
||||
logger.debug('Server shutdown receive data');
|
||||
return;
|
||||
}
|
||||
if (conn.isCiriticalError && ('message' == data.type || 'getLock' == data.type || 'saveChanges' == data.type)) {
|
||||
logger.warn("conn.isCiriticalError send command: docId = %s type = %s", docId, data.type);
|
||||
return;
|
||||
}
|
||||
switch (data.type) {
|
||||
case 'auth' :
|
||||
yield* auth(conn, data);
|
||||
@ -1085,6 +1097,7 @@ exports.install = function(server, callbackFunction) {
|
||||
|
||||
function sendFileError(conn, errorId) {
|
||||
logger.error('error description: docId = %s errorId = %s', conn.docId, errorId);
|
||||
conn.isCiriticalError = true;
|
||||
sendData(conn, {type: 'error', description: errorId});
|
||||
}
|
||||
|
||||
@ -1235,6 +1248,16 @@ exports.install = function(server, callbackFunction) {
|
||||
return resultLock;
|
||||
}
|
||||
|
||||
function* authRestore(conn, sessionId) {
|
||||
conn.sessionId = sessionId;//restore old
|
||||
//Kill previous connections
|
||||
connections = _.reject(connections, function(el) {
|
||||
return el.sessionId === sessionId;//Delete this connection
|
||||
});
|
||||
|
||||
yield* endAuth(conn, true);
|
||||
}
|
||||
|
||||
function* auth(conn, data) {
|
||||
// Проверка версий
|
||||
if (data.version !== asc_coAuthV) {
|
||||
@ -1305,76 +1328,73 @@ exports.install = function(server, callbackFunction) {
|
||||
if (bIsRestore) {
|
||||
logger.info("restored old session: docId = %s id = %s", docId, data.sessionId);
|
||||
|
||||
// Останавливаем сборку (вдруг она началась)
|
||||
// Когда переподсоединение, нам нужна проверка на сборку файла
|
||||
try {
|
||||
var result = yield sqlBase.checkStatusFilePromise(docId);
|
||||
if (!conn.user.view) {
|
||||
// Останавливаем сборку (вдруг она началась)
|
||||
// Когда переподсоединение, нам нужна проверка на сборку файла
|
||||
try {
|
||||
var result = yield sqlBase.checkStatusFilePromise(docId);
|
||||
|
||||
var status = result && result.length > 0 ? result[0]['status'] : null;
|
||||
if (taskResult.FileStatus.Ok === status) {
|
||||
// Все хорошо, статус обновлять не нужно
|
||||
} else if (taskResult.FileStatus.SaveVersion === status) {
|
||||
// Обновим статус файла (идет сборка, нужно ее остановить)
|
||||
var updateMask = new taskResult.TaskResultData();
|
||||
updateMask.key = docId;
|
||||
updateMask.status = status;
|
||||
updateMask.statusInfo = result[0]['status_info'];
|
||||
var updateTask = new taskResult.TaskResultData();
|
||||
updateTask.status = taskResult.FileStatus.Ok;
|
||||
updateTask.statusInfo = constants.NO_ERROR;
|
||||
var updateIfRes = yield taskResult.updateIf(updateTask, updateMask);
|
||||
if (!(updateIfRes.affectedRows > 0)) {
|
||||
var status = result && result.length > 0 ? result[0]['status'] : null;
|
||||
if (taskResult.FileStatus.Ok === status) {
|
||||
// Все хорошо, статус обновлять не нужно
|
||||
} else if (taskResult.FileStatus.SaveVersion === status) {
|
||||
// Обновим статус файла (идет сборка, нужно ее остановить)
|
||||
var updateMask = new taskResult.TaskResultData();
|
||||
updateMask.key = docId;
|
||||
updateMask.status = status;
|
||||
updateMask.statusInfo = result[0]['status_info'];
|
||||
var updateTask = new taskResult.TaskResultData();
|
||||
updateTask.status = taskResult.FileStatus.Ok;
|
||||
updateTask.statusInfo = constants.NO_ERROR;
|
||||
var updateIfRes = yield taskResult.updateIf(updateTask, updateMask);
|
||||
if (!(updateIfRes.affectedRows > 0)) {
|
||||
// error version
|
||||
sendFileError(conn, 'Update Version error');
|
||||
return;
|
||||
}
|
||||
} else if (taskResult.FileStatus.UpdateVersion === status) {
|
||||
// error version
|
||||
sendFileError(conn, 'Update Version error');
|
||||
return;
|
||||
} else {
|
||||
// Other error
|
||||
sendFileError(conn, 'Other error');
|
||||
return;
|
||||
}
|
||||
} else if (taskResult.FileStatus.UpdateVersion === status) {
|
||||
// error version
|
||||
sendFileError(conn, 'Update Version error');
|
||||
return;
|
||||
} else {
|
||||
// Other error
|
||||
sendFileError(conn, 'Other error');
|
||||
return;
|
||||
}
|
||||
|
||||
var objChangesDocument = yield* getDocumentChanges(docId);
|
||||
var bIsSuccessRestore = true;
|
||||
if (objChangesDocument && 0 < objChangesDocument.arrChanges.length) {
|
||||
var change = objChangesDocument.arrChanges[objChangesDocument.getLength() - 1];
|
||||
if (change['change']) {
|
||||
if (change['user'] !== curUserId) {
|
||||
bIsSuccessRestore = 0 === (((data['lastOtherSaveTime'] - change['time']) / 1000) >> 0);
|
||||
var objChangesDocument = yield* getDocumentChanges(docId);
|
||||
var bIsSuccessRestore = true;
|
||||
if (objChangesDocument && 0 < objChangesDocument.arrChanges.length) {
|
||||
var change = objChangesDocument.arrChanges[objChangesDocument.getLength() - 1];
|
||||
if (change['change']) {
|
||||
if (change['user'] !== curUserId) {
|
||||
bIsSuccessRestore = 0 === (((data['lastOtherSaveTime'] - change['time']) / 1000) >> 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bIsSuccessRestore) {
|
||||
conn.sessionId = data.sessionId;//restore old
|
||||
|
||||
// Проверяем lock-и
|
||||
var arrayBlocks = data['block'];
|
||||
var getLockRes = yield* getLock(conn, data, true);
|
||||
if (arrayBlocks && (0 === arrayBlocks.length || getLockRes)) {
|
||||
//Kill previous connections
|
||||
connections = _.reject(connections, function(el) {
|
||||
return el.sessionId === data.sessionId;//Delete this connection
|
||||
});
|
||||
|
||||
yield* endAuth(conn, true);
|
||||
if (bIsSuccessRestore) {
|
||||
// Проверяем lock-и
|
||||
var arrayBlocks = data['block'];
|
||||
var getLockRes = yield* getLock(conn, data, true);
|
||||
if (arrayBlocks && (0 === arrayBlocks.length || getLockRes)) {
|
||||
yield* authRestore(conn, data.sessionId);
|
||||
} else {
|
||||
sendFileError(conn, 'Restore error. Locks not checked.');
|
||||
}
|
||||
} else {
|
||||
sendFileError(conn, 'Restore error. Locks not checked.');
|
||||
sendFileError(conn, 'Restore error. Document modified.');
|
||||
}
|
||||
} else {
|
||||
sendFileError(conn, 'Restore error. Document modified.');
|
||||
} catch (err) {
|
||||
sendFileError(conn, 'DataBase error\r\n' + err.stack);
|
||||
}
|
||||
} catch (err) {
|
||||
sendFileError(conn, 'DataBase error\r\n' + err.stack);
|
||||
} else {
|
||||
yield* authRestore(conn, data.sessionId);
|
||||
}
|
||||
} else {
|
||||
conn.sessionId = conn.id;
|
||||
yield* endAuth(conn, false, data.documentCallbackUrl);
|
||||
if (cmd) {
|
||||
var endAuthRes = yield* endAuth(conn, false, data.documentCallbackUrl);
|
||||
if (endAuthRes && cmd) {
|
||||
yield canvasService.openDocument(conn, cmd, upsertRes);
|
||||
}
|
||||
}
|
||||
@ -1382,6 +1402,7 @@ exports.install = function(server, callbackFunction) {
|
||||
}
|
||||
|
||||
function* endAuth(conn, bIsRestore, documentCallbackUrl) {
|
||||
var res = true;
|
||||
var docId = conn.docId;
|
||||
var tmpUser = conn.user;
|
||||
connections.push(conn);
|
||||
@ -1399,48 +1420,56 @@ exports.install = function(server, callbackFunction) {
|
||||
}
|
||||
|
||||
// Отправляем на внешний callback только для тех, кто редактирует
|
||||
var bindEventsRes = commonDefines.c_oAscServerCommandErrors.NoError;
|
||||
if (!tmpUser.view) {
|
||||
var userAction = new commonDefines.OutputAction(commonDefines.c_oAscUserAction.In, tmpUser.idOriginal);
|
||||
// Если пришла информация о ссылке для посылания информации, то добавляем
|
||||
if (documentCallbackUrl) {
|
||||
yield* bindEvents(docId, documentCallbackUrl, conn.baseUrl, userAction);
|
||||
bindEventsRes = yield* bindEvents(docId, documentCallbackUrl, conn.baseUrl, userAction);
|
||||
} else {
|
||||
yield* sendStatusDocument(docId, c_oAscChangeBase.No, userAction);
|
||||
}
|
||||
}
|
||||
var lockDocument = null;
|
||||
if (!bIsRestore && 2 === countNoView && !tmpUser.view) {
|
||||
// Ставим lock на документ
|
||||
var isLock = yield utils.promiseRedis(redisClient, redisClient.setnx,
|
||||
redisKeyLockDoc + docId, JSON.stringify(firstParticipantNoView));
|
||||
if(isLock) {
|
||||
lockDocument = firstParticipantNoView;
|
||||
yield utils.promiseRedis(redisClient, redisClient.expire, redisKeyLockDoc + docId, cfgExpLockDoc);
|
||||
}
|
||||
}
|
||||
if (!lockDocument) {
|
||||
var getRes = yield utils.promiseRedis(redisClient, redisClient.get, redisKeyLockDoc + docId);
|
||||
if (getRes) {
|
||||
lockDocument = JSON.parse(getRes);
|
||||
}
|
||||
}
|
||||
|
||||
if (lockDocument && !tmpUser.view) {
|
||||
// Для view не ждем снятия lock-а
|
||||
var sendObject = {
|
||||
type: "waitAuth",
|
||||
lockDocument: lockDocument
|
||||
};
|
||||
sendData(conn, sendObject);//Or 0 if fails
|
||||
} else {
|
||||
if (bIsRestore) {
|
||||
yield* sendAuthInfo(undefined, undefined, conn, participantsMap);
|
||||
} else {
|
||||
var objChangesDocument = yield* getDocumentChanges(docId);
|
||||
yield* sendAuthInfo(objChangesDocument.arrChanges, objChangesDocument.getLength(), conn, participantsMap);
|
||||
if (commonDefines.c_oAscServerCommandErrors.NoError === bindEventsRes) {
|
||||
var lockDocument = null;
|
||||
if (!bIsRestore && 2 === countNoView && !tmpUser.view) {
|
||||
// Ставим lock на документ
|
||||
var isLock = yield utils.promiseRedis(redisClient, redisClient.setnx,
|
||||
redisKeyLockDoc + docId, JSON.stringify(firstParticipantNoView));
|
||||
if (isLock) {
|
||||
lockDocument = firstParticipantNoView;
|
||||
yield utils.promiseRedis(redisClient, redisClient.expire, redisKeyLockDoc + docId, cfgExpLockDoc);
|
||||
}
|
||||
}
|
||||
if (!lockDocument) {
|
||||
var getRes = yield utils.promiseRedis(redisClient, redisClient.get, redisKeyLockDoc + docId);
|
||||
if (getRes) {
|
||||
lockDocument = JSON.parse(getRes);
|
||||
}
|
||||
}
|
||||
|
||||
if (lockDocument && !tmpUser.view) {
|
||||
// Для view не ждем снятия lock-а
|
||||
var sendObject = {
|
||||
type: "waitAuth",
|
||||
lockDocument: lockDocument
|
||||
};
|
||||
sendData(conn, sendObject);//Or 0 if fails
|
||||
} else {
|
||||
if (bIsRestore) {
|
||||
yield* sendAuthInfo(undefined, undefined, conn, participantsMap);
|
||||
} else {
|
||||
var objChangesDocument = yield* getDocumentChanges(docId);
|
||||
yield* sendAuthInfo(objChangesDocument.arrChanges, objChangesDocument.getLength(), conn, participantsMap);
|
||||
}
|
||||
}
|
||||
yield* publish({type: commonDefines.c_oPublishType.participantsState, docId: docId, user: tmpUser, state: true}, docId, tmpUser.id);
|
||||
} else {
|
||||
sendFileError(conn, 'ip filter');
|
||||
res = false;
|
||||
}
|
||||
yield* publish({type: commonDefines.c_oPublishType.participantsState, docId: docId, user: tmpUser, state: true}, docId, tmpUser.id);
|
||||
return res;
|
||||
}
|
||||
|
||||
function* sendAuthInfo(objChangesDocument, changesIndex, conn, participantsMap) {
|
||||
@ -1860,9 +1889,10 @@ exports.install = function(server, callbackFunction) {
|
||||
function _checkLicense(conn) {
|
||||
return co(function* () {
|
||||
try {
|
||||
const c_LR = constants.LICENSE_RESULT;
|
||||
var licenseType = licenseInfo.type;
|
||||
if (constants.LICENSE_RESULT.Success !== licenseType) {
|
||||
licenseType = constants.LICENSE_RESULT.Success;
|
||||
if (constants.PACKAGE_TYPE_OS === licenseInfo.packageType && c_LR.Error === licenseType) {
|
||||
licenseType = c_LR.Success;
|
||||
|
||||
var count = constants.LICENSE_CONNECTIONS;
|
||||
var cursor = '0', sum = 0, scanRes, tmp, length, i, users;
|
||||
@ -1873,7 +1903,7 @@ exports.install = function(server, callbackFunction) {
|
||||
|
||||
for (i = 0; i < length; ++i) {
|
||||
if (sum >= count) {
|
||||
licenseType = constants.LICENSE_RESULT.Connections;
|
||||
licenseType = c_LR.Connections;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1882,7 +1912,7 @@ exports.install = function(server, callbackFunction) {
|
||||
}
|
||||
|
||||
if (sum >= count) {
|
||||
licenseType = constants.LICENSE_RESULT.Connections;
|
||||
licenseType = c_LR.Connections;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -2057,8 +2087,13 @@ exports.install = function(server, callbackFunction) {
|
||||
}
|
||||
});
|
||||
}
|
||||
var innerPintJob = new cron.CronJob(cfgExpDocumentsCron, expireDoc);
|
||||
innerPintJob.start();
|
||||
var innerPingJob = function(opt_isStart) {
|
||||
if (!opt_isStart) {
|
||||
logger.warn('expireDoc restart');
|
||||
}
|
||||
new cron.CronJob(cfgExpDocumentsCron, expireDoc, innerPingJob, true);
|
||||
};
|
||||
innerPingJob(true);
|
||||
|
||||
pubsub = new pubsubService();
|
||||
pubsub.on('message', pubsubOnMessage);
|
||||
@ -2096,7 +2131,13 @@ exports.commandFromServer = function (req, res) {
|
||||
logger.debug('Start commandFromServer: docId = %s c = %s', docId, query.c);
|
||||
switch (query.c) {
|
||||
case 'info':
|
||||
result = yield* bindEvents(docId, query.callback, utils.getBaseUrlByRequest(req));
|
||||
//If no files in the database means they have not been edited.
|
||||
var selectRes = yield taskResult.select(docId);
|
||||
if (selectRes.length > 0) {
|
||||
result = yield* bindEvents(docId, query.callback, utils.getBaseUrlByRequest(req), undefined, query.userdata);
|
||||
} else {
|
||||
result = commonDefines.c_oAscServerCommandErrors.DocumentIdError;
|
||||
}
|
||||
break;
|
||||
case 'drop':
|
||||
if (query.userid) {
|
||||
|
||||
@ -81,39 +81,8 @@ exports.loadTable = function (tableId, callbackFunction) {
|
||||
var sqlCommand = "SELECT * FROM " + table + ";";
|
||||
baseConnector.sqlQuery(sqlCommand, callbackFunction);
|
||||
};
|
||||
exports.upsertInTable = function (tableId, toInsert, toUpdate, callbackFunction) {
|
||||
var table = getTableById(tableId);
|
||||
var sqlCommand = "INSERT INTO " + table + " VALUES (";
|
||||
for (var i = 0, l = toInsert.length; i < l; ++i) {
|
||||
sqlCommand += baseConnector.sqlEscape(toInsert[i]);
|
||||
if (i !== l - 1)
|
||||
sqlCommand += ",";
|
||||
}
|
||||
sqlCommand += ") ON DUPLICATE KEY UPDATE ";
|
||||
for (var i = 0, l = toUpdate.length; i + 1 < l; i += 2) {
|
||||
sqlCommand += toUpdate[i] + "=" + baseConnector.sqlEscape(toUpdate[i+1]);
|
||||
if (i + 1 !== l - 1)
|
||||
sqlCommand += ",";
|
||||
}
|
||||
sqlCommand += ";";
|
||||
baseConnector.sqlQuery(sqlCommand, callbackFunction);
|
||||
};
|
||||
exports.upsertInTablePromise = function (tableId, toInsert, toUpdate) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
exports.upsertInTable(tableId, toInsert, toUpdate, function(error, result) {
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
resolve(result);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
exports.insertCallback = function(id, href, baseUrl, callbackFunction) {
|
||||
var sqlCommand = "INSERT IGNORE INTO " + tableCallbacks + " VALUES (" + baseConnector.sqlEscape(id) + "," +
|
||||
baseConnector.sqlEscape(href) + "," + baseConnector.sqlEscape(baseUrl) + ");";
|
||||
|
||||
baseConnector.sqlQuery(sqlCommand, callbackFunction);
|
||||
baseConnector.insertCallback(id, href, baseUrl, callbackFunction);
|
||||
};
|
||||
exports.insertCallbackPromise = function(id, href, baseUrl) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
@ -266,6 +235,7 @@ exports.getChangesPromise = function (docId, optStartIndex, optEndIndex) {
|
||||
if (null != optStartIndex && null != optEndIndex) {
|
||||
getCondition += ' AND change_id>=' + optStartIndex + ' AND change_id<' + optEndIndex;
|
||||
}
|
||||
getCondition += ' ORDER BY change_id ASC';
|
||||
getDataFromTable(c_oTableId.changes, "*", getCondition, function(error, result) {
|
||||
if (error) {
|
||||
reject(error);
|
||||
@ -279,7 +249,7 @@ exports.getChanges = function (docId, callback) {
|
||||
lockCriticalSection(docId, function () {_getChanges(docId, callback);});
|
||||
};
|
||||
function _getChanges (docId, callback) {
|
||||
getDataFromTable(c_oTableId.changes, "*", "id='" + docId + "'",
|
||||
getDataFromTable(c_oTableId.changes, "*", "id='" + docId + "' ORDER BY change_id ASC",
|
||||
function (error, result) {unLockCriticalSection(docId); if (callback) callback(error, result);});
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -32,6 +32,7 @@
|
||||
|
||||
var config = require('config');
|
||||
var co = require('co');
|
||||
const forwarded = require('forwarded');
|
||||
var taskResult = require('./taskresult');
|
||||
var logger = require('./../../Common/sources/logger');
|
||||
var utils = require('./../../Common/sources/utils');
|
||||
@ -74,7 +75,7 @@ function* getConvertStatus(cmd, selectRes, baseUrl) {
|
||||
status.err = constants.UNKNOWN;
|
||||
break;
|
||||
case taskResult.FileStatus.NeedPassword:
|
||||
status.err = constants.CONVERT_DRM;
|
||||
status.err = row.status_info;
|
||||
break;
|
||||
}
|
||||
var lastOpenDate = row.last_open_date;
|
||||
@ -150,12 +151,13 @@ function convertHealthCheck(req, res) {
|
||||
var docId = task.key;
|
||||
//put test file to storage
|
||||
var data = yield utils.readFile(cfgHealthCheckFilePath);
|
||||
yield storage.putObject(docId + '/origin', data, data.length);
|
||||
var format = 'docx';
|
||||
yield storage.putObject(docId + '/origin.' + format, data, data.length);
|
||||
//convert
|
||||
var cmd = new commonDefines.InputCommand();
|
||||
cmd.setCommand('conv');
|
||||
cmd.setSaveKey(docId);
|
||||
cmd.setFormat('docx');
|
||||
cmd.setFormat(format);
|
||||
cmd.setDocId(docId);
|
||||
cmd.setTitle('Editor.bin');
|
||||
cmd.setOutputFormat(constants.AVS_OFFICESTUDIO_FILE_CANVAS);
|
||||
@ -200,7 +202,7 @@ function convertRequest(req, res) {
|
||||
cmd.setUrl(req.query['url']);
|
||||
cmd.setEmbeddedFonts(false);//req.query['embeddedfonts'];
|
||||
cmd.setFormat(req.query['filetype']);
|
||||
var outputtype = req.query['outputtype'];
|
||||
var outputtype = req.query['outputtype'] || '';
|
||||
docId = 'conv_' + req.query['key'] + '_' + outputtype;
|
||||
cmd.setDocId(docId);
|
||||
cmd.setTitle(constants.OUTPUT_NAME + '.' + outputtype);
|
||||
@ -211,8 +213,14 @@ function convertRequest(req, res) {
|
||||
cmd.setPassword(req.query['password']);
|
||||
var async = 'true' == req.query['async'];
|
||||
|
||||
var status = yield* convertByCmd(cmd, async, utils.getBaseUrlByRequest(req));
|
||||
utils.fillXmlResponse(res, status.url, status.err);
|
||||
if (constants.AVS_OFFICESTUDIO_FILE_UNKNOWN !== cmd.getOutputFormat()) {
|
||||
var status = yield* convertByCmd(cmd, async, utils.getBaseUrlByRequest(req));
|
||||
utils.fillXmlResponse(res, status.url, status.err);
|
||||
} else {
|
||||
var addresses = forwarded(req);
|
||||
logger.error('Error convert unknown outputtype: query = %s from = %s docId = %s', JSON.stringify(req.query), addresses, docId);
|
||||
utils.fillXmlResponse(res, undefined, constants.UNKNOWN);
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
logger.error('Error convert: docId = %s\r\n%s', docId, e.stack);
|
||||
|
||||
@ -129,9 +129,18 @@ var checkDocumentExpire = function() {
|
||||
}
|
||||
});
|
||||
};
|
||||
var documentExpireJob = function(opt_isStart) {
|
||||
if (!opt_isStart) {
|
||||
logger.warn('checkDocumentExpire restart');
|
||||
}
|
||||
new cron.CronJob(cfgExpDocumentsCron, checkDocumentExpire, documentExpireJob, true);
|
||||
};
|
||||
documentExpireJob(true);
|
||||
|
||||
var documentExpireJob = new cron.CronJob(cfgExpDocumentsCron, checkDocumentExpire);
|
||||
documentExpireJob.start();
|
||||
|
||||
var fileExpireJob = new cron.CronJob(cfgExpFilesCron, checkFileExpire);
|
||||
fileExpireJob.start();
|
||||
var fileExpireJob = function(opt_isStart) {
|
||||
if (!opt_isStart) {
|
||||
logger.warn('checkFileExpire restart');
|
||||
}
|
||||
new cron.CronJob(cfgExpFilesCron, checkFileExpire, fileExpireJob, true);
|
||||
};
|
||||
fileExpireJob(true);
|
||||
|
||||
@ -1 +1 @@
|
||||
/*
|
||||
/*
|
||||
@ -31,29 +31,145 @@
|
||||
*/
|
||||
|
||||
var pg = require('pg');
|
||||
var co = require('co');
|
||||
var pgEscape = require('pg-escape');
|
||||
var types = require('pg').types;
|
||||
var sqlBase = require('./baseConnector');
|
||||
var configSql = require('config').get('services.CoAuthoring.sql');
|
||||
var connectionString = 'postgres://' + configSql.get('dbUser') + ':' + configSql.get('dbPass') + '@' + configSql.get('dbHost') +
|
||||
(configSql.get('dbPort') ? (':' + configSql.get('dbPort')) : '') + '/' + configSql.get('dbName');
|
||||
var pool = new pg.Pool({
|
||||
host: configSql.get('dbHost'),
|
||||
port: configSql.get('dbPort'),
|
||||
user: configSql.get('dbUser'),
|
||||
password: configSql.get('dbPass'),
|
||||
database: configSql.get('dbName'),
|
||||
max: configSql.get('connectionlimit'),
|
||||
min: 0,
|
||||
ssl: false,
|
||||
idleTimeoutMillis: 30000
|
||||
});
|
||||
var cfgTableCallbacks = configSql.get('tableCallbacks');
|
||||
//todo datetime timezone
|
||||
types.setTypeParser(1114, function(stringValue) {
|
||||
return new Date(stringValue + '+0000');
|
||||
});
|
||||
types.setTypeParser(1184, function(stringValue) {
|
||||
return new Date(stringValue + '+0000');
|
||||
});
|
||||
|
||||
var logger = require('./../../Common/sources/logger');
|
||||
|
||||
exports.sqlQuery = function (sqlCommand, callbackFunction) {
|
||||
pg.connect(connectionString, function (err, connection, done) {
|
||||
if(err) {
|
||||
logger.error('pool.getConnection error: %s', err);
|
||||
if (callbackFunction) callbackFunction(err, null);
|
||||
return;
|
||||
}
|
||||
|
||||
connection.query(sqlCommand, function (error, result) {
|
||||
//call `done()` to release the client back to the pool
|
||||
done();
|
||||
|
||||
if (error) logger.error('sqlQuery: %s sqlCommand: %s', error.message, sqlCommand.slice(0, 50));
|
||||
if (callbackFunction) callbackFunction(error, result ? result.rows : result);
|
||||
});
|
||||
});
|
||||
exports.sqlQuery = function(sqlCommand, callbackFunction, opt_noModifyRes, opt_noLog) {
|
||||
co(function *() {
|
||||
var client = null;
|
||||
var result = null;
|
||||
var error = null;
|
||||
try {
|
||||
client = yield pool.connect();
|
||||
result = yield client.query(sqlCommand);
|
||||
} catch (err) {
|
||||
error = err;
|
||||
if (!opt_noLog) {
|
||||
if (client) {
|
||||
logger.error('sqlQuery error sqlCommand: %s:\r\n%s', sqlCommand.slice(0, 50), err.stack);
|
||||
} else {
|
||||
logger.error('pool.getConnection error: %s', err);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (client) {
|
||||
client.release();
|
||||
}
|
||||
if (callbackFunction) {
|
||||
var output = result;
|
||||
if (result && !opt_noModifyRes) {
|
||||
if ('SELECT' === result.command) {
|
||||
output = result.rows;
|
||||
} else {
|
||||
output = {affectedRows: result.rowCount};
|
||||
}
|
||||
}
|
||||
callbackFunction(error, output);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
exports.sqlEscape = function(value) {
|
||||
//todo parameterized queries
|
||||
return undefined !== value ? pgEscape.literal(value.toString()) : 'NULL';
|
||||
};
|
||||
var isSupportOnConflict = false;
|
||||
(function checkIsSupportOnConflict() {
|
||||
var sqlCommand = 'INSERT INTO checkIsSupportOnConflict (id) VALUES(1) ON CONFLICT DO NOTHING;';
|
||||
exports.sqlQuery(sqlCommand, function(error, result) {
|
||||
if (error) {
|
||||
if ('42601' == error.code) {
|
||||
//SYNTAX ERROR
|
||||
isSupportOnConflict = false;
|
||||
logger.debug('checkIsSupportOnConflict false');
|
||||
} else if ('42P01' == error.code) {
|
||||
// UNDEFINED TABLE
|
||||
isSupportOnConflict = true;
|
||||
logger.debug('checkIsSupportOnConflict true');
|
||||
} else {
|
||||
logger.error('checkIsSupportOnConflict unexpected error code:\r\n%s', error.stack);
|
||||
}
|
||||
}
|
||||
}, true, true);
|
||||
})();
|
||||
|
||||
exports.insertCallback = function(id, href, baseUrl, callbackFunction) {
|
||||
var sqlCommand = "INSERT INTO " + cfgTableCallbacks + " VALUES (" + exports.sqlEscape(id) + "," +
|
||||
exports.sqlEscape(href) + "," + exports.sqlEscape(baseUrl) + ")";
|
||||
if (isSupportOnConflict) {
|
||||
sqlCommand += ' ON CONFLICT DO NOTHING;';
|
||||
exports.sqlQuery(sqlCommand, callbackFunction);
|
||||
} else {
|
||||
sqlCommand += ';';
|
||||
exports.sqlQuery(sqlCommand, function(error, result) {
|
||||
if (error && error.code == '23505') {
|
||||
//UNIQUE VIOLATION
|
||||
callbackFunction(null, result);
|
||||
} else {
|
||||
callbackFunction(error, result);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
function getUpsertString(task) {
|
||||
task.completeDefaults();
|
||||
var dateNow = sqlBase.getDateTime(new Date());
|
||||
var commandArg = [task.key, task.status, task.statusInfo, dateNow, task.title, task.userIndex, task.changeId];
|
||||
var commandArgEsc = commandArg.map(function(curVal) {
|
||||
return exports.sqlEscape(curVal)
|
||||
});
|
||||
if (isSupportOnConflict) {
|
||||
//http://stackoverflow.com/questions/34762732/how-to-find-out-if-an-upsert-was-an-update-with-postgresql-9-5-upsert
|
||||
return "INSERT INTO task_result (id, status, status_info, last_open_date, title, user_index, change_id) SELECT " +
|
||||
commandArgEsc.join(', ') +
|
||||
" WHERE 'false' = set_config('myapp.isupdate', 'false', true) ON CONFLICT (id) DO UPDATE SET last_open_date = " +
|
||||
sqlBase.baseConnector.sqlEscape(dateNow) +
|
||||
", user_index = task_result.user_index + 1 WHERE 'true' = set_config('myapp.isupdate', 'true', true) RETURNING" +
|
||||
" current_setting('myapp.isupdate') as isupdate, user_index as userindex;";
|
||||
} else {
|
||||
return "SELECT * FROM merge_db(" + commandArgEsc.join(', ') + ");";
|
||||
}
|
||||
}
|
||||
exports.upsert = function(task) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
var sqlCommand = getUpsertString(task);
|
||||
exports.sqlQuery(sqlCommand, function(error, result) {
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
if (result && result.rows.length > 0) {
|
||||
var first = result.rows[0];
|
||||
result = {affectedRows: 0, insertId: 0};
|
||||
result.affectedRows = 'true' == first.isupdate ? 2 : 1;
|
||||
result.insertId = first.userindex;
|
||||
}
|
||||
resolve(result);
|
||||
}
|
||||
}, true);
|
||||
});
|
||||
};
|
||||
exports.sqlEscape = function (value) {
|
||||
return value.replace( /(\')/g, "\\'" );
|
||||
};
|
||||
@ -54,14 +54,16 @@ function init(pubsub, callback) {
|
||||
pubsub.exchangePublish = yield rabbitMQCore.assertExchangePromise(pubsub.channelPublish, cfgRabbitExchangePubSub,
|
||||
'fanout', {durable: true});
|
||||
|
||||
var channelReceive = yield rabbitMQCore.createChannelPromise(conn);
|
||||
var queue = yield rabbitMQCore.assertQueuePromise(channelReceive, '', {autoDelete: true, exclusive: true});
|
||||
channelReceive.bindQueue(queue, cfgRabbitExchangePubSub, '');
|
||||
yield rabbitMQCore.consumePromise(channelReceive, queue, function (message) {
|
||||
if (message) {
|
||||
pubsub.emit('message', message.content.toString());
|
||||
pubsub.channelReceive = yield rabbitMQCore.createChannelPromise(conn);
|
||||
var queue = yield rabbitMQCore.assertQueuePromise(pubsub.channelReceive, '', {autoDelete: true, exclusive: true});
|
||||
pubsub.channelReceive.bindQueue(queue, cfgRabbitExchangePubSub, '');
|
||||
yield rabbitMQCore.consumePromise(pubsub.channelReceive, queue, function (message) {
|
||||
if(null != pubsub.channelReceive){
|
||||
if (message) {
|
||||
pubsub.emit('message', message.content.toString());
|
||||
}
|
||||
pubsub.channelReceive.ack(message);
|
||||
}
|
||||
channelReceive.ack(message);
|
||||
}, {noAck: false});
|
||||
//process messages received while reconnection time
|
||||
repeat(pubsub);
|
||||
@ -76,6 +78,7 @@ function init(pubsub, callback) {
|
||||
function clear(pubsub) {
|
||||
pubsub.channelPublish = null;
|
||||
pubsub.exchangePublish = null;
|
||||
pubsub.channelReceive = null;
|
||||
}
|
||||
function repeat(pubsub) {
|
||||
for (var i = 0; i < pubsub.publishStore.length; ++i) {
|
||||
@ -92,6 +95,7 @@ function PubsubRabbitMQ() {
|
||||
this.connection = null;
|
||||
this.channelPublish = null;
|
||||
this.exchangePublish = null;
|
||||
this.channelReceive = null;
|
||||
this.publishStore = [];
|
||||
}
|
||||
util.inherits(PubsubRabbitMQ, events.EventEmitter);
|
||||
|
||||
@ -104,6 +104,8 @@ if (cluster.isMaster) {
|
||||
const path = require('path');
|
||||
const bodyParser = require("body-parser");
|
||||
const mime = require('mime');
|
||||
const forwarded = require('forwarded');
|
||||
const ipaddr = require('ipaddr.js');
|
||||
const docsCoServer = require('./DocsCoServer');
|
||||
const canvasService = require('./canvasservice');
|
||||
const converterService = require('./converterservice');
|
||||
@ -112,6 +114,8 @@ if (cluster.isMaster) {
|
||||
const constants = require('./../../Common/sources/constants');
|
||||
const utils = require('./../../Common/sources/utils');
|
||||
const configStorage = configCommon.get('storage');
|
||||
var configIpFilter = configCommon.get('services.CoAuthoring.ipfilter');
|
||||
var cfgIpFilterEseForRequest = configIpFilter.get('useforrequest');
|
||||
const app = express();
|
||||
var server = null;
|
||||
|
||||
@ -132,7 +136,7 @@ if (cluster.isMaster) {
|
||||
var cfgStorageFolderName = configStorage.get('storageFolderName');
|
||||
app.use('/' + cfgBucketName + '/' + cfgStorageFolderName, (req, res, next) => {
|
||||
var index = req.url.lastIndexOf('/');
|
||||
if (-1 != index) {
|
||||
if ('GET' === req.method && -1 != index) {
|
||||
var contentDisposition = req.query['disposition'] || 'attachment';
|
||||
var sendFileOptions = {
|
||||
root: configStorage.get('fs.folderPath'),
|
||||
@ -154,11 +158,30 @@ if (cluster.isMaster) {
|
||||
}
|
||||
});
|
||||
} else {
|
||||
req.sendStatus(404)
|
||||
res.sendStatus(404)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function checkClientIp(req, res, next) {
|
||||
var status = 0;
|
||||
if (cfgIpFilterEseForRequest) {
|
||||
var addresses = forwarded(req);
|
||||
var ipString = addresses[addresses.length - 1];
|
||||
//IPv6 -> IPv4
|
||||
if (ipaddr.IPv6.isValid(ipString)) {
|
||||
var ip = ipaddr.IPv6.parse(ipString);
|
||||
if (ip.isIPv4MappedAddress()) {
|
||||
ipString = ip.toIPv4Address().toString();
|
||||
}
|
||||
}
|
||||
status = utils.checkIpFilter(ipString);
|
||||
}
|
||||
if (status > 0) {
|
||||
res.sendStatus(status);
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
}
|
||||
// Если захочется использовать 'development' и 'production',
|
||||
// то с помощью app.settings.env (https://github.com/strongloop/express/issues/936)
|
||||
// Если нужна обработка ошибок, то теперь она такая https://github.com/expressjs/errorhandler
|
||||
@ -171,8 +194,8 @@ if (cluster.isMaster) {
|
||||
res.send('Server is functioning normally. Version: ' + docsCoServer.version);
|
||||
});
|
||||
|
||||
app.get('/coauthoring/CommandService.ashx', docsCoServer.commandFromServer);
|
||||
app.post('/coauthoring/CommandService.ashx', docsCoServer.commandFromServer);
|
||||
app.get('/coauthoring/CommandService.ashx', checkClientIp, docsCoServer.commandFromServer);
|
||||
app.post('/coauthoring/CommandService.ashx', checkClientIp, docsCoServer.commandFromServer);
|
||||
|
||||
if (config.has('server.fonts_route')) {
|
||||
var fontsRoute = config.get('server.fonts_route');
|
||||
@ -181,12 +204,12 @@ if (cluster.isMaster) {
|
||||
app.get('/' + fontsRoute + 'odttf/:fontname', fontService.getFont);
|
||||
}
|
||||
|
||||
app.get('/ConvertService.ashx', converterService.convert);
|
||||
app.post('/ConvertService.ashx', converterService.convert);
|
||||
app.get('/ConvertService.ashx', checkClientIp, converterService.convert);
|
||||
app.post('/ConvertService.ashx', checkClientIp, converterService.convert);
|
||||
|
||||
var rawFileParser = bodyParser.raw({ inflate: true, limit: config.get('server.limits_tempfile_upload'), type: '*/*' });
|
||||
app.get('/FileUploader.ashx', rawFileParser, fileUploaderService.uploadTempFile);
|
||||
app.post('/FileUploader.ashx', rawFileParser, fileUploaderService.uploadTempFile);
|
||||
app.get('/FileUploader.ashx', checkClientIp, rawFileParser, fileUploaderService.uploadTempFile);
|
||||
app.post('/FileUploader.ashx', checkClientIp, rawFileParser, fileUploaderService.uploadTempFile);
|
||||
|
||||
var docIdRegExp = new RegExp("^[" + constants.DOC_ID_PATTERN + "]*$", 'i');
|
||||
app.param('docid', (req, res, next, val) => {
|
||||
@ -207,7 +230,7 @@ if (cluster.isMaster) {
|
||||
app.post('/upload/:docid/:userid/:index/:vkey?', rawFileParser, fileUploaderService.uploadImageFile);
|
||||
|
||||
app.post('/downloadas/:docid', rawFileParser, canvasService.downloadAs);
|
||||
app.get('/healthcheck', converterService.convertHealthCheck);
|
||||
app.get('/healthcheck', checkClientIp, converterService.convertHealthCheck);
|
||||
});
|
||||
|
||||
process.on('message', (msg) => {
|
||||
|
||||
@ -83,36 +83,8 @@ TaskResultData.prototype.completeDefaults = function() {
|
||||
}
|
||||
};
|
||||
|
||||
function getUpsertString(task, opt_updateUserIndex) {
|
||||
task.completeDefaults();
|
||||
var dateNow = sqlBase.getDateTime(new Date());
|
||||
var commandArg = [task.key, task.status, task.statusInfo, dateNow, task.title, task.userIndex, task.changeId];
|
||||
var commandArgEsc = commandArg.map(function(curVal) {
|
||||
return sqlBase.baseConnector.sqlEscape(curVal)
|
||||
});
|
||||
var sql = 'INSERT INTO task_result ( id, status, status_info, last_open_date, title,' +
|
||||
' user_index, change_id ) VALUES (' + commandArgEsc.join(', ') + ') ON DUPLICATE KEY UPDATE' +
|
||||
' last_open_date = ' + sqlBase.baseConnector.sqlEscape(dateNow);
|
||||
if (opt_updateUserIndex) {
|
||||
//todo LAST_INSERT_ID in posgresql - RETURNING
|
||||
sql += ', user_index = LAST_INSERT_ID(user_index + 1);';
|
||||
} else {
|
||||
sql += ';';
|
||||
}
|
||||
return sql;
|
||||
}
|
||||
|
||||
function upsert(task, opt_updateUserIndex) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
var sqlCommand = getUpsertString(task, opt_updateUserIndex);
|
||||
sqlBase.baseConnector.sqlQuery(sqlCommand, function(error, result) {
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
resolve(result);
|
||||
}
|
||||
});
|
||||
});
|
||||
return sqlBase.baseConnector.upsert(task, opt_updateUserIndex);
|
||||
}
|
||||
|
||||
function getSelectString(docId) {
|
||||
@ -281,7 +253,6 @@ exports.upsert = upsert;
|
||||
exports.select = select;
|
||||
exports.update = update;
|
||||
exports.updateIf = updateIf;
|
||||
exports.addRandomKey = addRandomKey;
|
||||
exports.addRandomKeyTask = addRandomKeyTask;
|
||||
exports.remove = remove;
|
||||
exports.getExpired = getExpired;
|
||||
|
||||
@ -5,6 +5,6 @@
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"co": "^4.6.0",
|
||||
"config": "^1.19.0"
|
||||
"config": "^1.21.0"
|
||||
}
|
||||
}
|
||||
|
||||
@ -66,10 +66,9 @@ var MAX_OPEN_FILES = 200;
|
||||
var TEMP_PREFIX = 'ASC_CONVERT';
|
||||
var queue = null;
|
||||
var clientStatsD = statsDClient.getClient();
|
||||
var exitCodesReturn = [constants.CONVERT_MS_OFFCRYPTO, constants.CONVERT_NEED_PARAMS, constants.CONVERT_CORRUPTED,
|
||||
constants.CONVERT_DRM, constants.CONVERT_PASSWORD];
|
||||
var exitCodesMinorError = [constants.CONVERT_MS_OFFCRYPTO, constants.CONVERT_NEED_PARAMS, constants.CONVERT_DRM,
|
||||
var exitCodesReturn = [constants.CONVERT_NEED_PARAMS, constants.CONVERT_CORRUPTED, constants.CONVERT_DRM,
|
||||
constants.CONVERT_PASSWORD];
|
||||
var exitCodesMinorError = [constants.CONVERT_NEED_PARAMS, constants.CONVERT_DRM, constants.CONVERT_PASSWORD];
|
||||
var exitCodesUpload = [constants.NO_ERROR, constants.CONVERT_CORRUPTED, constants.CONVERT_NEED_PARAMS,
|
||||
constants.CONVERT_DRM];
|
||||
|
||||
@ -167,24 +166,31 @@ function* downloadFile(docId, uri, fileFrom) {
|
||||
var res = false;
|
||||
var data = null;
|
||||
var downloadAttemptCount = 0;
|
||||
while (!res && downloadAttemptCount++ < cfgDownloadAttemptMaxCount) {
|
||||
try {
|
||||
data = yield utils.downloadUrlPromise(uri, cfgDownloadTimeout * 1000, cfgDownloadMaxBytes);
|
||||
res = true;
|
||||
} catch (err) {
|
||||
res = false;
|
||||
logger.error('error downloadFile:url=%s;attempt=%d;(id=%s)\r\n%s', uri, downloadAttemptCount, docId, err.stack);
|
||||
//not continue attempts if timeout
|
||||
if (err.code === 'ETIMEDOUT' || err.code === 'EMSGSIZE') {
|
||||
break;
|
||||
} else {
|
||||
yield utils.sleep(cfgDownloadAttemptDelay);
|
||||
var urlParsed = url.parse(uri);
|
||||
var filterStatus = utils.checkIpFilter(urlParsed.hostname);
|
||||
if (0 == filterStatus) {
|
||||
while (!res && downloadAttemptCount++ < cfgDownloadAttemptMaxCount) {
|
||||
try {
|
||||
data = yield utils.downloadUrlPromise(uri, cfgDownloadTimeout * 1000, cfgDownloadMaxBytes);
|
||||
res = true;
|
||||
} catch (err) {
|
||||
res = false;
|
||||
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);
|
||||
//not continue attempts if timeout
|
||||
if (err.code === 'ETIMEDOUT' || err.code === 'EMSGSIZE') {
|
||||
break;
|
||||
} else {
|
||||
yield utils.sleep(cfgDownloadAttemptDelay);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (res) {
|
||||
logger.debug('downloadFile complete(id=%s)', docId);
|
||||
fs.writeFileSync(fileFrom, data);
|
||||
if (res) {
|
||||
logger.debug('downloadFile complete(id=%s)', docId);
|
||||
fs.writeFileSync(fileFrom, data);
|
||||
}
|
||||
} else {
|
||||
logger.error('checkIpFilter error:url=%s;code:%s;(id=%s)', uri, filterStatus, docId);
|
||||
res = false;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
@ -231,17 +237,6 @@ function* downloadFileFromStorage(id, strPath, dir) {
|
||||
fs.writeFileSync(path.join(dir, fileRel), data);
|
||||
}
|
||||
}
|
||||
function pipeFile(fsFrom, fsTo) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
fsFrom.pipe(fsTo, {end: false});
|
||||
fsFrom.on('end', function() {
|
||||
resolve();
|
||||
});
|
||||
fsFrom.on('error', function(e) {
|
||||
reject(e);
|
||||
});
|
||||
});
|
||||
}
|
||||
function* processDownloadFromStorage(dataConvert, cmd, task, tempDirs) {
|
||||
if (task.getFromOrigin() || task.getFromSettings()) {
|
||||
dataConvert.fileFrom = path.join(tempDirs.source, 'origin.' + cmd.getFormat());
|
||||
@ -262,7 +257,7 @@ function* processDownloadFromStorage(dataConvert, cmd, task, tempDirs) {
|
||||
fsFullFile = yield utils.promiseCreateWriteStream(dataConvert.fileFrom);
|
||||
}
|
||||
var fsCurFile = yield utils.promiseCreateReadStream(file);
|
||||
yield pipeFile(fsCurFile, fsFullFile);
|
||||
yield utils.pipeStreams(fsCurFile, fsFullFile, false);
|
||||
}
|
||||
}
|
||||
if (fsFullFile) {
|
||||
@ -473,7 +468,7 @@ function* ExecuteTask(task) {
|
||||
if (constants.NO_ERROR === error) {
|
||||
if(constants.AVS_OFFICESTUDIO_FILE_OTHER_HTMLZIP === dataConvert.formatTo && cmd.getSaveKey() && !dataConvert.mailMergeSend) {
|
||||
//todo заглушка.вся конвертация на клиенте, но нет простого механизма сохранения на клиенте
|
||||
fs.writeFileSync(dataConvert.fileTo, fs.readFileSync(dataConvert.fileFrom));
|
||||
yield utils.pipeFiles(dataConvert.fileFrom, dataConvert.fileTo);
|
||||
} else {
|
||||
var paramsFile = path.join(tempDirs.temp, 'params.xml');
|
||||
dataConvert.serialize(paramsFile);
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
|
||||
[](http://www.gnu.org/licenses/agpl-3.0.ru.html) 
|
||||
[](http://www.gnu.org/licenses/agpl-3.0.ru.html) 
|
||||
|
||||
|
||||
## Server
|
||||
The backend server software layer which is the part of [ONLYOFFICE Document Server][2] and is the base for all other components.
|
||||
@ -74,10 +75,11 @@ In case it is necessary to temporarily edit the config files, create the local.j
|
||||
|
||||
## User Feedback and Support
|
||||
|
||||
If you have any problems with or questions about [ONLYOFFICE Document Server][2], please visit our official forum to find answers to your questions: [dev.onlyoffice.org][1].
|
||||
If you have any problems with or questions about [ONLYOFFICE Document Server][2], please visit our official forum to find answers to your questions: [dev.onlyoffice.org][1] or you can ask and answer ONLYOFFICE development questions on [Stack Overflow][3].
|
||||
|
||||
[1]: http://dev.onlyoffice.org
|
||||
[2]: https://github.com/ONLYOFFICE/DocumentServer
|
||||
[3]: http://stackoverflow.com/questions/tagged/onlyoffice
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@ -4,9 +4,9 @@
|
||||
"homepage": "http://www.onlyoffice.com",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"express" : "^4.13.4",
|
||||
"sockjs" : "^0.3.15",
|
||||
"nodehun" : "^2.0.8",
|
||||
"config": "^1.19.0"
|
||||
"express" : "^4.14.0",
|
||||
"sockjs" : "^0.3.17",
|
||||
"nodehun" : "^2.0.10",
|
||||
"config" : "^1.21.0"
|
||||
}
|
||||
}
|
||||
81
schema/postgresql/createdb.sql
Normal file
81
schema/postgresql/createdb.sql
Normal file
@ -0,0 +1,81 @@
|
||||
--
|
||||
-- Create schema onlyoffice
|
||||
--
|
||||
|
||||
-- CREATE DATABASE onlyoffice ENCODING = 'UTF8' CONNECTION LIMIT = -1;
|
||||
|
||||
--
|
||||
-- Drop tables
|
||||
--
|
||||
DROP TABLE IF EXISTS "public"."doc_callbacks";
|
||||
DROP TABLE IF EXISTS "public"."doc_changes";
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for doc_callbacks
|
||||
-- ----------------------------
|
||||
CREATE TABLE IF NOT EXISTS "public"."doc_callbacks" (
|
||||
"id" varchar(255) COLLATE "default" NOT NULL,
|
||||
"callback" text COLLATE "default" NOT NULL,
|
||||
"baseurl" text COLLATE "default" NOT NULL,
|
||||
PRIMARY KEY ("id")
|
||||
)
|
||||
WITH (OIDS=FALSE);
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for doc_changes
|
||||
-- ----------------------------
|
||||
CREATE TABLE IF NOT EXISTS "public"."doc_changes" (
|
||||
"id" varchar(255) COLLATE "default" NOT NULL,
|
||||
"change_id" int8 NOT NULL,
|
||||
"user_id" varchar(255) COLLATE "default" NOT NULL,
|
||||
"user_id_original" varchar(255) COLLATE "default" NOT NULL,
|
||||
"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")
|
||||
)
|
||||
WITH (OIDS=FALSE);
|
||||
|
||||
-- ----------------------------
|
||||
-- Table structure for task_result
|
||||
-- ----------------------------
|
||||
CREATE TABLE IF NOT EXISTS "public"."task_result" (
|
||||
"id" varchar(255) COLLATE "default" NOT NULL,
|
||||
"status" int2 NOT NULL,
|
||||
"status_info" int8 NOT NULL,
|
||||
"last_open_date" timestamp without time zone NOT NULL,
|
||||
"title" varchar(255) COLLATE "default" NOT NULL,
|
||||
"user_index" int8 NOT NULL DEFAULT 1,
|
||||
"change_id" int8 NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY ("id")
|
||||
)
|
||||
WITH (OIDS=FALSE);
|
||||
|
||||
--https://www.postgresql.org/docs/current/static/plpgsql-control-structures.html#PLPGSQL-UPSERT-EXAMPLE
|
||||
CREATE OR REPLACE FUNCTION merge_db(_id varchar(255), _status int2, _status_info int8, _last_open_date timestamp without time zone, _title varchar(255), _user_index int8, _change_id int8, OUT isupdate char(5), OUT userindex int8) AS
|
||||
$$
|
||||
DECLARE
|
||||
t_var "public"."task_result"."user_index"%TYPE;
|
||||
BEGIN
|
||||
LOOP
|
||||
-- first try to update the key
|
||||
-- note that "a" must be unique
|
||||
UPDATE "public"."task_result" SET last_open_date=_last_open_date, user_index=user_index+1 WHERE id = _id RETURNING user_index into userindex;
|
||||
IF found THEN
|
||||
isupdate := 'true';
|
||||
RETURN;
|
||||
END IF;
|
||||
-- not there, so try to insert the key
|
||||
-- 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, title, user_index, change_id) VALUES(_id, _status, _status_info, _last_open_date, _title, _user_index, _change_id) RETURNING user_index into userindex;
|
||||
isupdate := 'false';
|
||||
RETURN;
|
||||
EXCEPTION WHEN unique_violation THEN
|
||||
-- do nothing, and loop to try the UPDATE again
|
||||
END;
|
||||
END LOOP;
|
||||
END;
|
||||
$$
|
||||
LANGUAGE plpgsql;
|
||||
1
schema/postgresql/removedb.sql
Normal file
1
schema/postgresql/removedb.sql
Normal file
@ -0,0 +1 @@
|
||||
DROP DATABASE IF EXISTS onlyoffice;
|
||||
Reference in New Issue
Block a user