Manage expired channels

pull/1/head
yflory 7 years ago
parent 8f7489576f
commit 728a6a868d

@ -31,12 +31,15 @@ define(function () {
out.wrongApp = "Impossible d'afficher le contenu de ce document temps-réel dans votre navigateur. Vous pouvez essayer de recharger la page."; out.wrongApp = "Impossible d'afficher le contenu de ce document temps-réel dans votre navigateur. Vous pouvez essayer de recharger la page.";
out.padNotPinned = 'Ce pad va expirer dans 3 mois, {0}connectez-vous{1} ou {2}enregistrez-vous{3} pour le préserver.'; out.padNotPinned = 'Ce pad va expirer dans 3 mois, {0}connectez-vous{1} ou {2}enregistrez-vous{3} pour le préserver.';
out.anonymousStoreDisabled = "L'administrateur de cette instance de CryptPad a désactivé le drive pour les utilisateurs non enregistrés. Vous devez vous connecter pour pouvoir utiliser CryptDrive."; out.anonymousStoreDisabled = "L'administrateur de cette instance de CryptPad a désactivé le drive pour les utilisateurs non enregistrés. Vous devez vous connecter pour pouvoir utiliser CryptDrive.";
out.expiredError = "Ce pad a atteint sa date d'expiration est n'est donc plus disponible.";
out.expiredErrorCopy = ' Vous pouvez toujours copier son contenu ailleurs en appuyant sur <em>Échap</em>.<br> Dés que vous aurez quitté la page, il sera impossible de le récupérer.';
out.loading = "Chargement..."; out.loading = "Chargement...";
out.error = "Erreur"; out.error = "Erreur";
out.saved = "Enregistré"; out.saved = "Enregistré";
out.synced = "Tout est enregistré"; out.synced = "Tout est enregistré";
out.deleted = "Pad supprimé de votre CryptDrive"; out.deleted = "Pad supprimé de votre CryptDrive";
out.deletedFromServer = "Pad supprimé du serveur";
out.realtime_unrecoverableError = "Le moteur temps-réel a rencontré une erreur critique. Cliquez sur OK pour recharger la page."; out.realtime_unrecoverableError = "Le moteur temps-réel a rencontré une erreur critique. Cliquez sur OK pour recharger la page.";

@ -32,12 +32,15 @@ define(function () {
out.wrongApp = "Unable to display the content of that realtime session in your browser. Please try to reload that page."; out.wrongApp = "Unable to display the content of that realtime session in your browser. Please try to reload that page.";
out.padNotPinned = 'This pad will expire in 3 months, {0}login{1} or {2}register{3} to preserve it.'; out.padNotPinned = 'This pad will expire in 3 months, {0}login{1} or {2}register{3} to preserve it.';
out.anonymousStoreDisabled = "The webmaster of this CryptPad instance has disabled the store for anonymous users. You have to log in to be able to use CryptDrive."; out.anonymousStoreDisabled = "The webmaster of this CryptPad instance has disabled the store for anonymous users. You have to log in to be able to use CryptDrive.";
out.expiredError = 'This pad has reached its expiration time and is no longer available.';
out.expiredErrorCopy = ' You can still copy the content to another location by pressing <em>Esc</em>.<br>Once you leave this page, it will disappear forever!';
out.loading = "Loading..."; out.loading = "Loading...";
out.error = "Error"; out.error = "Error";
out.saved = "Saved"; out.saved = "Saved";
out.synced = "Everything is saved"; out.synced = "Everything is saved";
out.deleted = "Pad deleted from your CryptDrive"; out.deleted = "Pad deleted from your CryptDrive";
out.deletedFromServer = "Pad deleted from the server";
out.realtime_unrecoverableError = "The realtime engine has encountered an unrecoverable error. Click OK to reload."; out.realtime_unrecoverableError = "The realtime engine has encountered an unrecoverable error. Click OK to reload.";

@ -2,12 +2,21 @@ var Fs = require("fs");
var Path = require("path"); var Path = require("path");
var nThen = require("nthen"); var nThen = require("nthen");
var config = require("./config");
var config;
try {
config = require('./config');
} catch (e) {
console.log("You can customize the configuration by copying config.example.js to config.js");
config = require('./config.example');
}
var FileStorage = require(config.storage || './storage/file');
var root = Path.resolve(config.taskPath || './tasks'); var root = Path.resolve(config.taskPath || './tasks');
var dirs; var dirs;
var nt; var nt;
var store;
var queue = function (f) { var queue = function (f) {
nt = nt.nThen(f); nt = nt.nThen(f);
@ -41,17 +50,17 @@ var handleTask = function (str, path, cb) {
return cb(); return cb();
} }
nThen(function () { nThen(function (waitFor) {
switch (command) { switch (command) {
case 'EXPIRE': case 'EXPIRE':
console.log("expiring: %s", args[0]); console.log("expiring: %s", args[0]);
// TODO actually remove the file... store.removeChannel(args[0], waitFor());
break; break;
default: default:
console.log("unknown command", command); console.log("unknown command", command);
} }
}).nThen(function () { }).nThen(function () {
// remove the file... // remove the task file...
Fs.unlink(path, function (err) { Fs.unlink(path, function (err) {
if (err) { console.error(err); } if (err) { console.error(err); }
cb(); cb();
@ -64,6 +73,10 @@ nt = nThen(function (w) {
if (e) { throw e; } if (e) { throw e; }
dirs = list; dirs = list;
})); }));
}).nThen(function (waitFor) {
FileStorage.create(config, waitFor(function (_store) {
store = _store;
}));
}).nThen(function () { }).nThen(function () {
dirs.forEach(function (dir) { dirs.forEach(function (dir) {
queue(function (w) { queue(function (w) {

@ -601,11 +601,20 @@ define([
}, 3750); }, 3750);
// jquery.fadeout can get stuck // jquery.fadeout can get stuck
}; };
UI.errorLoadingScreen = function (error, transparent) { UI.errorLoadingScreen = function (error, transparent, exitable) {
if (!$('#' + LOADING).is(':visible')) { UI.addLoadingScreen({hideTips: true}); } if (!$('#' + LOADING).is(':visible') || $('#' + LOADING).hasClass('cp-loading-hidden')) {
UI.addLoadingScreen({hideTips: true});
}
$('.cp-loading-spinner-container').hide(); $('.cp-loading-spinner-container').hide();
$('#cp-loading-tip').remove();
if (transparent) { $('#' + LOADING).css('opacity', 0.8); } if (transparent) { $('#' + LOADING).css('opacity', 0.8); }
$('#' + LOADING).find('p').html(error || Messages.error); $('#' + LOADING).find('p').html(error || Messages.error);
if (exitable) {
$(window).focus();
$(window).keydown(function (e) {
if (e.which === 27) { $('#' + LOADING).hide(); }
});
}
}; };
var $defaultIcon = $('<span>', {"class": "fa fa-file-text-o"}); var $defaultIcon = $('<span>', {"class": "fa fa-file-text-o"});

@ -538,6 +538,7 @@ define([
pad.onJoinEvent = Util.mkEvent(); pad.onJoinEvent = Util.mkEvent();
pad.onLeaveEvent = Util.mkEvent(); pad.onLeaveEvent = Util.mkEvent();
pad.onDisconnectEvent = Util.mkEvent(); pad.onDisconnectEvent = Util.mkEvent();
pad.onErrorEvent = Util.mkEvent();
common.getFullHistory = function (data, cb) { common.getFullHistory = function (data, cb) {
postMessage("GET_FULL_HISTORY", data, cb); postMessage("GET_FULL_HISTORY", data, cb);
@ -679,6 +680,9 @@ define([
case 'PAD_DISCONNECT': { case 'PAD_DISCONNECT': {
common.padRpc.onDisconnectEvent.fire(data); break; common.padRpc.onDisconnectEvent.fire(data); break;
} }
case 'PAD_ERROR': {
common.padRpc.onErrorEvent.fire(data); break;
}
// Drive // Drive
case 'DRIVE_LOG': { case 'DRIVE_LOG': {
common.drive.onLog.fire(data); break; common.drive.onLog.fire(data); break;

@ -115,6 +115,12 @@ define(['json.sortify'], function (Sortify) {
if (!meta.user) { return; } if (!meta.user) { return; }
change(true); change(true);
}); });
sframeChan.on('EV_RT_ERROR', function (err) {
if (err.type !== 'EEXPIRED' && err.type !== 'EDELETED') { return; }
members = [];
if (!meta.user) { return; }
change(true);
});
return Object.freeze({ return Object.freeze({
updateMetadata: function (m) { updateMetadata: function (m) {

@ -815,6 +815,9 @@ define([
onDisconnect: function () { onDisconnect: function () {
postMessage("PAD_DISCONNECT"); postMessage("PAD_DISCONNECT");
}, // post EV_PAD_DISCONNECT }, // post EV_PAD_DISCONNECT
onError: function (err) {
postMessage("PAD_ERROR", err);
}, // post EV_PAD_ERROR
channel: data.channel, channel: data.channel,
validateKey: data.validateKey, validateKey: data.validateKey,
owners: data.owners, owners: data.owners,

@ -33,6 +33,7 @@ define([], function () {
var onLeave = conf.onLeave; var onLeave = conf.onLeave;
var onReady = conf.onReady; var onReady = conf.onReady;
var onDisconnect = conf.onDisconnect; var onDisconnect = conf.onDisconnect;
var onError = conf.onError;
var owners = conf.owners; var owners = conf.owners;
var password = conf.password; var password = conf.password;
var expire = conf.expire; var expire = conf.expire;
@ -44,6 +45,17 @@ define([], function () {
var messageFromOuter = function () {}; var messageFromOuter = function () {};
var error = function (err, wc) {
if (onError) {
onError({
type: err,
loaded: !initializing
});
if (wc && (err === "EEXPIRED" || err === "EDELETED")) { wc.leave(); }
}
else { console.error(err); }
};
var onRdy = function (padData) { var onRdy = function (padData) {
// Trigger onReady only if not ready yet. This is important because the history keeper sends a direct // Trigger onReady only if not ready yet. This is important because the history keeper sends a direct
// message through "network" when it is synced, and it triggers onReady for each channel joined. // message through "network" when it is synced, and it triggers onReady for each channel joined.
@ -96,11 +108,17 @@ define([], function () {
if (peer === hk) { if (peer === hk) {
// if the peer is the 'history keeper', extract their message // if the peer is the 'history keeper', extract their message
var parsed1 = JSON.parse(msg); var parsed1 = JSON.parse(msg);
// First check if it is an error message (EXPIRED/DELETED)
if (parsed1.channel === wc.id && parsed1.error) {
return void error(parsed1.error, wc);
}
msg = parsed1[4]; msg = parsed1[4];
// Check that this is a message for our channel // Check that this is a message for our channel
if (parsed1[3] !== wc.id) { return; } if (parsed1[3] !== wc.id) { return; }
} }
lastKnownHash = msg.slice(0,64); lastKnownHash = msg.slice(0,64);
var message = msgIn(peer, msg); var message = msgIn(peer, msg);
@ -177,7 +195,12 @@ define([], function () {
}; };
var msg = ['GET_HISTORY', wc.id, cfg]; var msg = ['GET_HISTORY', wc.id, cfg];
// Add the validateKey if we are the channel creator and we have a validateKey // Add the validateKey if we are the channel creator and we have a validateKey
if (hk) { network.sendto(hk, JSON.stringify(msg)); } if (hk) {
network.sendto(hk, JSON.stringify(msg)).then(function () {
}, function (err) {
console.error(err);
});
}
} else { } else {
onRdy(); onRdy();
} }
@ -204,8 +227,8 @@ define([], function () {
// join the netflux network, promise to handle opening of the channel // join the netflux network, promise to handle opening of the channel
network.join(channel || null).then(function(wc) { network.join(channel || null).then(function(wc) {
onOpen(wc, network, firstConnection); onOpen(wc, network, firstConnection);
}, function(error) { }, function(err) {
console.error(error); console.error(err);
}); });
}; };

@ -43,6 +43,7 @@ define([
var STATE = Object.freeze({ var STATE = Object.freeze({
DISCONNECTED: 'DISCONNECTED', DISCONNECTED: 'DISCONNECTED',
FORGOTTEN: 'FORGOTTEN', FORGOTTEN: 'FORGOTTEN',
DELETED: 'DELETED',
INFINITE_SPINNER: 'INFINITE_SPINNER', INFINITE_SPINNER: 'INFINITE_SPINNER',
INITIALIZING: 'INITIALIZING', INITIALIZING: 'INITIALIZING',
HISTORY_MODE: 'HISTORY_MODE', HISTORY_MODE: 'HISTORY_MODE',
@ -119,8 +120,9 @@ define([
var stateChange = function (newState) { var stateChange = function (newState) {
var wasEditable = (state === STATE.READY); var wasEditable = (state === STATE.READY);
if (state === STATE.DELETED) { return; }
if (state === STATE.INFINITE_SPINNER && newState !== STATE.READY) { return; } if (state === STATE.INFINITE_SPINNER && newState !== STATE.READY) { return; }
if (newState === STATE.INFINITE_SPINNER) { if (newState === STATE.INFINITE_SPINNER || newState === STATE.DELETED) {
state = newState; state = newState;
} else if (state === STATE.DISCONNECTED && newState !== STATE.INITIALIZING) { } else if (state === STATE.DISCONNECTED && newState !== STATE.INITIALIZING) {
throw new Error("Cannot transition from DISCONNECTED to " + newState); throw new Error("Cannot transition from DISCONNECTED to " + newState);
@ -149,6 +151,10 @@ define([
evStart.reg(function () { toolbar.forgotten(); }); evStart.reg(function () { toolbar.forgotten(); });
break; break;
} }
case STATE.DELETED: {
evStart.reg(function () { toolbar.deleted(); });
break;
}
default: default:
} }
if (wasEditable !== (state === STATE.READY)) { if (wasEditable !== (state === STATE.READY)) {
@ -257,6 +263,7 @@ define([
var onReady = function () { var onReady = function () {
var newContentStr = cpNfInner.chainpad.getUserDoc(); var newContentStr = cpNfInner.chainpad.getUserDoc();
if (state === STATE.DELETED) { return; }
var newPad = false; var newPad = false;
if (newContentStr === '') { newPad = true; } if (newContentStr === '') { newPad = true; }
@ -316,6 +323,7 @@ define([
} }
}; };
var onConnectionChange = function (info) { var onConnectionChange = function (info) {
if (state === STATE.DELETED) { return; }
stateChange(info.state ? STATE.INITIALIZING : STATE.DISCONNECTED); stateChange(info.state ? STATE.INITIALIZING : STATE.DISCONNECTED);
if (info.state) { if (info.state) {
UI.findOKButton().click(); UI.findOKButton().click();
@ -324,6 +332,18 @@ define([
} }
}; };
var onError = function (err) {
stateChange(STATE.DELETED);
var msg = err.type;
if (err.type === 'EEXPIRED') {
msg = Messages.expiredError;
if (err.loaded) {
msg += Messages.expiredErrorCopy;
}
}
UI.errorLoadingScreen(msg, true, true);
};
var setFileExporter = function (extension, fe, async) { var setFileExporter = function (extension, fe, async) {
var $export = common.createButton('export', true, {}, function () { var $export = common.createButton('export', true, {}, function () {
var ext = (typeof(extension) === 'function') ? extension() : extension; var ext = (typeof(extension) === 'function') ? extension() : extension;
@ -441,7 +461,8 @@ define([
onLocal: onLocal, onLocal: onLocal,
onInit: function () { stateChange(STATE.INITIALIZING); }, onInit: function () { stateChange(STATE.INITIALIZING); },
onReady: function () { evStart.reg(onReady); }, onReady: function () { evStart.reg(onReady); },
onConnectionChange: onConnectionChange onConnectionChange: onConnectionChange,
onError: onError
}); });
var privReady = Util.once(waitFor()); var privReady = Util.once(waitFor());
@ -457,6 +478,7 @@ define([
var infiniteSpinnerModal = false; var infiniteSpinnerModal = false;
window.setInterval(function () { window.setInterval(function () {
if (state === STATE.DISCONNECTED) { return; } if (state === STATE.DISCONNECTED) { return; }
if (state === STATE.DELETED) { return; }
var l; var l;
try { try {
l = cpNfInner.chainpad.getLag(); l = cpNfInner.chainpad.getLag();

@ -34,6 +34,7 @@ define([
var onLocal = config.onLocal || function () { }; var onLocal = config.onLocal || function () { };
var setMyID = config.setMyID || function () { }; var setMyID = config.setMyID || function () { };
var onReady = config.onReady || function () { }; var onReady = config.onReady || function () { };
var onError = config.onError || function () { };
var userName = config.userName; var userName = config.userName;
var initialState = config.initialState; var initialState = config.initialState;
if (config.transformFunction) { throw new Error("transformFunction is nolonger allowed"); } if (config.transformFunction) { throw new Error("transformFunction is nolonger allowed"); }
@ -83,6 +84,11 @@ define([
chainpad.abort(); chainpad.abort();
onConnectionChange({ state: false }); onConnectionChange({ state: false });
}); });
sframeChan.on('EV_RT_ERROR', function (err) {
isReady = false;
chainpad.abort();
onError(err);
});
sframeChan.on('EV_RT_CONNECT', function (content) { sframeChan.on('EV_RT_CONNECT', function (content) {
//content.members.forEach(userList.onJoin); //content.members.forEach(userList.onJoin);
isReady = false; isReady = false;

@ -102,6 +102,10 @@ define([], function () {
sframeChan.event('EV_RT_DISCONNECT'); sframeChan.event('EV_RT_DISCONNECT');
}); });
padRpc.onErrorEvent.reg(function (err) {
sframeChan.event('EV_RT_ERROR', err);
});
// join the netflux network, promise to handle opening of the channel // join the netflux network, promise to handle opening of the channel
padRpc.joinPad({ padRpc.joinPad({
channel: channel || null, channel: channel || null,

@ -31,6 +31,8 @@ define({
'EV_RT_CONNECT': true, 'EV_RT_CONNECT': true,
// Called after the history is finished synchronizing, no arguments. // Called after the history is finished synchronizing, no arguments.
'EV_RT_READY': true, 'EV_RT_READY': true,
// Called when the server returns an error in a pad (EEXPIRED, EDELETED).
'EV_RT_ERROR': true,
// Called from both outside and inside, argument is a (string) chainpad message. // Called from both outside and inside, argument is a (string) chainpad message.
'Q_RT_MESSAGE': true, 'Q_RT_MESSAGE': true,

@ -709,6 +709,7 @@ define([
typing = 1; typing = 1;
$spin.text(Messages.typing); $spin.text(Messages.typing);
$spin.interval = window.setInterval(function () { $spin.interval = window.setInterval(function () {
if (toolbar.isErrorState) { return; }
var dots = Array(typing+1).join('.'); var dots = Array(typing+1).join('.');
$spin.text(Messages.typing + dots); $spin.text(Messages.typing + dots);
typing++; typing++;
@ -718,6 +719,7 @@ define([
var onSynced = function () { var onSynced = function () {
if ($spin.timeout) { clearTimeout($spin.timeout); } if ($spin.timeout) { clearTimeout($spin.timeout); }
$spin.timeout = setTimeout(function () { $spin.timeout = setTimeout(function () {
if (toolbar.isErrorState) { return; }
window.clearInterval($spin.interval); window.clearInterval($spin.interval);
typing = -1; typing = -1;
$spin.text(Messages.saved); $spin.text(Messages.saved);
@ -1094,6 +1096,15 @@ define([
} }
}; };
// When the pad is deleted from the server
toolbar.deleted = function (/*userId*/) {
toolbar.isErrorState = true;
toolbar.connected = false;
if (toolbar.spinner) {
toolbar.spinner.text(Messages.deletedFromServer);
}
};
// On log out, remove permanently the realtime elements of the toolbar // On log out, remove permanently the realtime elements of the toolbar
Common.onLogout(function () { Common.onLogout(function () {
failed(); failed();

Loading…
Cancel
Save