cryptpad/www/common/outer/cache-store.js
2021-08-18 15:07:23 +05:30

190 lines
6 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

define([
'/common/common-util.js',
'/bower_components/localforage/dist/localforage.min.js',
], function (Util, localForage) {
var S = window.CryptPad_Cache = {};
var onReady = Util.mkEvent(true);
// Check if indexedDB is allowed
var allowed = false;
var disabled = false;
var supported = false;
try {
var request = window.indexedDB.open('test_db', 1);
request.onsuccess = function () {
supported = true;
allowed = supported && !disabled;
onReady.fire();
};
request.onerror = function () {
onReady.fire();
};
} catch (e) {
onReady.fire();
}
S.enable = function () {
disabled = false;
allowed = supported && !disabled;
};
S.disable = function () {
disabled = true;
allowed = supported && !disabled;
};
var cache = localForage.createInstance({
driver: localForage.INDEXEDDB,
name: "cp_cache"
});
S.getBlobCache = function (id, cb) {
cb = Util.once(Util.mkAsync(cb || function () {}));
onReady.reg(function () {
if (!allowed) { return void cb('NOCACHE'); }
cache.getItem(id, function (err, obj) {
if (err || !obj || !obj.c) {
return void cb(Util.serializeError(err || 'EINVAL'));
}
cb(null, obj.c);
obj.t = +new Date();
cache.setItem(id, obj, function (err) {
if (!err) { return; }
console.error(err);
});
});
});
};
S.setBlobCache = function (id, u8, cb) {
cb = Util.once(Util.mkAsync(cb || function () {}));
onReady.reg(function () {
if (!allowed) { return void cb('NOCACHE'); }
if (!u8) { return void cb('EINVAL'); }
cache.setItem(id, {
c: u8,
t: (+new Date()) // 't' represent the "lastAccess" of this cache (get or set)
}, function (err) {
cb(Util.serializeError(err));
});
});
};
// id: channel ID or blob ID
// returns array of messages
S.getChannelCache = function (id, cb) {
cb = Util.once(Util.mkAsync(cb || function () {}));
onReady.reg(function () {
if (!allowed) { return void cb('NOCACHE'); }
cache.getItem(id, function (err, obj) {
if (err || !obj || !Array.isArray(obj.c)) {
return void cb(Util.serializeError(err || 'EINVAL'));
}
cb(null, obj);
obj.t = +new Date();
cache.setItem(id, obj, function (err) {
if (!err) { return; }
console.error(err);
});
});
});
};
// Keep the last two checkpoint + any checkpoint that may exist in the last 100 messages
// FIXME: duplicate system with sliceCpIndex from lib/hk-util.js
var checkCheckpoints = function (array) {
if (!Array.isArray(array)) { return; }
// Keep the last 100 messages
if (array.length > 100) { // FIXME this behaviour is only valid for chainpad-style documents
array.splice(0, array.length - 100);
}
// Remove every message before the first checkpoint
var firstCpIdx;
array.some(function (el, i) {
if (!el.isCheckpoint) { return; }
firstCpIdx = i;
return true;
});
array.splice(0, firstCpIdx);
};
var t = {};
S.storeCache = function (id, validateKey, val, onError) {
onError = Util.once(Util.mkAsync(onError || function () {}));
onReady.reg(function () {
// Make a throttle or use the existing one to avoid calling
// storeCache with the same array multiple times
t[id] = t[id] || Util.throttle(function (validateKey, val, onError) {
if (!allowed) { return void onError('NOCACHE'); }
if (!Array.isArray(val) || !validateKey) { return void onError('EINVAL'); }
checkCheckpoints(val);
cache.setItem(id, {
k: validateKey,
c: val,
t: (+new Date()) // 't' represent the "lastAccess" of this cache (get or set)
}, function (err) {
if (err) { onError(Util.serializeError(err)); }
});
}, 50);
t[id](validateKey, val, onError);
});
};
S.leaveChannel = function (id) {
delete t[id];
};
S.clearChannel = function (id, cb) {
cb = Util.once(Util.mkAsync(cb || function () {}));
onReady.reg(function () {
if (!allowed) { return void cb('NOCACHE'); }
cache.removeItem(id, function () {
cb();
});
});
};
S.clear = function (cb) {
cb = Util.once(Util.mkAsync(cb || function () {}));
onReady.reg(function () {
if (!allowed) { return void cb('NOCACHE'); }
cache.clear(cb);
});
};
S.getKeys = function (cb) {
cb = Util.once(Util.mkAsync(cb || function () {}));
onReady.reg(function () {
if (!allowed) { return void cb('NOCACHE'); }
cache.keys().then(function (keys) {
cb(null, keys);
}).catch(function (err) {
cb(err);
});
});
};
S.getTime = function (id, cb) {
cb = Util.once(Util.mkAsync(cb || function () {}));
onReady.reg(function () {
if (!allowed) { return void cb('NOCACHE'); }
cache.getItem(id, function (err, obj) {
if (err || !obj || !obj.c) {
return void cb(Util.serializeError(err || 'EINVAL'));
}
cb(null, obj.t);
});
});
};
self.CryptPad_clearIndexedDB = S.clear;
return S;
});