From 07ee0c13826443744888ddff8328f4c9169b015f Mon Sep 17 00:00:00 2001 From: yflory Date: Wed, 28 Aug 2024 16:56:25 +0200 Subject: [PATCH 1/5] Fix missing timeout reference in roster #1613 --- www/common/outer/roster.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/common/outer/roster.js b/www/common/outer/roster.js index bcd952d9d..cb693436e 100644 --- a/www/common/outer/roster.js +++ b/www/common/outer/roster.js @@ -694,7 +694,7 @@ var factory = function (Util, Hash, CPNetflux, Sortify, nThen, Crypto, Feedback) // but since multiple users who can and should might be online at once // and since they'll all trigger this process at the same time... // we want to stagger attempts at random intervals - setTimeout(function () { + ref.internal.checkpointTimeout = setTimeout(function () { ref.internal.pendingCheckpointId = roster.checkpoint(function (err) { if (err) { console.error(err); } }); From 1619fb23ee04cde729ee6d473a3ed78c7876dbcd Mon Sep 17 00:00:00 2001 From: yflory Date: Thu, 29 Aug 2024 15:26:17 +0200 Subject: [PATCH 2/5] Automatically run TRIM_HISTORY on teams --- customize.dist/messages.js | 3 +++ www/common/outer/history.js | 15 ++++++++++++++ www/teams/app-team.less | 9 +++++++++ www/teams/inner.js | 40 +++++++++++++++++++++++++++++++++++++ 4 files changed, 67 insertions(+) diff --git a/customize.dist/messages.js b/customize.dist/messages.js index a5784b495..3104fab18 100755 --- a/customize.dist/messages.js +++ b/customize.dist/messages.js @@ -136,6 +136,9 @@ define(req, function(AppConfig, Default, Language) { } }; + + Messages.team_autoTrim = "Removing unused history... Please wait."; // XXX + return Messages; }); diff --git a/www/common/outer/history.js b/www/common/outer/history.js index 10078f116..d8df615cb 100644 --- a/www/common/outer/history.js +++ b/www/common/outer/history.js @@ -62,6 +62,19 @@ define([ return channels; }; + let getTeamChannels = function (ctx, teamId) { + let team = Util.find(ctx.store, ['proxy', 'teams', teamId]); + if (!team) { return []; } + + let channels = [team.channel]; + let roster = team.keys.roster; + channels.push({ + channel: roster.channel, + lastKnownHash: roster.lastKnownHash + }); + return channels; + }; + var getEdPublic = function (ctx, teamId) { if (!teamId) { return Util.find(ctx.store, ['proxy', 'edPublic']); } @@ -155,6 +168,8 @@ define([ // If account trim history, get the correct channels here if (data.account) { channels = getAccountChannels(ctx); + } else if (data.team) { + channels = getTeamChannels(ctx, data.team); } var size = 0; diff --git a/www/teams/app-team.less b/www/teams/app-team.less index db1efa29b..9e99d0e70 100644 --- a/www/teams/app-team.less +++ b/www/teams/app-team.less @@ -292,6 +292,15 @@ margin: 0; } } + .cp-team-trim { + display: flex; + align-items: center; + justify-content: center; + .fa { + font-size: 30px; + margin-right: 10px; + } + } .cp-teams-invite-uses { input { diff --git a/www/teams/inner.js b/www/teams/inner.js index 9f35da7d8..99157708d 100644 --- a/www/teams/inner.js +++ b/www/teams/inner.js @@ -191,6 +191,7 @@ define([ 'cp-team-avatar', 'cp-team-export', 'cp-team-delete', + 'cp-team-history', ], }; @@ -398,6 +399,43 @@ define([ ]); }); + + let AUTOTRIM_LIMIT = 102400; // 100kB history before auto trim + var trimHistory = function(common) { + var size; + var channels = []; + nThen(function(waitFor) { + APP.history.execCommand('GET_HISTORY_SIZE', { + team: APP.team, + channels: [] + }, waitFor(function(obj) { + if (obj && obj.error) { + waitFor.abort(); + console.error(obj.error); + return; + } + channels = obj.channels; + size = Number(obj.size); + })); + }).nThen(function() { + if (!size || size < AUTOTRIM_LIMIT) { + // Nothing to delete + return; + } + var div = h('div.cp-team-trim', [ + h('span.fa.fa-spin.fa-spinner'), + h('span', Messages.team_autoTrim) + ]); + let modal = UI.openCustomModal(UI.dialog.customModal(div, {buttons: []})); + console.log('Trimming team history', APP.team, size); + APP.history.execCommand('TRIM_HISTORY', { + channels: channels + }, function(obj) { + if (obj && obj.error) { console.error(obj.error); } + UI.removeModals(); + }); + }); + }; var openTeam = function (common, id, team) { var sframeChan = common.getSframeChannel(); APP.module.execCommand('SUBSCRIBE', id, function () { @@ -422,6 +460,7 @@ define([ APP.team = id; APP.teamEdPublic = Util.find(team, ['keys', 'drive', 'edPublic']); buildUI(common, true, team.owner); + if (team.owner) { trimHistory(common); } }); }); }; @@ -1556,6 +1595,7 @@ define([ } }; + APP.history = common.makeUniversal('history'); APP.module = common.makeUniversal('team', { onEvent: onEvent }); From fddf4a0753633e06bc6a58b08dcd2e77c2eccddf Mon Sep 17 00:00:00 2001 From: yflory Date: Thu, 29 Aug 2024 16:10:05 +0200 Subject: [PATCH 3/5] Add tool to force-trim a roster to the report app --- www/common/outer/roster.js | 5 ++- www/report/main.js | 73 +++++++++++++++++++++++++++++++++++++- 2 files changed, 76 insertions(+), 2 deletions(-) diff --git a/www/common/outer/roster.js b/www/common/outer/roster.js index cb693436e..72379c3b1 100644 --- a/www/common/outer/roster.js +++ b/www/common/outer/roster.js @@ -730,7 +730,10 @@ var factory = function (Util, Hash, CPNetflux, Sortify, nThen, Crypto, Feedback) //console.log("Sending with id [%s]", id, msg); //console.log(); - response.expect(id, cb, TIMEOUT_INTERVAL); + response.expect(id, function (err, state) { + if (err) { return void cb(err); } + cb(void 0, state, id); + }, TIMEOUT_INTERVAL); anon_rpc.send('WRITE_PRIVATE_MESSAGE', [ channel, ciphertext diff --git a/www/report/main.js b/www/report/main.js index bfc03a8fe..08df20511 100644 --- a/www/report/main.js +++ b/www/report/main.js @@ -20,6 +20,9 @@ define([ '/common/userObject.js', '/common/clipboard.js', '/common/outer/login-block.js', + '/common/outer/roster.js', + '/common/rpc.js', + '/common/pinpad.js', '/components/tweetnacl/nacl-fast.min.js', @@ -27,7 +30,7 @@ define([ 'less!/customize/src/less2/pages/page-report.less', ], function ($, ApiConfig, h, Messages, nThen, Hash, Util, Constants, Crypt, Cryptpad, Cache, UI, CPNetflux, - Crypto, UserObject, Clipboard, Block) { + Crypto, UserObject, Clipboard, Block, Roster, Rpc, Pinpad) { var $report = $('#cp-report'); var blockHash = localStorage.Block_hash; if (!blockHash) { @@ -41,6 +44,21 @@ define([ }); } + let report = true; + let fixRoster = false; + + let urlHash = window.location.hash.slice(1); + if (urlHash) { + let s = urlHash.split('='); + let key = s[0]; + let value = s[1]; + if (key === "roster") { + report = false; + fixRoster = value; // teamid + } + } + + var addReport = function (str) { $report.append(h('div', str)); }; @@ -153,6 +171,8 @@ define([ addReport('Teams: ' + Object.keys(proxy.teams || {}).join(', ')); addReport('-------------------'); + if (!report) { return; } + var n = nThen; Object.keys(drive.sharedFolders || {}).forEach(function (id) { n = n(function (w) { @@ -188,16 +208,67 @@ define([ addReport('==================='); var n = nThen; Object.keys(proxy.teams || {}).forEach(function (id) { + // If we're in "repair" mode, only load the affected team + if (!report && Number(fixRoster) !== Number(id)) { return; } n = n(function (w) { var next = w(); var obj = proxy.teams[id]; var team; addReport('Load team. ID: ' + id + '. Channel ID: '+ obj.channel); + addReport('Roster channel: ' + obj.keys.roster.channel); + addReport('Roster lkh: ' + obj.keys.roster.lastKnownHash); if (!obj.hash) { addReport("View only"); } var teamSecret = Hash.getSecrets('team', obj.hash || obj.roHash, obj.password); var cryptor = UserObject.createCryptor(teamSecret.keys.secondaryKey); + + // Repair roster mode + if (!report) { + let roster = obj.keys.roster; + let rpc, anon_rpc; + if (!obj.owner || !roster.edit) { + return void addReport('Roster error: only owners can repair'); + } + n(w => { + Rpc.createAnonymous(network, w(function (e, call) { + anon_rpc = call; + })); + Pinpad.create(network, proxy, w(function (e, call) { + rpc = call; + })); + }).nThen(w => { + if (!anon_rpc || !rpc) { + return void addReport('RPC error'); + } + var rosterKeys = Crypto.Team.deriveMemberKeys(roster.edit, { + curvePublic: proxy.curvePublic, + curvePrivate: proxy.curvePrivate + }); + let store = { anon_rpc }; + Roster.create({ + network: network, + channel: roster.channel, + keys: rosterKeys, + store: store, + lastKnownHash: roster.lastKnownHash + }, w(function (err, _roster) { + if (err) { return void addReport('Fix roster error', err); } + _roster.checkpoint(w((err, state, hash) => { + if (err) { addReport('Fix roster error', err); } + rpc.trimHistory({ + channel: roster.channel, + hash + }, w(function (err) { + console.error(arguments); + if (err) { addReport('Trim roster error', err); } + })); + })) + })); + }).nThen(next); + return; + } + // Check team drive nThen(function (ww) { addReport('Team drive'); From f1571d2bad56b5eed2038c107c2b9c322deeecf5 Mon Sep 17 00:00:00 2001 From: yflory Date: Thu, 29 Aug 2024 16:46:12 +0200 Subject: [PATCH 4/5] Use team channel instead of team id for roster fix --- www/report/main.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/www/report/main.js b/www/report/main.js index 08df20511..45c62d234 100644 --- a/www/report/main.js +++ b/www/report/main.js @@ -209,10 +209,10 @@ define([ var n = nThen; Object.keys(proxy.teams || {}).forEach(function (id) { // If we're in "repair" mode, only load the affected team - if (!report && Number(fixRoster) !== Number(id)) { return; } + var obj = proxy.teams[id]; + if (!report && fixRoster !== obj.channel) { return; } n = n(function (w) { var next = w(); - var obj = proxy.teams[id]; var team; addReport('Load team. ID: ' + id + '. Channel ID: '+ obj.channel); addReport('Roster channel: ' + obj.keys.roster.channel); From f0446e5719721b7ded42a9b396335f71942e1fa4 Mon Sep 17 00:00:00 2001 From: yflory Date: Mon, 2 Sep 2024 17:34:11 +0200 Subject: [PATCH 5/5] lint compliance --- www/report/main.js | 2 +- www/teams/inner.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/www/report/main.js b/www/report/main.js index 45c62d234..9888fb7cd 100644 --- a/www/report/main.js +++ b/www/report/main.js @@ -263,7 +263,7 @@ define([ console.error(arguments); if (err) { addReport('Trim roster error', err); } })); - })) + })); })); }).nThen(next); return; diff --git a/www/teams/inner.js b/www/teams/inner.js index 1c231a1a5..a1c9f2d86 100644 --- a/www/teams/inner.js +++ b/www/teams/inner.js @@ -401,7 +401,7 @@ define([ let AUTOTRIM_LIMIT = 102400; // 100kB history before auto trim - var trimHistory = function(common) { + var trimHistory = function () { var size; var channels = []; nThen(function(waitFor) { @@ -426,7 +426,7 @@ define([ h('span.fa.fa-spin.fa-spinner'), h('span', Messages.team_autoTrim) ]); - let modal = UI.openCustomModal(UI.dialog.customModal(div, {buttons: []})); + UI.openCustomModal(UI.dialog.customModal(div, {buttons: []})); console.log('Trimming team history', APP.team, size); APP.history.execCommand('TRIM_HISTORY', { channels: channels