From 8bee95d13d2278282a99531b69ace14c0dbeb584 Mon Sep 17 00:00:00 2001 From: ansuz Date: Sun, 26 Apr 2020 13:19:14 -0400 Subject: [PATCH 01/11] improve error reporting in GET_HISTORY --- lib/hk-util.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/hk-util.js b/lib/hk-util.js index 3b8c07e09..562114ec7 100644 --- a/lib/hk-util.js +++ b/lib/hk-util.js @@ -622,7 +622,8 @@ const handleGetHistory = function (Env, Server, seq, userId, parsed) { }, (err) => { if (err && err.code !== 'ENOENT') { if (err.message !== 'EINVAL') { Log.error("HK_GET_HISTORY", { - err: err && err.message, + channel: channelName, + err: err && err.message || err, stack: err && err.stack, }); } const parsedMsg = {error:err.message, channel: channelName, txid: txid}; From 8a5d012edf5aa4e70568773305d2b878098e87cf Mon Sep 17 00:00:00 2001 From: ansuz Date: Tue, 28 Apr 2020 16:57:52 -0400 Subject: [PATCH 02/11] add special error handling for a few special serverside errors --- lib/api.js | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/lib/api.js b/lib/api.js index 4f0f08602..f615360c5 100644 --- a/lib/api.js +++ b/lib/api.js @@ -9,6 +9,16 @@ module.exports.create = function (config) { var log = config.log; + var noop = function () {}; + + var special_errors = {}; + ['EPIPE', 'ECONNRESET'].forEach(function (k) { special_errors[k] = noop; }); + special_errors.NF_ENOENT = function (error, label, info) { + log.error(label, { + info: info, + }); + }; + // spawn ws server and attach netflux event handlers NetfluxSrv.create(new WebSocketServer({ server: config.httpServer})) .on('channelClose', historyKeeper.channelClose) @@ -17,11 +27,17 @@ module.exports.create = function (config) { .on('sessionClose', historyKeeper.sessionClose) .on('error', function (error, label, info) { if (!error) { return; } - if (['EPIPE', 'ECONNRESET'].indexOf(error && error.code) !== -1) { return; } + if (error && error.code) { + /* EPIPE,ECONNERESET, NF_ENOENT */ + if (typeof(special_errors[error.code]) === 'function') { + return void special_errors[error.code](error, label, info); + } + } + /* labels: SEND_MESSAGE_FAIL, SEND_MESSAGE_FAIL_2, FAIL_TO_DISCONNECT, FAIL_TO_TERMINATE, HANDLE_CHANNEL_LEAVE, NETFLUX_BAD_MESSAGE, - NETFLUX_WEBSOCKET_ERROR, NF_ENOENT + NETFLUX_WEBSOCKET_ERROR */ log.error(label, { code: error.code, From 37d3995ac15c32d49061705180eabf83f5ade150 Mon Sep 17 00:00:00 2001 From: ansuz Date: Tue, 28 Apr 2020 17:05:15 -0400 Subject: [PATCH 03/11] unify format of console output with stored logs --- lib/log.js | 32 ++++++++------------------------ 1 file changed, 8 insertions(+), 24 deletions(-) diff --git a/lib/log.js b/lib/log.js index 35dfad7a3..386ee6b41 100644 --- a/lib/log.js +++ b/lib/log.js @@ -23,29 +23,13 @@ var write = function (ctx, content) { // various degrees of logging const logLevels = Logger.levels = ['silly', 'verbose', 'debug', 'feedback', 'info', 'warn', 'error']; -var handlers = { - silly: function (ctx, time, tag, info) { - console.log('[SILLY]', time, tag, info); - }, - debug: function (ctx, time, tag, info) { - console.log('[DEBUG]', time, tag, info); - }, - verbose: function (ctx, time, tag, info) { - console.log('[VERBOSE]', time, tag, info); - }, - feedback: function (ctx, time, tag, info) { - console.log('[FEEDBACK]', time, tag, info); - }, - info: function (ctx, time, tag, info) { - console.info('[INFO]', time, tag, info); - }, - warn: function (ctx, time, tag, info) { - console.warn('[WARN]', time, tag, info); - }, - error: function (ctx, time, tag, info) { - console.error('[ERROR]', time, tag, info); - } -}; +var handlers = {}; +['silly', 'debug', 'verbose', 'feedback', 'info'].forEach(function (level) { + handlers[level] = function (ctx, content) { console.log(content); }; +}); +['warn', 'error'].forEach(function (level) { + handlers[level] = function (ctx, content) { console.error(content); } +}); var noop = function () {}; @@ -65,7 +49,7 @@ var createLogType = function (ctx, type) { return; } if (ctx.logToStdout && typeof(handlers[type]) === 'function') { - handlers[type](ctx, time, tag, info); + handlers[type](ctx, content); } write(ctx, content); }; From d35b015c3fa9f5002b66fb8df35806123e1dae04 Mon Sep 17 00:00:00 2001 From: ansuz Date: Tue, 28 Apr 2020 17:12:03 -0400 Subject: [PATCH 04/11] log some info on successful file uploads --- lib/commands/upload.js | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/lib/commands/upload.js b/lib/commands/upload.js index c64368949..7286caa93 100644 --- a/lib/commands/upload.js +++ b/lib/commands/upload.js @@ -75,8 +75,21 @@ Upload.upload = function (Env, safeKey, chunk, cb) { Env.blobStore.upload(safeKey, chunk, cb); }; +var reportStatus = function (Env, label, safeKey, err, id) { + var data = { + safeKey: safeKey, + err: err && err.message || err, + id: id, + }; + var method = err? 'error': 'info'; + Env.Log[method](label, data); +}; + Upload.complete = function (Env, safeKey, arg, cb) { - Env.blobStore.complete(safeKey, arg, cb); + Env.blobStore.complete(safeKey, arg, function (err, id) { + reportStatus(Env, 'UPLOAD_COMPLETE', safeKey, err, id); + cb(err, id); + }); }; Upload.cancel = function (Env, safeKey, arg, cb) { @@ -84,6 +97,9 @@ Upload.cancel = function (Env, safeKey, arg, cb) { }; Upload.complete_owned = function (Env, safeKey, arg, cb) { - Env.blobStore.completeOwned(safeKey, arg, cb); + Env.blobStore.completeOwned(safeKey, arg, function (err, id) { + reportStatus(Env, 'UPLOAD_COMPLETE_OWNED', safeKey, err, id); + cb(err, id); + }); }; From 9e947e4fd614cc08fe7df3c88917f11e0edbeb66 Mon Sep 17 00:00:00 2001 From: yflory Date: Wed, 29 Apr 2020 18:18:59 +0200 Subject: [PATCH 05/11] Fix modal errors --- www/common/sframe-common-outer.js | 333 +++++++++++++++--------------- 1 file changed, 167 insertions(+), 166 deletions(-) diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js index 8c30f2886..0a301fc15 100644 --- a/www/common/sframe-common-outer.js +++ b/www/common/sframe-common-outer.js @@ -395,7 +395,7 @@ define([ var forceCreationScreen = cfg.useCreationScreen && sessionStorage[Utils.Constants.displayPadCreationScreen]; delete sessionStorage[Utils.Constants.displayPadCreationScreen]; - var isSafe = ['debug', 'profile', 'drive'].indexOf(currentPad.app) !== -1; + var isSafe = ['debug', 'profile', 'drive', 'teams'].indexOf(currentPad.app) !== -1; var updateMeta = function () { //console.log('EV_METADATA_UPDATE'); var metaObj; @@ -620,6 +620,172 @@ define([ }, href); }); + // Add or remove our mailbox from the list if we're an owner + sframeChan.on('Q_UPDATE_MAILBOX', function (data, cb) { + var metadata = data.metadata; + var add = data.add; + var _secret = secret; + if (metadata && (metadata.href || metadata.roHref)) { + var _parsed = Utils.Hash.parsePadUrl(metadata.href || metadata.roHref); + _secret = Utils.Hash.getSecrets(_parsed.type, _parsed.hash, metadata.password); + } + if (_secret.channel.length !== 32) { + return void cb({error: 'EINVAL'}); + } + var crypto = Crypto.createEncryptor(_secret.keys); + nThen(function (waitFor) { + // If we already have metadata, use it, otherwise, try to get it + if (metadata) { return; } + + Cryptpad.getPadMetadata({ + channel: secret.channel + }, waitFor(function (obj) { + obj = obj || {}; + if (obj.error) { + waitFor.abort(); + return void cb(obj); + } + metadata = obj; + })); + }).nThen(function () { + // Get and maybe migrate the existing mailbox object + var owners = metadata.owners; + if (!Array.isArray(owners) || owners.indexOf(edPublic) === -1) { + return void cb({ error: 'INSUFFICIENT_PERMISSIONS' }); + } + + // Remove a mailbox + if (!add) { + // Old format: this is the mailbox of the first owner + if (typeof (metadata.mailbox) === "string" && metadata.mailbox) { + // Not our mailbox? abort + if (owners[0] !== edPublic) { + return void cb({ error: 'INSUFFICIENT_PERMISSIONS' }); + } + // Remove it + return void Cryptpad.setPadMetadata({ + channel: _secret.channel, + command: 'RM_MAILBOX', + value: [] + }, cb); + } else if (metadata.mailbox) { // New format + return void Cryptpad.setPadMetadata({ + channel: _secret.channel, + command: 'RM_MAILBOX', + value: [edPublic] + }, cb); + } + return void cb({ + error: 'NO_MAILBOX' + }); + } + // Add a mailbox + var toAdd = {}; + toAdd[edPublic] = crypto.encrypt(JSON.stringify({ + notifications: notifications, + curvePublic: curvePublic + })); + Cryptpad.setPadMetadata({ + channel: _secret.channel, + command: 'ADD_MAILBOX', + value: toAdd + }, cb); + }); + }); + + // REQUEST_ACCESS is used both to check IF we can contact an owner (send === false) + // AND also to send the request if we want (send === true) + sframeChan.on('Q_REQUEST_ACCESS', function (data, cb) { + if (readOnly && hashes.editHash) { + return void cb({error: 'ALREADYKNOWN'}); + } + var send = data.send; + var metadata = data.metadata; + var owner, owners; + var _secret = secret; + if (metadata && metadata.roHref) { + var _parsed = Utils.Hash.parsePadUrl(metadata.roHref); + _secret = Utils.Hash.getSecrets(_parsed.type, _parsed.hash, metadata.password); + } + if (_secret.channel.length !== 32) { + return void cb({error: 'EINVAL'}); + } + var crypto = Crypto.createEncryptor(_secret.keys); + nThen(function (waitFor) { + // Try to get the owner's mailbox from the pad metadata first. + // If it's is an older owned pad, check if the owner is a friend + // or an acquaintance (from async-store directly in requestAccess) + var todo = function (obj) { + owners = obj.owners; + + var mailbox; + // Get the first available mailbox (the field can be an string or an object) + // TODO maybe we should send the request to all the owners? + if (typeof (obj.mailbox) === "string") { + mailbox = obj.mailbox; + } else if (obj.mailbox && obj.owners && obj.owners.length) { + mailbox = obj.mailbox[obj.owners[0]]; + } + if (mailbox) { + try { + var dataStr = crypto.decrypt(mailbox, true, true); + var data = JSON.parse(dataStr); + if (!data.notifications || !data.curvePublic) { return; } + owner = data; + } catch (e) { console.error(e); } + } + }; + + // If we already have metadata, use it, otherwise, try to get it + if (metadata) { return void todo(metadata); } + + Cryptpad.getPadMetadata({ + channel: _secret.channel + }, waitFor(function (obj) { + obj = obj || {}; + if (obj.error) { return; } + todo(obj); + })); + }).nThen(function () { + // If we are just checking (send === false) and there is a mailbox field, cb state true + // If there is no mailbox, we'll have to check if an owner is a friend in the worker + if (!send) { return void cb({state: Boolean(owner)}); } + + Cryptpad.padRpc.requestAccess({ + send: send, + channel: _secret.channel, + owner: owner, + owners: owners + }, cb); + }); + }); + + sframeChan.on('Q_BLOB_PASSWORD_CHANGE', function (data, cb) { + data.href = data.href || currentPad.href; + var onPending = function (cb) { + sframeChan.query('Q_BLOB_PASSWORD_CHANGE_PENDING', null, function (err, obj) { + if (obj && obj.cancel) { cb(); } + }); + }; + var updateProgress = function (p) { + sframeChan.event('EV_BLOB_PASSWORD_CHANGE_PROGRESS', p); + }; + Cryptpad.changeBlobPassword(data, { + onPending: onPending, + updateProgress: updateProgress + }, cb); + }); + + sframeChan.on('Q_OO_PASSWORD_CHANGE', function (data, cb) { + data.href = data.href; + Cryptpad.changeOOPassword(data, cb); + }); + + sframeChan.on('Q_PAD_PASSWORD_CHANGE', function (data, cb) { + data.href = data.href; + Cryptpad.changePadPassword(Cryptget, Crypto, data, cb); + }); + }; addCommonRpc(sframeChan, isSafe); @@ -1126,32 +1292,6 @@ define([ }); }); - sframeChan.on('Q_BLOB_PASSWORD_CHANGE', function (data, cb) { - data.href = data.href || currentPad.href; - var onPending = function (cb) { - sframeChan.query('Q_BLOB_PASSWORD_CHANGE_PENDING', null, function (err, obj) { - if (obj && obj.cancel) { cb(); } - }); - }; - var updateProgress = function (p) { - sframeChan.event('EV_BLOB_PASSWORD_CHANGE_PROGRESS', p); - }; - Cryptpad.changeBlobPassword(data, { - onPending: onPending, - updateProgress: updateProgress - }, cb); - }); - - sframeChan.on('Q_OO_PASSWORD_CHANGE', function (data, cb) { - data.href = data.href; - Cryptpad.changeOOPassword(data, cb); - }); - - sframeChan.on('Q_PAD_PASSWORD_CHANGE', function (data, cb) { - data.href = data.href; - Cryptpad.changePadPassword(Cryptget, Crypto, data, cb); - }); - sframeChan.on('Q_CHANGE_USER_PASSWORD', function (data, cb) { Cryptpad.changeUserPassword(Cryptget, edPublic, data, cb); }); @@ -1248,145 +1388,6 @@ define([ sframeChan.on('EV_GIVE_ACCESS', function (data, cb) { Cryptpad.padRpc.giveAccess(data, cb); }); - // REQUEST_ACCESS is used both to check IF we can contact an owner (send === false) - // AND also to send the request if we want (send === true) - sframeChan.on('Q_REQUEST_ACCESS', function (data, cb) { - if (readOnly && hashes.editHash) { - return void cb({error: 'ALREADYKNOWN'}); - } - var send = data.send; - var metadata = data.metadata; - var owner, owners; - var _secret = secret; - if (metadata && metadata.roHref) { - var _parsed = Utils.Hash.parsePadUrl(metadata.roHref); - _secret = Utils.Hash.getSecrets(_parsed.type, _parsed.hash, metadata.password); - } - if (_secret.channel.length !== 32) { - return void cb({error: 'EINVAL'}); - } - var crypto = Crypto.createEncryptor(_secret.keys); - nThen(function (waitFor) { - // Try to get the owner's mailbox from the pad metadata first. - // If it's is an older owned pad, check if the owner is a friend - // or an acquaintance (from async-store directly in requestAccess) - var todo = function (obj) { - owners = obj.owners; - - var mailbox; - // Get the first available mailbox (the field can be an string or an object) - // TODO maybe we should send the request to all the owners? - if (typeof (obj.mailbox) === "string") { - mailbox = obj.mailbox; - } else if (obj.mailbox && obj.owners && obj.owners.length) { - mailbox = obj.mailbox[obj.owners[0]]; - } - if (mailbox) { - try { - var dataStr = crypto.decrypt(mailbox, true, true); - var data = JSON.parse(dataStr); - if (!data.notifications || !data.curvePublic) { return; } - owner = data; - } catch (e) { console.error(e); } - } - }; - - // If we already have metadata, use it, otherwise, try to get it - if (metadata) { return void todo(metadata); } - - Cryptpad.getPadMetadata({ - channel: _secret.channel - }, waitFor(function (obj) { - obj = obj || {}; - if (obj.error) { return; } - todo(obj); - })); - }).nThen(function () { - // If we are just checking (send === false) and there is a mailbox field, cb state true - // If there is no mailbox, we'll have to check if an owner is a friend in the worker - if (!send) { return void cb({state: Boolean(owner)}); } - - Cryptpad.padRpc.requestAccess({ - send: send, - channel: _secret.channel, - owner: owner, - owners: owners - }, cb); - }); - }); - - // Add or remove our mailbox from the list if we're an owner - sframeChan.on('Q_UPDATE_MAILBOX', function (data, cb) { - var metadata = data.metadata; - var add = data.add; - var _secret = secret; - if (metadata && (metadata.href || metadata.roHref)) { - var _parsed = Utils.Hash.parsePadUrl(metadata.href || metadata.roHref); - _secret = Utils.Hash.getSecrets(_parsed.type, _parsed.hash, metadata.password); - } - if (_secret.channel.length !== 32) { - return void cb({error: 'EINVAL'}); - } - var crypto = Crypto.createEncryptor(_secret.keys); - nThen(function (waitFor) { - // If we already have metadata, use it, otherwise, try to get it - if (metadata) { return; } - - Cryptpad.getPadMetadata({ - channel: secret.channel - }, waitFor(function (obj) { - obj = obj || {}; - if (obj.error) { - waitFor.abort(); - return void cb(obj); - } - metadata = obj; - })); - }).nThen(function () { - // Get and maybe migrate the existing mailbox object - var owners = metadata.owners; - if (!Array.isArray(owners) || owners.indexOf(edPublic) === -1) { - return void cb({ error: 'INSUFFICIENT_PERMISSIONS' }); - } - - // Remove a mailbox - if (!add) { - // Old format: this is the mailbox of the first owner - if (typeof (metadata.mailbox) === "string" && metadata.mailbox) { - // Not our mailbox? abort - if (owners[0] !== edPublic) { - return void cb({ error: 'INSUFFICIENT_PERMISSIONS' }); - } - // Remove it - return void Cryptpad.setPadMetadata({ - channel: _secret.channel, - command: 'RM_MAILBOX', - value: [] - }, cb); - } else if (metadata.mailbox) { // New format - return void Cryptpad.setPadMetadata({ - channel: _secret.channel, - command: 'RM_MAILBOX', - value: [edPublic] - }, cb); - } - return void cb({ - error: 'NO_MAILBOX' - }); - } - // Add a mailbox - var toAdd = {}; - toAdd[edPublic] = crypto.encrypt(JSON.stringify({ - notifications: notifications, - curvePublic: curvePublic - })); - Cryptpad.setPadMetadata({ - channel: _secret.channel, - command: 'ADD_MAILBOX', - value: toAdd - }, cb); - }); - }); sframeChan.on('EV_BURN_PAD', function (channel) { if (!burnAfterReading) { return; } From 36e790dee6501b2e49097874eb7134cdb5eb85eb Mon Sep 17 00:00:00 2001 From: ansuz Date: Wed, 29 Apr 2020 14:40:42 -0400 Subject: [PATCH 06/11] style support tickets from premium users via a class --- www/admin/app-admin.less | 3 +++ www/support/ui.js | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/www/admin/app-admin.less b/www/admin/app-admin.less index 84532c6c5..787c31219 100644 --- a/www/admin/app-admin.less +++ b/www/admin/app-admin.less @@ -47,5 +47,8 @@ color: @colortheme_logo-2; } } + .cp-support-frompremium { + border: 2px solid red; + } } diff --git a/www/support/ui.js b/www/support/ui.js index 2038d8ddd..234953cb6 100644 --- a/www/support/ui.js +++ b/www/support/ui.js @@ -188,6 +188,7 @@ define([ var senderKey = content.sender && content.sender.edPublic; var fromMe = senderKey === privateData.edPublic; var fromAdmin = ctx.adminKeys.indexOf(senderKey) !== -1; + var fromPremium = Boolean(privateData.plan); var userData = h('div.cp-support-showdata', [ Messages.support_showData, @@ -199,8 +200,10 @@ define([ ev.stopPropagation(); }); + var adminClass = (fromAdmin? '.cp-support-fromadmin': ''); + var premiumClass = (fromPremium? '.cp-support-frompremium': ''); var name = Util.fixHTML(content.sender.name) || Messages.anonymous; - return h('div.cp-support-list-message' + (fromAdmin? '.cp-support-fromadmin': ''), { + return h('div.cp-support-list-message' + adminClass + premiumClass, { 'data-hash': hash }, [ h('div.cp-support-message-from' + (fromMe ? '.cp-support-fromme' : ''), [ From 6dc6fed21de3bd4e7c447841241db11d09c703bb Mon Sep 17 00:00:00 2001 From: ansuz Date: Wed, 29 Apr 2020 14:51:09 -0400 Subject: [PATCH 07/11] check whether support tickets came from a premium user, not whether the person viewing the content is a premium user --- www/support/ui.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/www/support/ui.js b/www/support/ui.js index 234953cb6..ba6ae9967 100644 --- a/www/support/ui.js +++ b/www/support/ui.js @@ -188,7 +188,7 @@ define([ var senderKey = content.sender && content.sender.edPublic; var fromMe = senderKey === privateData.edPublic; var fromAdmin = ctx.adminKeys.indexOf(senderKey) !== -1; - var fromPremium = Boolean(privateData.plan); + var fromPremium = Boolean(content.sender.plan); var userData = h('div.cp-support-showdata', [ Messages.support_showData, @@ -201,7 +201,7 @@ define([ }); var adminClass = (fromAdmin? '.cp-support-fromadmin': ''); - var premiumClass = (fromPremium? '.cp-support-frompremium': ''); + var premiumClass = (fromPremium && !fromAdmin? '.cp-support-frompremium': ''); var name = Util.fixHTML(content.sender.name) || Messages.anonymous; return h('div.cp-support-list-message' + adminClass + premiumClass, { 'data-hash': hash From 488f876ad6f617ff554263b57bd419a6804009be Mon Sep 17 00:00:00 2001 From: ansuz Date: Wed, 29 Apr 2020 17:23:29 -0400 Subject: [PATCH 08/11] lint compliance --- lib/log.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/log.js b/lib/log.js index 386ee6b41..a815500b0 100644 --- a/lib/log.js +++ b/lib/log.js @@ -28,7 +28,7 @@ var handlers = {}; handlers[level] = function (ctx, content) { console.log(content); }; }); ['warn', 'error'].forEach(function (level) { - handlers[level] = function (ctx, content) { console.error(content); } + handlers[level] = function (ctx, content) { console.error(content); }; }); var noop = function () {}; From 9975c27e84674e5cdcf73498c742617b605741d2 Mon Sep 17 00:00:00 2001 From: ansuz Date: Wed, 29 Apr 2020 17:24:38 -0400 Subject: [PATCH 09/11] increase timeout from one minute to three and decrease the number of parallel jobs per worker --- lib/workers/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/workers/index.js b/lib/workers/index.js index 911b34337..6858dffb5 100644 --- a/lib/workers/index.js +++ b/lib/workers/index.js @@ -8,7 +8,7 @@ const Workers = module.exports; const PID = process.pid; const DB_PATH = 'lib/workers/db-worker'; -const MAX_JOBS = 16; +const MAX_JOBS = 8; Workers.initialize = function (Env, config, _cb) { var cb = Util.once(Util.mkAsync(_cb)); @@ -97,7 +97,7 @@ Workers.initialize = function (Env, config, _cb) { // track which worker is doing which jobs state.tasks[txid] = msg; - response.expect(txid, cb, 60000); + response.expect(txid, cb, 180000); state.worker.send(msg); }; From 565a855734ba77532d3ecd39ac0bdeb69628b607 Mon Sep 17 00:00:00 2001 From: yflory Date: Thu, 30 Apr 2020 11:07:55 +0200 Subject: [PATCH 10/11] Fix upload error with audio files --- www/common/common-thumbnail.js | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/www/common/common-thumbnail.js b/www/common/common-thumbnail.js index e9c478bff..19b6713da 100644 --- a/www/common/common-thumbnail.js +++ b/www/common/common-thumbnail.js @@ -110,8 +110,11 @@ define([ c2.height = D.dim; var ctx = c2.getContext('2d'); - ctx.drawImage(canvas, D.x, D.y, D.w, D.h); - + try { + ctx.drawImage(canvas, D.x, D.y, D.w, D.h); + } catch (e) { + return void cb('ERROR'); + } cb(void 0, c2.toDataURL()); }; @@ -157,6 +160,8 @@ define([ viewport: page.getViewport(scale) }).promise.then(function () { return canvas; + }).catch(function () { + cb('ERROR'); }); }; PDFJS.getDocument(url).promise @@ -190,7 +195,8 @@ define([ }); reader.readAsText(blob); }; - Thumb.fromBlob = function (blob, cb) { + Thumb.fromBlob = function (blob, _cb) { + var cb = Util.once(_cb); if (blob.type.indexOf('video/') !== -1) { return void Thumb.fromVideoBlob(blob, cb); } @@ -200,7 +206,10 @@ define([ if (Util.isPlainTextFile(blob.type, blob.name)) { return void Thumb.fromPlainTextBlob(blob, cb); } - Thumb.fromImageBlob(blob, cb); + if (blob.type.indexOf('image/') !== -1) { + return void Thumb.fromImageBlob(blob, cb); + } + return void cb('NO_THUMBNAIL'); }; window.html2canvas = undefined; From dc383615e0f40cbfc72ddf95f8936427f22f63d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Benqu=C3=A9?= Date: Thu, 30 Apr 2020 15:46:21 +0100 Subject: [PATCH 11/11] different color for premium users --- www/admin/app-admin.less | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/www/admin/app-admin.less b/www/admin/app-admin.less index 787c31219..96d89b44b 100644 --- a/www/admin/app-admin.less +++ b/www/admin/app-admin.less @@ -32,9 +32,18 @@ .cp-support-list-message { &:last-child:not(.cp-support-fromadmin) { color: @colortheme_cp-red; - background-color: lighten(@colortheme_cp-red, 25%); + background-color: lighten(@colortheme_form-warning, 25%); .cp-support-showdata { - background-color: lighten(@colortheme_cp-red, 30%); + background-color: lighten(@colortheme_form-warning, 30%); + } + } + + &:last-child { + &.cp-support-frompremium { + background-color: lighten(@colortheme_cp-red, 25%); + .cp-support-showdata { + background-color: lighten(@colortheme_cp-red, 30%); + } } } } @@ -47,8 +56,5 @@ color: @colortheme_logo-2; } } - .cp-support-frompremium { - border: 2px solid red; - } }