Stop uploading files on quota error until next manual sync or restart
This commit is contained in:
parent
94795095f2
commit
ead93b6ccc
5 changed files with 123 additions and 44 deletions
|
@ -133,6 +133,12 @@ Zotero.Sync.Storage.Engine.prototype.start = Zotero.Promise.coroutine(function*
|
||||||
var filesEditable = Zotero.Libraries.get(libraryID).filesEditable;
|
var filesEditable = Zotero.Libraries.get(libraryID).filesEditable;
|
||||||
this.requestsRemaining = 0;
|
this.requestsRemaining = 0;
|
||||||
|
|
||||||
|
// Clear over-quota flag on manual sync
|
||||||
|
if (!this.background && Zotero.Sync.Storage.Local.storageRemainingForLibrary.has(libraryID)) {
|
||||||
|
Zotero.debug("Clearing over-quota flag for " + this.library.name);
|
||||||
|
Zotero.Sync.Storage.Local.storageRemainingForLibrary.delete(libraryID)
|
||||||
|
}
|
||||||
|
|
||||||
// Check for updated files to upload
|
// Check for updated files to upload
|
||||||
if (!filesEditable) {
|
if (!filesEditable) {
|
||||||
Zotero.debug("No file editing access -- skipping file modification check for "
|
Zotero.debug("No file editing access -- skipping file modification check for "
|
||||||
|
|
|
@ -11,6 +11,25 @@ Zotero.Sync.Storage.Local = {
|
||||||
|
|
||||||
lastFullFileCheck: {},
|
lastFullFileCheck: {},
|
||||||
uploadCheckFiles: [],
|
uploadCheckFiles: [],
|
||||||
|
storageRemainingForLibrary: new Map(),
|
||||||
|
|
||||||
|
init: function () {
|
||||||
|
Zotero.Notifier.registerObserver(this, ['group'], 'storageLocal');
|
||||||
|
},
|
||||||
|
|
||||||
|
notify: async function (action, type, ids, _extraData) {
|
||||||
|
// Clean up cache on group deletion
|
||||||
|
if (action == 'delete' && type == 'group') {
|
||||||
|
for (let libraryID of ids) {
|
||||||
|
if (this.lastFullFileCheck[libraryID]) {
|
||||||
|
delete this.lastFullFileCheck[libraryID];
|
||||||
|
}
|
||||||
|
if (this.storageRemainingForLibrary.has(libraryID)) {
|
||||||
|
this.storageRemainingForLibrary.delete(libraryID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
getEnabledForLibrary: function (libraryID) {
|
getEnabledForLibrary: function (libraryID) {
|
||||||
var libraryType = Zotero.Libraries.get(libraryID).libraryType;
|
var libraryType = Zotero.Libraries.get(libraryID).libraryType;
|
||||||
|
|
|
@ -257,7 +257,38 @@ Zotero.Sync.Storage.Mode.ZFS.prototype = {
|
||||||
|
|
||||||
uploadFile: Zotero.Promise.coroutine(function* (request) {
|
uploadFile: Zotero.Promise.coroutine(function* (request) {
|
||||||
var item = Zotero.Sync.Storage.Utilities.getItemFromRequest(request);
|
var item = Zotero.Sync.Storage.Utilities.getItemFromRequest(request);
|
||||||
if (yield Zotero.Attachments.hasMultipleFiles(item)) {
|
var multipleFiles = yield Zotero.Attachments.hasMultipleFiles(item);
|
||||||
|
|
||||||
|
// If we got a quota error for this library, skip upload for all multi-file attachments
|
||||||
|
// and for single-file attachments that are bigger than the remaining space. This is cleared
|
||||||
|
// in storageEngine for manual syncs.
|
||||||
|
var remaining = Zotero.Sync.Storage.Local.storageRemainingForLibrary.get(item.libraryID);
|
||||||
|
if (remaining !== undefined) {
|
||||||
|
let skip = false;
|
||||||
|
if (multipleFiles) {
|
||||||
|
Zotero.debug("Skipping multi-file upload after quota error");
|
||||||
|
skip = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
let size;
|
||||||
|
try {
|
||||||
|
// API rounds megabytes to 1 decimal place
|
||||||
|
size = ((yield OS.File.stat(item.getFilePath())).size / 1024 / 1024).toFixed(1);
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
Zotero.logError(e);
|
||||||
|
}
|
||||||
|
if (size >= remaining) {
|
||||||
|
Zotero.debug(`Skipping file upload after quota error (${size} >= ${remaining})`);
|
||||||
|
skip = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (skip) {
|
||||||
|
throw yield this._getQuotaError(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (multipleFiles) {
|
||||||
let created = yield Zotero.Sync.Storage.Utilities.createUploadFile(request);
|
let created = yield Zotero.Sync.Storage.Utilities.createUploadFile(request);
|
||||||
if (!created) {
|
if (!created) {
|
||||||
return new Zotero.Sync.Storage.Result;
|
return new Zotero.Sync.Storage.Result;
|
||||||
|
@ -565,51 +596,16 @@ Zotero.Sync.Storage.Mode.ZFS.prototype = {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let text, buttonText = null, buttonCallback;
|
// Store the remaining space so that we can skip files bigger than that until the next
|
||||||
let libraryType = item.library.libraryType;
|
// manual sync
|
||||||
|
let usage = req.getResponseHeader('Zotero-Storage-Usage');
|
||||||
|
let quota = req.getResponseHeader('Zotero-Storage-Quota');
|
||||||
|
Zotero.Sync.Storage.Local.storageRemainingForLibrary.set(item.libraryID, quota - usage);
|
||||||
|
|
||||||
// Group file
|
throw yield this._getQuotaError(item);
|
||||||
if (libraryType == 'group') {
|
|
||||||
var group = Zotero.Groups.getByLibraryID(item.libraryID);
|
|
||||||
text = Zotero.getString('sync.storage.error.zfs.groupQuotaReached1', group.name) + "\n\n"
|
|
||||||
+ Zotero.getString('sync.storage.error.zfs.groupQuotaReached2');
|
|
||||||
}
|
|
||||||
// Personal file
|
|
||||||
else {
|
|
||||||
text = Zotero.getString('sync.storage.error.zfs.personalQuotaReached1') + "\n\n"
|
|
||||||
+ Zotero.getString('sync.storage.error.zfs.personalQuotaReached2');
|
|
||||||
buttonText = Zotero.getString('sync.storage.openAccountSettings');
|
|
||||||
buttonCallback = function () {
|
|
||||||
var url = "https://www.zotero.org/settings/storage";
|
|
||||||
|
|
||||||
var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
|
|
||||||
.getService(Components.interfaces.nsIWindowMediator);
|
|
||||||
var win = wm.getMostRecentWindow("navigator:browser");
|
|
||||||
win.ZoteroPane.loadURI(url, { metaKey: true, ctrlKey: true, shiftKey: true });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var filename = item.attachmentFilename;
|
|
||||||
var fileSize = (yield OS.File.stat(item.getFilePath())).size;
|
|
||||||
|
|
||||||
text += "\n\n" + filename + " (" + Math.round(fileSize / 1024) + "KB)";
|
|
||||||
|
|
||||||
let e = new Zotero.Error(
|
|
||||||
text,
|
|
||||||
"ZFS_OVER_QUOTA",
|
|
||||||
{
|
|
||||||
dialogButtonText: buttonText,
|
|
||||||
dialogButtonCallback: buttonCallback
|
|
||||||
}
|
|
||||||
);
|
|
||||||
e.errorType = 'warning';
|
|
||||||
Zotero.debug(e, 2);
|
|
||||||
Components.utils.reportError(e);
|
|
||||||
throw e;
|
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given parameters from authorization, upload file to S3
|
* Given parameters from authorization, upload file to S3
|
||||||
*/
|
*/
|
||||||
|
@ -1013,5 +1009,45 @@ Zotero.Sync.Storage.Mode.ZFS.prototype = {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
return this._uploadFile(request, item, result);
|
return this._uploadFile(request, item, result);
|
||||||
})
|
}),
|
||||||
|
|
||||||
|
|
||||||
|
_getQuotaError: async function (item) {
|
||||||
|
var text, buttonText = null, buttonCallback;
|
||||||
|
var libraryType = item.library.libraryType;
|
||||||
|
|
||||||
|
// Group file
|
||||||
|
if (libraryType == 'group') {
|
||||||
|
let group = Zotero.Groups.getByLibraryID(item.libraryID);
|
||||||
|
text = Zotero.getString('sync.storage.error.zfs.groupQuotaReached1', group.name) + "\n\n"
|
||||||
|
+ Zotero.getString('sync.storage.error.zfs.groupQuotaReached2');
|
||||||
|
}
|
||||||
|
// Personal file
|
||||||
|
else {
|
||||||
|
text = Zotero.getString('sync.storage.error.zfs.personalQuotaReached1') + "\n\n"
|
||||||
|
+ Zotero.getString('sync.storage.error.zfs.personalQuotaReached2');
|
||||||
|
buttonText = Zotero.getString('sync.storage.openAccountSettings');
|
||||||
|
buttonCallback = function () {
|
||||||
|
let url = "https://www.zotero.org/settings/storage";
|
||||||
|
let win = Services.wm.getMostRecentWindow("navigator:browser");
|
||||||
|
win.ZoteroPane.loadURI(url, { metaKey: true, ctrlKey: true, shiftKey: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var filename = item.attachmentFilename;
|
||||||
|
var fileSize = (await OS.File.stat(item.getFilePath())).size;
|
||||||
|
|
||||||
|
text += "\n\n" + filename + " (" + Math.round(fileSize / 1024) + " KB)";
|
||||||
|
|
||||||
|
var e = new Zotero.Error(
|
||||||
|
text,
|
||||||
|
"ZFS_OVER_QUOTA",
|
||||||
|
{
|
||||||
|
dialogButtonText: buttonText,
|
||||||
|
dialogButtonCallback: buttonCallback
|
||||||
|
}
|
||||||
|
);
|
||||||
|
e.errorType = 'warning';
|
||||||
|
return e;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -735,6 +735,7 @@ Services.scriptloader.loadSubScript("resource://zotero/polyfill.js");
|
||||||
|
|
||||||
yield Zotero.Sync.Data.Local.init();
|
yield Zotero.Sync.Data.Local.init();
|
||||||
yield Zotero.Sync.Data.Utilities.init();
|
yield Zotero.Sync.Data.Utilities.init();
|
||||||
|
Zotero.Sync.Storage.Local.init();
|
||||||
Zotero.Sync.Runner = new Zotero.Sync.Runner_Module;
|
Zotero.Sync.Runner = new Zotero.Sync.Runner_Module;
|
||||||
Zotero.Sync.EventListeners.init();
|
Zotero.Sync.EventListeners.init();
|
||||||
Zotero.Streamer = new Zotero.Streamer_Module;
|
Zotero.Streamer = new Zotero.Streamer_Module;
|
||||||
|
|
|
@ -1001,16 +1001,20 @@ describe("Zotero.Sync.Storage.Mode.ZFS", function () {
|
||||||
item.synced = true;
|
item.synced = true;
|
||||||
yield item.saveTx();
|
yield item.saveTx();
|
||||||
|
|
||||||
|
var responses = 0;
|
||||||
server.respond(function (req) {
|
server.respond(function (req) {
|
||||||
if (req.method == "POST"
|
if (req.method == "POST"
|
||||||
&& req.url == `${baseURL}users/1/items/${item.key}/file`
|
&& req.url == `${baseURL}users/1/items/${item.key}/file`
|
||||||
&& req.requestBody.indexOf('upload=') == -1
|
&& req.requestBody.indexOf('upload=') == -1
|
||||||
&& req.requestHeaders["If-None-Match"] == "*") {
|
&& req.requestHeaders["If-None-Match"] == "*") {
|
||||||
|
responses++;
|
||||||
req.respond(
|
req.respond(
|
||||||
413,
|
413,
|
||||||
{
|
{
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
"Last-Modified-Version": 10
|
"Last-Modified-Version": 10,
|
||||||
|
"Zotero-Storage-Usage": "300",
|
||||||
|
"Zotero-Storage-Quota": "300"
|
||||||
},
|
},
|
||||||
"File would exceed quota (299.7 + 0.5 > 300)"
|
"File would exceed quota (299.7 + 0.5 > 300)"
|
||||||
);
|
);
|
||||||
|
@ -1024,6 +1028,19 @@ describe("Zotero.Sync.Storage.Mode.ZFS", function () {
|
||||||
assert.equal(e.errorType, 'warning');
|
assert.equal(e.errorType, 'warning');
|
||||||
assert.include(e.message, 'test.png');
|
assert.include(e.message, 'test.png');
|
||||||
assert.equal(e.dialogButtonText, Zotero.getString('sync.storage.openAccountSettings'));
|
assert.equal(e.dialogButtonText, Zotero.getString('sync.storage.openAccountSettings'));
|
||||||
|
assert.equal(responses, 1);
|
||||||
|
|
||||||
|
// Try again
|
||||||
|
var e = yield getPromiseError(zfs.uploadFile({
|
||||||
|
name: item.libraryKey
|
||||||
|
}));
|
||||||
|
assert.ok(e);
|
||||||
|
assert.equal(e.errorType, 'warning');
|
||||||
|
assert.include(e.message, 'test.png');
|
||||||
|
assert.equal(e.dialogButtonText, Zotero.getString('sync.storage.openAccountSettings'));
|
||||||
|
// Shouldn't have been another request. A manual sync resets the flag, but we're not
|
||||||
|
// testing that here.
|
||||||
|
assert.equal(responses, 1);
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue