mirror of
https://github.com/cryptpad/cryptpad.git
synced 2026-09-14 11:05:41 +05:00
Move pad functions to a new component
This commit is contained in:
parent
900ab02404
commit
c7d8750bc0
@ -617,7 +617,7 @@ const factory = (UserObject, Util, Hash,
|
||||
if (data.password) { folderData.password = data.password; }
|
||||
if (data.owned) { folderData.owners = [Env.edPublic]; }
|
||||
}).nThen(function (waitFor) {
|
||||
Env.Store.getPadMetadata(null, {
|
||||
Env.Store.pad.getMetadata(null, {
|
||||
channel: folderData.channel
|
||||
}, waitFor(function (obj) {
|
||||
if (obj && (obj.error || obj.rejected)) {
|
||||
@ -669,7 +669,7 @@ const factory = (UserObject, Util, Hash,
|
||||
}
|
||||
if (data.folderData) {
|
||||
// If we're importing a folder, check its serverside metadata
|
||||
Env.Store.getPadMetadata(null, { channel: folderData.channel }, function (md) {
|
||||
Env.Store.pad.getMetadata(null, { channel: folderData.channel }, function (md) {
|
||||
var fData = Env.user.proxy[UserObject.SHARED_FOLDERS][id];
|
||||
if (md.owners) { fData.owners = md.owners; }
|
||||
if (md.expire) { fData.expire = +md.expire; }
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
const factory = (Sortify, UserObject, ProxyManager,
|
||||
Migrate, Hash, Util, Constants, Feedback,
|
||||
Realtime, Messaging, Pinpad, Rpc, Merge, Cache,
|
||||
SF, AccountTS, DriveTS, Cursor,
|
||||
SF, AccountTS, DriveTS, PadTS, Cursor,
|
||||
Support, Integration, OnlyOffice,
|
||||
Mailbox, Profile, Team, Messenger, History,
|
||||
Calendar, BadgeTS, Block, NetConfig,
|
||||
@ -14,6 +14,7 @@ const factory = (Sortify, UserObject, ProxyManager,
|
||||
|
||||
const Account = AccountTS.Account;
|
||||
const Drive = DriveTS.Drive;
|
||||
const Pad = PadTS.Pad;
|
||||
const Badge = BadgeTS.Badge;
|
||||
const window = globalThis;
|
||||
globalThis.nacl = globalThis.nacl || Crypto.Nacl;
|
||||
@ -24,7 +25,6 @@ const factory = (Sortify, UserObject, ProxyManager,
|
||||
const Saferphore = Util.Saferphore;
|
||||
var onReadyEvt = Util.mkEvent(true);
|
||||
var onCacheReadyEvt = Util.mkEvent(true);
|
||||
var onJoinedEvt = Util.mkEvent(true);
|
||||
var onPadRejectedEvt = Util.mkEvent(true);
|
||||
|
||||
const setCustomize = data => {
|
||||
@ -63,16 +63,20 @@ const factory = (Sortify, UserObject, ProxyManager,
|
||||
|
||||
Store.onReadyEvt = onReadyEvt;
|
||||
|
||||
Store.pad = Pad.init({
|
||||
Store, store, postMessage, broadcast
|
||||
});
|
||||
|
||||
// Drive clients
|
||||
var driveEventClients = [];
|
||||
var sendDriveEvent = function (q, data, sender) {
|
||||
var sendDriveEvent = store.sendDriveEvent = function (q, data, sender) {
|
||||
driveEventClients.forEach(function (cId) {
|
||||
if (cId === sender) { return; }
|
||||
postMessage(cId, q, data);
|
||||
});
|
||||
};
|
||||
|
||||
var getStore = function (teamId) {
|
||||
var getStore = Store.getStore = function (teamId) {
|
||||
if (!teamId) { return store; }
|
||||
try {
|
||||
var teams = store.modules['team'];
|
||||
@ -392,38 +396,6 @@ const factory = (Sortify, UserObject, ProxyManager,
|
||||
});
|
||||
};
|
||||
|
||||
var myDeletions = {};
|
||||
Store.removeOwnedChannel = function (clientId, data, cb) {
|
||||
// "data" used to be a string (channelID), now it can also be an object
|
||||
// data.force tells us we can safely remove the drive ID
|
||||
var channel = data;
|
||||
var force = false;
|
||||
var teamId;
|
||||
var reason;
|
||||
if (data && typeof(data) === "object") {
|
||||
channel = data.channel;
|
||||
force = data.force;
|
||||
teamId = data.teamId;
|
||||
reason = data.reason;
|
||||
}
|
||||
|
||||
if (channel === store.driveChannel && !force) {
|
||||
return void cb({error: 'User drive removal blocked!'});
|
||||
}
|
||||
|
||||
var s = getStore(teamId);
|
||||
if (!s) { return void cb({ error: 'ENOTFOUND' }); }
|
||||
if (!s.rpc) { return void cb({error: 'RPC_NOT_READY'}); }
|
||||
|
||||
// If this channel is loaded, remember that we deleted it ourselves
|
||||
if (Store.channels[channel]) { myDeletions[channel] = true; }
|
||||
|
||||
s.rpc.removeOwnedChannel(channel, function (err) {
|
||||
if (err) { delete myDeletions[channel]; }
|
||||
cb({error:err});
|
||||
}, reason);
|
||||
};
|
||||
|
||||
var arePinsSynced = function (cb) {
|
||||
if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); }
|
||||
|
||||
@ -855,7 +827,7 @@ const factory = (Sortify, UserObject, ProxyManager,
|
||||
}));
|
||||
}).nThen(function (_w) {
|
||||
if (otherOwners) {
|
||||
Store.setPadMetadata(null, {
|
||||
Store.pad.setMetadata(null, {
|
||||
channel: c,
|
||||
command: 'RM_OWNERS',
|
||||
value: [edPublic],
|
||||
@ -918,7 +890,7 @@ const factory = (Sortify, UserObject, ProxyManager,
|
||||
}).nThen(function (waitFor) {
|
||||
// Delete Drive
|
||||
store.ownDeletion = true;
|
||||
Store.removeOwnedChannel(clientId, {
|
||||
Store.pad.destroy(clientId, {
|
||||
channel: store.driveChannel,
|
||||
force: true
|
||||
}, waitFor());
|
||||
@ -1200,7 +1172,7 @@ const factory = (Sortify, UserObject, ProxyManager,
|
||||
return void cb({ notStored: true });
|
||||
}
|
||||
|
||||
var channelData = Store.channels && Store.channels[channel];
|
||||
var channelData = Store.pad.getChannels()[channel];
|
||||
|
||||
var owners;
|
||||
if (channelData && channelData.wc && channel === channelData.wc.id) {
|
||||
@ -1674,8 +1646,6 @@ const factory = (Sortify, UserObject, ProxyManager,
|
||||
/////////////////////// PAD //////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////
|
||||
|
||||
var channels = Store.channels = store.channels = {};
|
||||
|
||||
Store.getSnapshot = function (clientId, data, cb) {
|
||||
Store.getHistoryRange(clientId, {
|
||||
cpCount: 1,
|
||||
@ -1684,55 +1654,6 @@ const factory = (Sortify, UserObject, ProxyManager,
|
||||
}, cb);
|
||||
};
|
||||
|
||||
var getVersionHash = function (clientId, data) {
|
||||
var validateKey;
|
||||
var fakeNetflux = Hash.createChannelId();
|
||||
nThen(function (waitFor) {
|
||||
Store.getPadMetadata(null, {
|
||||
channel: data.channel
|
||||
}, waitFor(function (md) {
|
||||
if (md && md.rejected) {
|
||||
postMessage(clientId, "PAD_ERROR", {type: "ERESTRICTED"});
|
||||
waitFor.abort();
|
||||
return;
|
||||
}
|
||||
validateKey = md.validateKey;
|
||||
}));
|
||||
}).nThen(function () {
|
||||
Store.getHistoryRange(clientId, {
|
||||
cpCount: 1,
|
||||
channel: data.channel,
|
||||
lastKnownHash: data.versionHash
|
||||
}, function (obj) {
|
||||
if (obj && obj.error) {
|
||||
postMessage(clientId, "PAD_ERROR", obj.error);
|
||||
return;
|
||||
}
|
||||
var msgs = obj.messages || [];
|
||||
if (msgs.length && msgs[msgs.length - 1].serverHash !== data.versionHash) {
|
||||
postMessage(clientId, "PAD_ERROR", {type: "HASH_NOT_FOUND"});
|
||||
return;
|
||||
}
|
||||
postMessage(clientId, "PAD_CONNECT", {
|
||||
myID: fakeNetflux,
|
||||
id: data.channel,
|
||||
members: [fakeNetflux]
|
||||
});
|
||||
(obj.messages || []).forEach(function (data) {
|
||||
postMessage(clientId, "PAD_MESSAGE", {
|
||||
msg: data.msg,
|
||||
time: data.time,
|
||||
user: fakeNetflux.slice(0,16), // fake history keeper to avoid validate
|
||||
});
|
||||
});
|
||||
if (validateKey && store.messenger) {
|
||||
store.messenger.storeValidateKey(data.channel, validateKey);
|
||||
}
|
||||
postMessage(clientId, "PAD_READY");
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
Store.onRejected = function (allowed, _cb) {
|
||||
var cb = Util.once(Util.mkAsync(_cb));
|
||||
if (!Array.isArray(allowed)) { return void cb('ERESTRICTED'); }
|
||||
@ -1783,226 +1704,6 @@ const factory = (Sortify, UserObject, ProxyManager,
|
||||
});
|
||||
};
|
||||
|
||||
Store.joinPad = function (clientId, data) {
|
||||
if (data.versionHash) {
|
||||
return void getVersionHash(clientId, data);
|
||||
}
|
||||
if (!Hash.isValidChannel(data.channel)) {
|
||||
return void postMessage(clientId, "PAD_ERROR", 'INVALID_CHAN');
|
||||
}
|
||||
var isNew = typeof channels[data.channel] === "undefined";
|
||||
var channel = channels[data.channel] = channels[data.channel] || {
|
||||
queue: [],
|
||||
data: {},
|
||||
clients: [],
|
||||
bcast: function (cmd, data, notMe) {
|
||||
channel.clients.forEach(function (cId) {
|
||||
if (cId === notMe) { return; }
|
||||
postMessage(cId, cmd, data);
|
||||
});
|
||||
},
|
||||
history: [],
|
||||
pushHistory: function (msg, isCp) {
|
||||
if (isCp) {
|
||||
// the current message is a checkpoint.
|
||||
// push it to your worker's history, prepending it with cp|
|
||||
// cp| and anything else related to checkpoints has already
|
||||
// been stripped by chainpad-netflux-worker or within async store
|
||||
// when the message was outgoing.
|
||||
channel.history.push('cp|' + msg);
|
||||
// since the latest message is a checkpoint, we are able to drop
|
||||
// some of the older history, but we can't rely on checkpoints being
|
||||
// correct, as they might be checkpoints from different forks
|
||||
var i;
|
||||
for (i = channel.history.length - 101; i > 0; i--) {
|
||||
if (/^cp\|/.test(channel.history[i])) { break; }
|
||||
}
|
||||
channel.history = channel.history.slice(Math.max(i, 0));
|
||||
return;
|
||||
}
|
||||
channel.history.push(msg);
|
||||
}
|
||||
};
|
||||
if (channel.clients.indexOf(clientId) === -1) {
|
||||
channel.clients.push(clientId);
|
||||
}
|
||||
|
||||
if (!isNew && channel.wc) {
|
||||
postMessage(clientId, "PAD_CONNECT", {
|
||||
myID: channel.wc.myID,
|
||||
id: channel.wc.id,
|
||||
members: channel.wc.members
|
||||
});
|
||||
channel.wc.members.forEach(function (m) {
|
||||
postMessage(clientId, "PAD_JOIN", m);
|
||||
});
|
||||
channel.history.forEach(function (msg) {
|
||||
postMessage(clientId, "PAD_MESSAGE", {
|
||||
msg: CpNetflux.removeCp(msg),
|
||||
user: channel.wc.myID,
|
||||
validateKey: channel.data.validateKey
|
||||
});
|
||||
});
|
||||
postMessage(clientId, "PAD_READY");
|
||||
|
||||
return;
|
||||
}
|
||||
var onError = function (err) {
|
||||
// If it's a deletion started from this worker, different UI message
|
||||
if (err && err.type === "EDELETED" && myDeletions[data.channel]) {
|
||||
delete myDeletions[channel];
|
||||
err.ownDeletion = true;
|
||||
}
|
||||
channel.bcast("PAD_ERROR", err);
|
||||
|
||||
if (err && err.type === "EDELETED" && Cache && Cache.clearChannel) {
|
||||
Cache.clearChannel(data.channel);
|
||||
}
|
||||
|
||||
// If this is a DELETED, EXPIRED or RESTRICTED pad, leave the channel
|
||||
if (["EDELETED", "EEXPIRED", "ERESTRICTED"].indexOf(err.type) === -1) { return; }
|
||||
Store.leavePad(null, data, function () {});
|
||||
};
|
||||
var conf = {
|
||||
Cache: store.neverCache ? undefined : Cache,
|
||||
priority: 1,
|
||||
onCacheStart: function () {
|
||||
postMessage(clientId, "PAD_CACHE");
|
||||
},
|
||||
onCacheReady: function () {
|
||||
postMessage(clientId, "PAD_CACHE_READY");
|
||||
},
|
||||
onReady: function (pad) {
|
||||
var padData = pad.metadata || {};
|
||||
channel.data = padData;
|
||||
if (padData && padData.validateKey && store.messenger) {
|
||||
store.messenger.storeValidateKey(data.channel, padData.validateKey);
|
||||
}
|
||||
postMessage(clientId, "PAD_READY", pad.noCache);
|
||||
/*
|
||||
if (!store.proxy) {
|
||||
postMessage(clientId, "PAD_READY", pad.noCache);
|
||||
return;
|
||||
}
|
||||
onReadyEvt.reg(function () {
|
||||
postMessage(clientId, "PAD_READY", pad.noCache);
|
||||
});
|
||||
*/
|
||||
},
|
||||
onMessage: function (m, user, validateKey, isCp, hash) {
|
||||
channel.lastHash = hash;
|
||||
channel.pushHistory(m, isCp);
|
||||
channel.bcast("PAD_MESSAGE", {
|
||||
user: user,
|
||||
msg: m,
|
||||
validateKey: validateKey
|
||||
});
|
||||
},
|
||||
onJoin: function (m) {
|
||||
channel.bcast("PAD_JOIN", m);
|
||||
},
|
||||
onLeave: function (m) {
|
||||
channel.bcast("PAD_LEAVE", m);
|
||||
},
|
||||
onError: onError,
|
||||
onChannelError: onError,
|
||||
onRejected: Store.onRejected,
|
||||
onConnectionChange: function (info) {
|
||||
if (!info.state) {
|
||||
channel.bcast("PAD_DISCONNECT");
|
||||
}
|
||||
},
|
||||
onMetadataUpdate: function (metadata) {
|
||||
channel.data = metadata || {};
|
||||
getAllStores().forEach(function (s) {
|
||||
var allData = s.manager.findChannel(data.channel, true);
|
||||
allData.forEach(function (obj) {
|
||||
obj.data.owners = metadata.owners;
|
||||
obj.data.atime = +new Date();
|
||||
if (metadata.expire) {
|
||||
obj.data.expire = +metadata.expire;
|
||||
}
|
||||
});
|
||||
var send = s.sendEvent || sendDriveEvent;
|
||||
send('DRIVE_CHANGE', {
|
||||
path: ['drive', UserObject.FILES_DATA]
|
||||
});
|
||||
});
|
||||
channel.bcast("PAD_METADATA", metadata);
|
||||
},
|
||||
crypto: {
|
||||
// The encryption and decryption is done in the outer window.
|
||||
// This async-store only deals with already encrypted messages.
|
||||
encrypt: function (m) { return m; },
|
||||
decrypt: function (m) { return m; }
|
||||
},
|
||||
noChainPad: true,
|
||||
channel: data.channel,
|
||||
metadata: data.metadata,
|
||||
network: store.network || store.networkPromise,
|
||||
websocketURL: NetConfig.getWebsocketURL(),
|
||||
onInit: function () {
|
||||
onJoinedEvt.fire();
|
||||
},
|
||||
//readOnly: data.readOnly,
|
||||
onConnect: function (wc, sendMessage) {
|
||||
channel.sendMessage = function (msg, cId, cb) {
|
||||
// Send to server
|
||||
sendMessage(msg, function (err) {
|
||||
if (err) {
|
||||
return void cb({ error: err });
|
||||
}
|
||||
// Broadcast to other tabs
|
||||
channel.lastHash = msg.slice(0,64);
|
||||
channel.pushHistory(CpNetflux.removeCp(msg), /^cp\|/.test(msg));
|
||||
channel.bcast("PAD_MESSAGE", {
|
||||
user: wc.myID,
|
||||
msg: CpNetflux.removeCp(msg),
|
||||
validateKey: channel.data.validateKey
|
||||
}, cId);
|
||||
cb();
|
||||
});
|
||||
};
|
||||
channel.wc = wc;
|
||||
channel.queue.forEach(function (data) {
|
||||
channel.sendMessage(data.message, clientId);
|
||||
});
|
||||
channel.queue = [];
|
||||
channel.bcast("PAD_CONNECT", {
|
||||
myID: wc.myID,
|
||||
id: wc.id,
|
||||
members: wc.members
|
||||
});
|
||||
}
|
||||
};
|
||||
channel.cpNf = CpNetflux.start(conf);
|
||||
};
|
||||
Store.leavePad = function (clientId, data, cb) {
|
||||
var channel = channels[data.channel];
|
||||
if (!channel || !channel.cpNf) { return void cb ({error: 'EINVAL'}); }
|
||||
Store.dropChannel(data.channel);
|
||||
cb();
|
||||
};
|
||||
Store.sendPadMsg = function (clientId, data, cb) {
|
||||
var msg = data.msg;
|
||||
var channel = channels[data.channel];
|
||||
if (!channel) {
|
||||
return; }
|
||||
if (!channel.wc) {
|
||||
channel.queue.push(msg);
|
||||
return void cb();
|
||||
}
|
||||
channel.sendMessage(msg, clientId, cb);
|
||||
};
|
||||
|
||||
Store.corruptedCache = function (clientId, channel) {
|
||||
var chan = channels[channel];
|
||||
if (!chan || !chan.cpNf) { return; }
|
||||
Cache.clearChannel(channel);
|
||||
if (!chan.cpNf.resetCache) { return; }
|
||||
chan.cpNf.resetCache();
|
||||
};
|
||||
|
||||
// Unpin and pin the new channel in all team when changing a pad password
|
||||
Store.changePadPasswordPin = function (clientId, data, cb) {
|
||||
var oldChannel = data.oldChannel;
|
||||
@ -2079,15 +1780,6 @@ const factory = (Sortify, UserObject, ProxyManager,
|
||||
cb();
|
||||
};
|
||||
|
||||
Store.getLastHash = function (clientId, data, cb) {
|
||||
var chan = channels[data.channel];
|
||||
if (!chan) { return void cb({error: 'ENOCHAN'}); }
|
||||
if (!chan.lastHash) { return void cb({error: 'EINVAL'}); }
|
||||
cb({
|
||||
hash: chan.lastHash
|
||||
});
|
||||
};
|
||||
|
||||
// Delete a pad received with a burn after reading URL
|
||||
|
||||
var notifyOwnerPadRemoved = function (data, obj) {
|
||||
@ -2147,7 +1839,7 @@ const factory = (Sortify, UserObject, ProxyManager,
|
||||
edPrivate: Hash.encodeBase64(pair.secretKey)
|
||||
}, function (e, rpc) {
|
||||
if (e) { return void console.error(e); }
|
||||
Store.getPadMetadata(null, {
|
||||
Store.pad.getMetadata(null, {
|
||||
channel: channel
|
||||
}, function (md) {
|
||||
rpc.removeOwnedChannel(channel, function (err) {
|
||||
@ -2162,75 +1854,6 @@ const factory = (Sortify, UserObject, ProxyManager,
|
||||
}
|
||||
};
|
||||
|
||||
// Fetch the latest version of the metadata on the server and return it.
|
||||
// If the pad is stored in our drive, update the local values of "owners" and "expire"
|
||||
Store.getPadMetadata = function (clientId, data, _cb) {
|
||||
var cb = Util.once(Util.mkAsync(_cb));
|
||||
|
||||
if (store.offline || !store.anon_rpc) { return void cb({ error: 'OFFLINE' }); }
|
||||
if (!data.channel) { return void cb({ error: 'ENOTFOUND'}); }
|
||||
if (data.channel.length !== 32) { return void cb({ error: 'EINVAL'}); }
|
||||
if (!Hash.isValidChannel(data.channel)) {
|
||||
Feedback.send('METADATA_INVALID_CHAN');
|
||||
return void cb({ error: 'EINVAL' });
|
||||
}
|
||||
store.anon_rpc.send('GET_METADATA', data.channel, function (err, obj) {
|
||||
if (err) { return void cb({error: err}); }
|
||||
var metadata = (obj && obj[0]) || {};
|
||||
cb(metadata);
|
||||
|
||||
// If you don't have access to the metadata, stop here
|
||||
// (we can't update the local data)
|
||||
if (metadata.rejected) { return; }
|
||||
|
||||
// Update owners and expire time in the drive
|
||||
getAllStores().forEach(function (s) {
|
||||
var allData = s.manager.findChannel(data.channel, true);
|
||||
var changed = false;
|
||||
allData.forEach(function (obj) {
|
||||
if (Sortify(obj.data.owners) !== Sortify(metadata.owners)) {
|
||||
changed = true;
|
||||
}
|
||||
obj.data.owners = metadata.owners;
|
||||
obj.data.atime = +new Date();
|
||||
if (metadata.expire) {
|
||||
obj.data.expire = +metadata.expire;
|
||||
}
|
||||
});
|
||||
// If we had to change the "owners" field, redraw the drive UI
|
||||
if (!changed) { return; }
|
||||
var send = s.sendEvent || sendDriveEvent;
|
||||
send('DRIVE_CHANGE', {
|
||||
path: ['drive', UserObject.FILES_DATA]
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
Store.setPadMetadata = function (clientId, data, cb) {
|
||||
if (!data.channel) { return void cb({ error: 'ENOTFOUND'}); }
|
||||
if (!data.command) { return void cb({ error: 'EINVAL' }); }
|
||||
var s = getStore(data.teamId);
|
||||
if (!s) { return void cb({ error: 'ENOTFOUND' }); }
|
||||
|
||||
var otherChannels = data.channels;
|
||||
delete data.channels;
|
||||
s.rpc.setMetadata(data, function (err, res) {
|
||||
if (err) { return void cb({ error: err }); }
|
||||
if (!Array.isArray(res) || !res.length) { return void cb({}); }
|
||||
cb(res[0]);
|
||||
});
|
||||
// If we have other related channels, send the command for them too
|
||||
if (Array.isArray(otherChannels)) {
|
||||
otherChannels.forEach(function (chan) {
|
||||
var _d = Util.clone(data);
|
||||
_d.channel = chan;
|
||||
Store.setPadMetadata(clientId, _d, function () {
|
||||
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
Store.deleteMailboxMessage = function (clientId, data, cb) {
|
||||
if (!store.anon_rpc) { return void cb({error: 'RPC_NOT_READY'}); }
|
||||
store.anon_rpc.send('DELETE_MAILBOX_MESSAGE', data, function (e) {
|
||||
@ -2593,114 +2216,21 @@ const factory = (Sortify, UserObject, ProxyManager,
|
||||
s.manager.command(cmdData, cb2);
|
||||
};
|
||||
|
||||
|
||||
// Check if this is a channel that we shouldn't leave when closing the debug app
|
||||
var alwaysOnline = function (chanId) {
|
||||
if (!store) { return; }
|
||||
// Drive
|
||||
if (store.driveChannel === chanId) { return true; }
|
||||
// Shared folders
|
||||
if (SF.isSharedFolderChannel(chanId)) { return true; }
|
||||
// Teams
|
||||
if (Util.find(store, ['proxy', 'teams'])) {
|
||||
var t = Util.find(store, ['proxy', 'teams']) || {};
|
||||
return Object.keys(t).some(function (id) {
|
||||
return t[id].channel === chanId;
|
||||
});
|
||||
}
|
||||
// Profile
|
||||
if (Util.find(store, ['proxy', 'profile', 'href'])) {
|
||||
return Hash.hrefToHexChannelId(Util.find(store, ['proxy', 'profile', 'href']))
|
||||
=== chanId;
|
||||
}
|
||||
};
|
||||
|
||||
var dropChannel = Store.dropChannel = function (chanId) {
|
||||
console.error('Drop channel', chanId);
|
||||
|
||||
try {
|
||||
store.messenger.leavePad(chanId);
|
||||
} catch (e) { console.error(e); }
|
||||
try {
|
||||
store.modules['cursor'].leavePad(chanId);
|
||||
} catch (e) { console.error(e); }
|
||||
try {
|
||||
store.modules['integration'].leavePad(chanId);
|
||||
} catch (e) { console.error(e); }
|
||||
try {
|
||||
store.onlyoffice.leavePad(chanId);
|
||||
} catch (e) { console.error(e); }
|
||||
|
||||
try {
|
||||
if (alwaysOnline(chanId)) {
|
||||
delete Store.channels[chanId];
|
||||
return;
|
||||
}
|
||||
} catch (e) { console.error(e); }
|
||||
|
||||
try {
|
||||
Cache.leaveChannel(chanId);
|
||||
} catch (e) { console.error(e); }
|
||||
|
||||
if (!Store.channels[chanId]) { return; }
|
||||
|
||||
if (Store.channels[chanId].cpNf) {
|
||||
Store.channels[chanId].cpNf.stop();
|
||||
}
|
||||
|
||||
delete Store.channels[chanId];
|
||||
};
|
||||
Store._removeClient = function (clientId) {
|
||||
var driveIdx = driveEventClients.indexOf(clientId);
|
||||
if (driveIdx !== -1) {
|
||||
driveEventClients.splice(driveIdx, 1);
|
||||
}
|
||||
try {
|
||||
store.onlyoffice?.removeClient(clientId);
|
||||
} catch (e) { console.error(e); }
|
||||
try {
|
||||
store.mailbox?.removeClient(clientId);
|
||||
} catch (e) { console.error(e); }
|
||||
store.onlyoffice?.removeClient?.(clientId);
|
||||
store.mailbox?.removeClient?.(clientId);
|
||||
Object.keys(store.modules).forEach(function (key) {
|
||||
if (!store.modules[key]) { return; }
|
||||
if (!store.modules[key].removeClient) { return; }
|
||||
try {
|
||||
store.modules[key].removeClient(clientId);
|
||||
} catch (e) { console.error(e); }
|
||||
store.modules[key]?.removeClient?.(clientId);
|
||||
});
|
||||
|
||||
Object.keys(Store.channels).forEach(function (chanId) {
|
||||
var chanIdx = Store.channels[chanId].clients.indexOf(clientId);
|
||||
if (chanIdx !== -1) {
|
||||
Store.channels[chanId].clients.splice(chanIdx, 1);
|
||||
}
|
||||
if (Store.channels[chanId].clients.length === 0) {
|
||||
dropChannel(chanId);
|
||||
}
|
||||
});
|
||||
Store.pad?.removeClient?.(clientId);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
var loadProfile = function (waitFor) {
|
||||
store.profile = Profile.init({
|
||||
store: store,
|
||||
updateMetadata: function () {
|
||||
broadcast([], "UPDATE_METADATA");
|
||||
},
|
||||
pinPads: function (data, cb) { Store.pinPads(null, data, cb); },
|
||||
}, waitFor, function (ev, data, clients) {
|
||||
clients.forEach(function (cId) {
|
||||
postMessage(cId, 'PROFILE_EVENT', {
|
||||
ev: ev,
|
||||
data: data
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
*/
|
||||
var loadOnlyOffice = function () {
|
||||
if (store.onlyoffice) { return; }
|
||||
store.onlyoffice = OnlyOffice.init(store, function (ev, data, clients) {
|
||||
@ -2771,7 +2301,7 @@ const factory = (Sortify, UserObject, ProxyManager,
|
||||
loadSharedFolder: loadSharedFolder,
|
||||
settings: proxy.settings,
|
||||
removeOwnedChannel: function (channel, cb) {
|
||||
Store.removeOwnedChannel('', channel, cb);
|
||||
Store.pad.destroy('', channel, cb);
|
||||
},
|
||||
store: store,
|
||||
Store: Store
|
||||
@ -3038,8 +2568,9 @@ const factory = (Sortify, UserObject, ProxyManager,
|
||||
|
||||
setInterval(function () {
|
||||
var clients = [];
|
||||
Object.keys(Store.channels).forEach(function (chanId) {
|
||||
var c = Store.channels[chanId].clients;
|
||||
const channels = Store.pad.getChannels();
|
||||
Object.keys(channels).forEach(function (chanId) {
|
||||
var c = channels[chanId].clients;
|
||||
Array.prototype.push.apply(clients, c);
|
||||
});
|
||||
clients = Util.deduplicateString(clients);
|
||||
@ -3274,7 +2805,7 @@ const factory = (Sortify, UserObject, ProxyManager,
|
||||
startModules(clientId, ret, onInit);
|
||||
});
|
||||
});
|
||||
onJoinedEvt.reg(next);
|
||||
Store.pad.onJoined.reg(next);
|
||||
onPadRejectedEvt.reg(next);
|
||||
});
|
||||
}
|
||||
@ -3478,6 +3009,7 @@ module.exports = factory(
|
||||
require('./components/sharedfolder'),
|
||||
require('./components/account'), // .ts
|
||||
require('./components/drive'), // .ts
|
||||
require('./components/pad'), // .ts
|
||||
require('./modules/cursor'),
|
||||
require('./modules/support'),
|
||||
require('./modules/integration'),
|
||||
|
||||
526
src/worker/components/pad.ts
Normal file
526
src/worker/components/pad.ts
Normal file
@ -0,0 +1,526 @@
|
||||
// SPDX-FileCopyrightText: 2025 XWiki CryptPad Team <contact@cryptpad.org> and contributors
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import nacl from 'tweetnacl/nacl-fast';
|
||||
import { Pad, RpcCall, Callback } from '../types'
|
||||
|
||||
// node modules
|
||||
import * as ChainPad from 'chainpad';
|
||||
import * as Crypto from 'chainpad-crypto';
|
||||
import * as CpNetflux from 'chainpad-netflux';
|
||||
import nThen from 'nthen';
|
||||
import Sortify from 'json.sortify';
|
||||
// custom modules
|
||||
import * as NetConfig from '../../common/network-config.js';
|
||||
import * as Hash from '../../common/common-hash.js';
|
||||
import * as Util from '../../common/common-util.js';
|
||||
import * as Cache from '../../common/cache-store.js';
|
||||
import * as UserObject from '../../common/user-object.js';
|
||||
|
||||
import * as SF from './sharedfolder.js';
|
||||
|
||||
const onJoinedEvt: any = Util.mkEvent(true);
|
||||
|
||||
const _getMetadata: Callback = (ctx, clientId, data, _cb) => {
|
||||
const cb = Util.once(Util.mkAsync(_cb));
|
||||
const { store, Store } = ctx;
|
||||
|
||||
if (store.offline || !store.anon_rpc) {
|
||||
return void cb({ error: 'OFFLINE' });
|
||||
}
|
||||
if (!data.channel) {
|
||||
return void cb({ error: 'ENOTFOUND'});
|
||||
}
|
||||
if (data.channel.length !== 32) {
|
||||
return void cb({ error: 'EINVAL'});
|
||||
}
|
||||
if (!Hash.isValidChannel(data.channel)) {
|
||||
return void cb({ error: 'EINVAL' });
|
||||
}
|
||||
|
||||
store.anon_rpc.send('GET_METADATA', data.channel, (err, obj) => {
|
||||
if (err) { return void cb({error: err}); }
|
||||
const metadata = (obj && obj[0]) || {};
|
||||
cb(metadata);
|
||||
|
||||
// If you don't have access to the metadata, stop here
|
||||
// (we can't update the local data)
|
||||
if (metadata.rejected) { return; }
|
||||
|
||||
// Update owners and expire time in the drive
|
||||
Store.getAllStores().forEach(s => {
|
||||
const allData = s.manager.findChannel(data.channel, true);
|
||||
let changed = false;
|
||||
allData.forEach(obj => {
|
||||
if (Sortify(obj.data.owners) !== Sortify(metadata.owners)) {
|
||||
changed = true;
|
||||
}
|
||||
obj.data.owners = metadata.owners;
|
||||
obj.data.atime = +new Date();
|
||||
if (metadata.expire) {
|
||||
obj.data.expire = +metadata.expire;
|
||||
}
|
||||
});
|
||||
|
||||
// If we had to change the "owners" field,
|
||||
// redraw the drive UI
|
||||
if (!changed) { return; }
|
||||
var send = s.sendEvent || store.sendDriveEvent;
|
||||
send('DRIVE_CHANGE', {
|
||||
path: ['drive', UserObject.FILES_DATA]
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
const _setMetadata: Callback = (ctx, clientId, data, cb) => {
|
||||
if (!data.channel) { return void cb({ error: 'ENOTFOUND'}); }
|
||||
if (!data.command) { return void cb({ error: 'EINVAL' }); }
|
||||
const { Store } = ctx;
|
||||
const s = Store.getStore(data.teamId);
|
||||
if (!s) { return void cb({ error: 'ENOTFOUND' }); }
|
||||
|
||||
const otherChannels = data.channels;
|
||||
delete data.channels;
|
||||
s.rpc.setMetadata(data, (err, res) => {
|
||||
if (err) { return void cb({ error: err }); }
|
||||
if (!Array.isArray(res) || !res.length) { return void cb({}); }
|
||||
cb(res[0]);
|
||||
});
|
||||
// If we have other related channels, send the command for them too
|
||||
if (Array.isArray(otherChannels)) {
|
||||
otherChannels.forEach(chan => {
|
||||
var _d = Util.clone(data);
|
||||
_d.channel = chan;
|
||||
Store.setPadMetadata(clientId, _d, () => {});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const _getVersionHash: Callback = (ctx, clientId, data) => {
|
||||
let validateKey;
|
||||
const { Store, store, postMessage } = ctx;
|
||||
|
||||
const channel = data.channel;
|
||||
const toFind = data.versionHash;
|
||||
|
||||
// create fake history keeper to avoid validate
|
||||
const fakeNetflux = Hash.createChannelId();
|
||||
|
||||
nThen(waitFor => {
|
||||
_getMetadata(ctx, clientId, { channel }, waitFor(md => {
|
||||
if (md && md.rejected) {
|
||||
postMessage(clientId, "PAD_ERROR", {
|
||||
type: "ERESTRICTED"
|
||||
});
|
||||
waitFor.abort();
|
||||
return;
|
||||
}
|
||||
validateKey = md.validateKey;
|
||||
}));
|
||||
}).nThen(() => {
|
||||
Store.getHistoryRange(clientId, {
|
||||
cpCount: 1,
|
||||
channel,
|
||||
lastKnownHash: toFind
|
||||
}, obj => {
|
||||
if (obj && obj.error) {
|
||||
return postMessage(clientId, "PAD_ERROR", obj.error);
|
||||
}
|
||||
const msgs = obj.messages || [];
|
||||
if (msgs[msgs.length - 1]?.serverHash !== toFind) {
|
||||
return postMessage(clientId, "PAD_ERROR", {
|
||||
type: "HASH_NOT_FOUND"
|
||||
});
|
||||
}
|
||||
postMessage(clientId, "PAD_CONNECT", {
|
||||
myID: fakeNetflux,
|
||||
id: channel,
|
||||
members: [fakeNetflux]
|
||||
});
|
||||
(obj.messages || []).forEach(data => {
|
||||
postMessage(clientId, "PAD_MESSAGE", {
|
||||
msg: data.msg,
|
||||
time: data.time,
|
||||
user: fakeNetflux.slice(0,16)
|
||||
});
|
||||
});
|
||||
if (validateKey) {
|
||||
store?.messenger?.storeValidateKey(channel, validateKey);
|
||||
}
|
||||
postMessage(clientId, "PAD_READY");
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const _join: Callback = (ctx, clientId, data) => {
|
||||
if (data.versionHash) {
|
||||
return _getVersionHash(ctx, clientId, data);
|
||||
}
|
||||
|
||||
const { channels, store, Store, myDeletions, postMessage } = ctx;
|
||||
const channelId = data.channel;
|
||||
|
||||
if (!Hash.isValidChannel(channelId)) {
|
||||
return postMessage(clientId, "PAD_ERROR", 'INVALID_CHAN');
|
||||
}
|
||||
|
||||
const isNew = typeof channels[channelId] === "undefined";
|
||||
|
||||
// Create or get existing channel object
|
||||
const channel = channels[channelId] ||= {
|
||||
queue: [],
|
||||
data: {},
|
||||
clients: [],
|
||||
bcast: (cmd, data, notMe) => {
|
||||
channel.clients.forEach(function (cId) {
|
||||
if (cId === notMe) { return; }
|
||||
postMessage(cId, cmd, data);
|
||||
});
|
||||
},
|
||||
history: [],
|
||||
pushHistory: (msg, isCp) => {
|
||||
if (isCp) {
|
||||
// Checkpoint, re-add "cp|" and clear older history
|
||||
channel.history.push('cp|' + msg);
|
||||
let i;
|
||||
for (i = channel.history.length - 101; i > 0; i--) {
|
||||
if (/^cp\|/.test(channel.history[i])) { break; }
|
||||
}
|
||||
channel.history = channel.history.slice(i);
|
||||
return;
|
||||
}
|
||||
channel.history.push(msg);
|
||||
}
|
||||
};
|
||||
|
||||
// Add new client
|
||||
if (channel.clients.indexOf(clientId) === -1) {
|
||||
channel.clients.push(clientId);
|
||||
}
|
||||
|
||||
// Existing pad already loaded: send userlist and history
|
||||
if (!isNew && channel.wc) {
|
||||
postMessage(clientId, "PAD_CONNECT", { // Initialize
|
||||
myID: channel.wc.myID,
|
||||
id: channel.wc.id,
|
||||
members: channel.wc.members
|
||||
});
|
||||
channel.wc.members.forEach(m => { // Userlist
|
||||
postMessage(clientId, "PAD_JOIN", m);
|
||||
});
|
||||
channel.history.forEach(msg => { // History
|
||||
postMessage(clientId, "PAD_MESSAGE", {
|
||||
msg: CpNetflux.removeCp(msg),
|
||||
user: channel.wc.myID,
|
||||
validateKey: channel.data.validateKey
|
||||
});
|
||||
});
|
||||
postMessage(clientId, "PAD_READY"); // Ready
|
||||
return;
|
||||
}
|
||||
|
||||
// chainpad-netflux config
|
||||
const onError = err => {
|
||||
const type = err?.type;
|
||||
// Deletion started from this worker => different UI message
|
||||
if (type === "EDELETED" && myDeletions[channelId]) {
|
||||
delete myDeletions[channelId];
|
||||
err.ownDeletion = true;
|
||||
}
|
||||
channel.bcast("PAD_ERROR", err);
|
||||
|
||||
if (type === "EDELETED" && Cache?.clearChannel) {
|
||||
Cache.clearChannel(channelId);
|
||||
}
|
||||
|
||||
// DELETED, EXPIRED or RESTRICTED pad => leave the channel
|
||||
if (!["EDELETED","EEXPIRED","ERESTRICTED"].includes(type)) {
|
||||
return;
|
||||
}
|
||||
Store.leavePad(null, data, function () {});
|
||||
};
|
||||
const conf = {
|
||||
Cache: store.neverCache ? undefined : Cache,
|
||||
priority: 1,
|
||||
onCacheStart: () => {
|
||||
postMessage(clientId, "PAD_CACHE");
|
||||
},
|
||||
onCacheReady: () => {
|
||||
postMessage(clientId, "PAD_CACHE_READY");
|
||||
},
|
||||
onReady: pad => {
|
||||
const padData = pad.metadata || {};
|
||||
channel.data = padData;
|
||||
if (padData?.validateKey && store.messenger) {
|
||||
store.messenger.storeValidateKey(channelId, padData.validateKey);
|
||||
}
|
||||
postMessage(clientId, "PAD_READY", pad.noCache);
|
||||
},
|
||||
onMessage: function (m, user, validateKey, isCp, hash) {
|
||||
channel.lastHash = hash;
|
||||
channel.pushHistory(m, isCp);
|
||||
channel.bcast("PAD_MESSAGE", {
|
||||
user: user,
|
||||
msg: m,
|
||||
validateKey: validateKey
|
||||
});
|
||||
},
|
||||
onJoin: function (m) {
|
||||
channel.bcast("PAD_JOIN", m);
|
||||
},
|
||||
onLeave: function (m) {
|
||||
channel.bcast("PAD_LEAVE", m);
|
||||
},
|
||||
onError: onError,
|
||||
onChannelError: onError,
|
||||
onRejected: Store.onRejected,
|
||||
onConnectionChange: info => {
|
||||
if (!info.state) {
|
||||
channel.bcast("PAD_DISCONNECT");
|
||||
}
|
||||
},
|
||||
onMetadataUpdate: metadata => {
|
||||
channel.data = metadata || {};
|
||||
Store.getAllStores().forEach(s => {
|
||||
let allData = s.manager.findChannel(channelId, true);
|
||||
allData.forEach(obj => {
|
||||
obj.data.owners = metadata.owners;
|
||||
obj.data.atime = +new Date();
|
||||
if (metadata.expire) {
|
||||
obj.data.expire = +metadata.expire;
|
||||
}
|
||||
});
|
||||
const send = s.sendEvent || store.sendDriveEvent;
|
||||
send('DRIVE_CHANGE', {
|
||||
path: ['drive', UserObject.FILES_DATA]
|
||||
});
|
||||
});
|
||||
channel.bcast("PAD_METADATA", metadata);
|
||||
},
|
||||
crypto: {
|
||||
// Encryption and decryption is done in the outer window
|
||||
// Async-store only deals with encrypted messages
|
||||
encrypt: function (m) { return m; },
|
||||
decrypt: function (m) { return m; }
|
||||
},
|
||||
noChainPad: true,
|
||||
channel: channelId,
|
||||
metadata: data.metadata,
|
||||
network: store.network || store.networkPromise,
|
||||
websocketURL: NetConfig.getWebsocketURL(),
|
||||
onInit: function () {
|
||||
onJoinedEvt.fire();
|
||||
},
|
||||
//readOnly: data.readOnly,
|
||||
onConnect: (wc, sendMessage) => {
|
||||
channel.sendMessage = (msg, cId, cb) => {
|
||||
// Send to server
|
||||
sendMessage(msg, err => {
|
||||
if (err) { return void cb({ error: err }); }
|
||||
// Broadcast to other tabs
|
||||
channel.lastHash = msg.slice(0,64);
|
||||
channel.pushHistory(CpNetflux.removeCp(msg), /^cp\|/.test(msg));
|
||||
channel.bcast("PAD_MESSAGE", {
|
||||
user: wc.myID,
|
||||
msg: CpNetflux.removeCp(msg),
|
||||
validateKey: channel.data.validateKey
|
||||
}, cId);
|
||||
cb();
|
||||
});
|
||||
};
|
||||
channel.wc = wc;
|
||||
channel.queue.forEach(function (data) {
|
||||
channel.sendMessage(data.message, clientId);
|
||||
});
|
||||
channel.queue = [];
|
||||
channel.bcast("PAD_CONNECT", {
|
||||
myID: wc.myID,
|
||||
id: wc.id,
|
||||
members: wc.members
|
||||
});
|
||||
}
|
||||
};
|
||||
channel.cpNf = CpNetflux.start(conf);
|
||||
};
|
||||
|
||||
// Send a message to a pad we already joined
|
||||
const _sendMessage: Callback = (ctx, clientId, data, cb) => {
|
||||
var msg = data.msg;
|
||||
var channel = ctx.channels[data.channel];
|
||||
if (!channel) { return; }
|
||||
if (!channel.wc) {
|
||||
channel.queue.push(msg);
|
||||
return void cb();
|
||||
}
|
||||
channel.sendMessage(msg, clientId, cb);
|
||||
};
|
||||
|
||||
const _onCorruptedCache: Callback = (ctx, clientId, channel) => {
|
||||
var chan = ctx.channels[channel];
|
||||
if (!chan || !chan.cpNf) { return; }
|
||||
Cache.clearChannel(channel);
|
||||
if (!chan.cpNf.resetCache) { return; }
|
||||
chan.cpNf.resetCache();
|
||||
};
|
||||
|
||||
const _getLastHash: Callback = (ctx, clientId, data, cb) => {
|
||||
var chan = ctx.channels[data.channel];
|
||||
if (!chan) { return void cb({error: 'ENOCHAN'}); }
|
||||
if (!chan.lastHash) { return void cb({error: 'EINVAL'}); }
|
||||
cb({
|
||||
hash: chan.lastHash
|
||||
});
|
||||
};
|
||||
|
||||
const _destroy: Callback = (ctx, clientId, data, cb) => {
|
||||
const { store, Store, channels, myDeletions } = ctx;
|
||||
|
||||
// "data" used to be a channelId, now it can also be an object
|
||||
// data.force tells us we can safely remove the drive ID
|
||||
let channel = data;
|
||||
let force = false;
|
||||
let teamId, reason;
|
||||
if (data && typeof(data) === "object") {
|
||||
({ channel, force, teamId, reason } = data);
|
||||
}
|
||||
|
||||
if (channel === store.driveChannel && !force) {
|
||||
return void cb({error: 'User drive removal blocked!'});
|
||||
}
|
||||
|
||||
const s = Store.getStore(teamId);
|
||||
if (!s) { return void cb({ error: 'ENOTFOUND' }); }
|
||||
if (!s.rpc) { return void cb({error: 'RPC_NOT_READY'}); }
|
||||
|
||||
// If this channel is loaded, store that we deleted it ourselves
|
||||
if (channels[channel]) { myDeletions[channel] = true; }
|
||||
|
||||
s.rpc.removeOwnedChannel(channel, err => {
|
||||
if (err) { delete myDeletions[channel]; }
|
||||
cb({error:err});
|
||||
}, reason);
|
||||
};
|
||||
|
||||
const alwaysOnline = (ctx, chanId) => {
|
||||
const { store } = ctx;
|
||||
if (!store) { return false; }
|
||||
// Drive
|
||||
if (store.driveChannel === chanId) { return true; }
|
||||
// Shared folders
|
||||
if (SF.isSharedFolderChannel(chanId)) { return true; }
|
||||
// Teams
|
||||
if (Util.find(store, ['proxy', 'teams'])) {
|
||||
var t = Util.find(store, ['proxy', 'teams']) || {};
|
||||
return Object.keys(t).some(id => {
|
||||
return t[id].channel === chanId;
|
||||
});
|
||||
}
|
||||
// Profile
|
||||
if (Util.find(store, ['proxy', 'profile', 'href'])) {
|
||||
let href = Util.find(store, ['proxy', 'profile', 'href']);
|
||||
return Hash.hrefToHexChannelId(href) === chanId;
|
||||
}
|
||||
};
|
||||
const dropChannel = (ctx, chanId) => {
|
||||
const store = ctx.store;
|
||||
store.messenger?.leavePad?.(chanId);
|
||||
store.onlyoffice?.leavePad?.(chanId);
|
||||
Object.keys(store.modules).forEach(key => {
|
||||
store.modules[key]?.leavePad?.(chanId);
|
||||
});
|
||||
|
||||
if (!alwaysOnline(ctx, chanId)) {
|
||||
try {
|
||||
Cache.leaveChannel(chanId);
|
||||
} catch (e) { console.error(e); }
|
||||
ctx.channels[chanId]?.cpNf?.stop();
|
||||
}
|
||||
delete ctx.channels[chanId];
|
||||
};
|
||||
|
||||
const init = (config) => {
|
||||
const { broadcast, postMessage, store, Store } = config;
|
||||
|
||||
/** XXX
|
||||
* Variables
|
||||
* Store.channels, store.channels
|
||||
*
|
||||
* Methods
|
||||
* sendPadMsg, corruptedCache, getLastHash
|
||||
**/
|
||||
|
||||
const ctx = {
|
||||
channels: [],
|
||||
postMessage,
|
||||
store,
|
||||
Store,
|
||||
myDeletions: []
|
||||
};
|
||||
|
||||
const join: RpcCall = (clientId, data, cb) => {
|
||||
_join(ctx, clientId, data, cb);
|
||||
};
|
||||
const destroy: RpcCall = (clientId, data, cb) => {
|
||||
_destroy(ctx, clientId, data, cb);
|
||||
};
|
||||
const setMetadata: RpcCall = (clientId, data, cb) => {
|
||||
_setMetadata(ctx, clientId, data, cb);
|
||||
};
|
||||
const getMetadata: RpcCall = (clientId, data, cb) => {
|
||||
_getMetadata(ctx, clientId, data, cb);
|
||||
};
|
||||
const sendMessage: RpcCall = (clientId, data, cb) => {
|
||||
_sendMessage(ctx, clientId, data, cb);
|
||||
};
|
||||
const onCorruptedCache: RpcCall = (clientId, data, cb) => {
|
||||
_onCorruptedCache(ctx, clientId, data, cb);
|
||||
};
|
||||
const getLastHash: RpcCall = (clientId, data, cb) => {
|
||||
_getLastHash(ctx, clientId, data, cb);
|
||||
};
|
||||
|
||||
const removeClient = clientId => {
|
||||
Object.keys(ctx.channels).forEach(chanId => {
|
||||
let idx = ctx.channels[chanId].clients.indexOf(clientId);
|
||||
if (idx !== -1) {
|
||||
ctx.channels[chanId].clients.splice(idx, 1);
|
||||
}
|
||||
if (ctx.channels[chanId].clients.length === 0) {
|
||||
dropChannel(ctx, chanId);
|
||||
}
|
||||
});
|
||||
};
|
||||
const leave: RpcCall = (clientId, data, cb) => {
|
||||
const channel = ctx.channels[data.channel];
|
||||
if (!channel?.cpNf) { return void cb ({error: 'EINVAL'}); }
|
||||
dropChannel(ctx, data.channel);
|
||||
cb();
|
||||
};
|
||||
|
||||
const getChannels = () => {
|
||||
return ctx.channels;
|
||||
};
|
||||
|
||||
return {
|
||||
join,
|
||||
destroy,
|
||||
setMetadata,
|
||||
getMetadata,
|
||||
sendMessage,
|
||||
onCorruptedCache,
|
||||
getLastHash,
|
||||
leave,
|
||||
removeClient,
|
||||
onJoined: onJoinedEvt.reg,
|
||||
getChannels
|
||||
};
|
||||
};
|
||||
|
||||
const Pad: Pad = {
|
||||
init: init
|
||||
};
|
||||
|
||||
export { Pad }
|
||||
|
||||
|
||||
@ -20,7 +20,6 @@ const factory = AStore => {
|
||||
UPDATE_PIN_LIMIT: Store.updatePinLimit,
|
||||
GET_PIN_LIMIT: Store.getPinLimit,
|
||||
CLEAR_OWNED_CHANNEL: Store.clearOwnedChannel,
|
||||
REMOVE_OWNED_CHANNEL: Store.removeOwnedChannel,
|
||||
UPLOAD_CHUNK: Store.uploadChunk,
|
||||
UPLOAD_COMPLETE: Store.uploadComplete,
|
||||
UPLOAD_STATUS: Store.uploadStatus,
|
||||
@ -73,9 +72,12 @@ const factory = AStore => {
|
||||
// Universal
|
||||
UNIVERSAL_COMMAND: Store.universal.execCommand,
|
||||
// Pad
|
||||
SEND_PAD_MSG: Store.sendPadMsg,
|
||||
JOIN_PAD: Store.joinPad,
|
||||
LEAVE_PAD: Store.leavePad,
|
||||
SEND_PAD_MSG: Store.pad.sendMessage,
|
||||
JOIN_PAD: Store.pad.join,
|
||||
LEAVE_PAD: Store.pad.leave,
|
||||
REMOVE_OWNED_CHANNEL: Store.pad.destroy,
|
||||
CORRUPTED_CACHE: Store.pad.onCorruptedCache,
|
||||
GET_LAST_HASH: Store.pad.getLastHash,
|
||||
GET_FULL_HISTORY: Store.getFullHistory,
|
||||
GET_HISTORY: Store.getHistory,
|
||||
GET_HISTORY_RANGE: Store.getHistoryRange,
|
||||
@ -83,12 +85,10 @@ const factory = AStore => {
|
||||
CONTACT_PAD_OWNER: Store.contactPadOwner,
|
||||
GIVE_PAD_ACCESS: Store.givePadAccess,
|
||||
BURN_PAD: Store.burnPad,
|
||||
GET_PAD_METADATA: Store.getPadMetadata,
|
||||
SET_PAD_METADATA: Store.setPadMetadata,
|
||||
GET_PAD_METADATA: Store.pad?.getMetadata,
|
||||
SET_PAD_METADATA: Store.pad?.setMetadata,
|
||||
CHANGE_PAD_PASSWORD_PIN: Store.changePadPasswordPin,
|
||||
GET_LAST_HASH: Store.getLastHash,
|
||||
GET_SNAPSHOT: Store.getSnapshot,
|
||||
CORRUPTED_CACHE: Store.corruptedCache,
|
||||
DELETE_MAILBOX_MESSAGE: Store.deleteMailboxMessage,
|
||||
// Drive
|
||||
DRIVE_USEROBJECT: Store.userObjectCommand,
|
||||
|
||||
@ -132,7 +132,7 @@ const factory = (Util, Hash, UserObject, nThen) => {
|
||||
history = messages.join('\n').length;
|
||||
}), true);
|
||||
// Metadata
|
||||
Store.getPadMetadata(null, {
|
||||
Store.pad.getMetadata(null, {
|
||||
channel: channel
|
||||
}, waitFor(function (obj) {
|
||||
if (obj && obj.error) { return; }
|
||||
|
||||
@ -297,7 +297,7 @@ const factory = (Util, Hash, Constants, Realtime, ProxyManager,
|
||||
teamId: id
|
||||
};
|
||||
}
|
||||
ctx.Store.removeOwnedChannel('', data, cb);
|
||||
ctx.Store.pad.destroy('', data, cb);
|
||||
},
|
||||
Store: ctx.Store,
|
||||
store: ctx.store
|
||||
@ -879,7 +879,7 @@ const factory = (Util, Hash, Constants, Realtime, ProxyManager,
|
||||
}));
|
||||
}).nThen(function (_w) {
|
||||
if (otherOwners) {
|
||||
ctx.Store.setPadMetadata(null, {
|
||||
ctx.Store.pad.setMetadata(null, {
|
||||
channel: c,
|
||||
command: 'RM_OWNERS',
|
||||
value: [teamEdPublic],
|
||||
@ -983,7 +983,7 @@ const factory = (Util, Hash, Constants, Realtime, ProxyManager,
|
||||
var md;
|
||||
nThen(function (waitFor) {
|
||||
// Get pending owners
|
||||
ctx.Store.getPadMetadata(null, {
|
||||
ctx.Store.pad.getMetadata(null, {
|
||||
channel: teamData.channel
|
||||
}, waitFor(function (obj) {
|
||||
if (obj && obj.error) {
|
||||
@ -1006,7 +1006,7 @@ const factory = (Util, Hash, Constants, Realtime, ProxyManager,
|
||||
});
|
||||
if (!member && teamData.owner) {
|
||||
var removeOwnership = function (chan) {
|
||||
ctx.Store.setPadMetadata(null, {
|
||||
ctx.Store.pad.setMetadata(null, {
|
||||
channel: chan,
|
||||
command: 'RM_PENDING_OWNERS',
|
||||
value: [ed],
|
||||
@ -1119,7 +1119,7 @@ const factory = (Util, Hash, Constants, Realtime, ProxyManager,
|
||||
}
|
||||
};
|
||||
var addPendingOwner = function (chan) {
|
||||
ctx.Store.setPadMetadata(null, {
|
||||
ctx.Store.pad.setMetadata(null, {
|
||||
channel: chan,
|
||||
command: 'ADD_PENDING_OWNERS',
|
||||
value: [user.edPublic],
|
||||
@ -1175,7 +1175,7 @@ const factory = (Util, Hash, Constants, Realtime, ProxyManager,
|
||||
}
|
||||
};
|
||||
var removeOwnership = function (chan) {
|
||||
ctx.Store.setPadMetadata(null, {
|
||||
ctx.Store.pad.setMetadata(null, {
|
||||
channel: chan,
|
||||
command: cmd,
|
||||
value: [user.edPublic],
|
||||
@ -1392,7 +1392,7 @@ const factory = (Util, Hash, Constants, Realtime, ProxyManager,
|
||||
var md;
|
||||
nThen(function (waitFor) {
|
||||
// Get pending owners
|
||||
ctx.Store.getPadMetadata(null, {
|
||||
ctx.Store.pad.getMetadata(null, {
|
||||
channel: teamData.channel
|
||||
}, waitFor(function (obj) {
|
||||
if (obj && obj.error) {
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
// Default CryptPad worker module, extended with
|
||||
// specific methods for each module
|
||||
export type Callback = (...args: any[]) => void
|
||||
export type RpcCall = (clientId: string, data: any, cb: Callback) => void;
|
||||
|
||||
export type ModuleConfig = {
|
||||
store: any,
|
||||
@ -66,3 +67,26 @@ export interface DriveObject {
|
||||
export interface Drive {
|
||||
init: (config: DriveConfig) => DriveObject
|
||||
}
|
||||
|
||||
export type PadConfig = {
|
||||
Store: any,
|
||||
store: any,
|
||||
broadcast: (exclude: object, cmd: string, data?: any, cb?: any) => void,
|
||||
postMessage: (clientId: string, cmd: string, data?: any, cb?: any) => void
|
||||
}
|
||||
export interface PadObject {
|
||||
join: RpcCall,
|
||||
destroy: RpcCall,
|
||||
setMetadata: RpcCall,
|
||||
getMetadata: RpcCall,
|
||||
leave: RpcCall,
|
||||
removeClient: (clientId: string) => void,
|
||||
sendMessage: RpcCall,
|
||||
getLastHash: RpcCall,
|
||||
onCorruptedCache: RpcCall,
|
||||
getChannels: () => string[],
|
||||
onJoined: any,
|
||||
}
|
||||
export interface Pad {
|
||||
init: (config: PadConfig) => PadObject
|
||||
}
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
|
||||
(() => {
|
||||
const factory = function () {
|
||||
let USE_MIN = true;
|
||||
let USE_MIN = false;
|
||||
|
||||
let path = '/common/worker.bundle.js?';
|
||||
if (USE_MIN) { path = '/common/worker.bundle.min.js?'; }
|
||||
|
||||
2
www/common/worker.bundle.min.js
vendored
2
www/common/worker.bundle.min.js
vendored
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user