1
0
mirror of https://github.com/laurent22/joplin.git synced 2024-12-24 10:27:10 +02:00

Server, Desktop: Sync deleted items first to allow fixing oversized accounts

This commit is contained in:
Laurent Cozic 2021-09-18 15:02:24 +01:00
parent 024967ce60
commit 43c594b6b2
4 changed files with 57 additions and 35 deletions

View File

@ -479,6 +479,31 @@ export default class Synchronizer {
void this.cancel();
});
// ========================================================================
// 2. DELETE_REMOTE
// ------------------------------------------------------------------------
// Delete the remote items that have been deleted locally.
// ========================================================================
if (syncSteps.indexOf('delete_remote') >= 0) {
const deletedItems = await BaseItem.deletedItems(syncTargetId);
for (let i = 0; i < deletedItems.length; i++) {
if (this.cancelling()) break;
const item = deletedItems[i];
const path = BaseItem.systemPath(item.item_id);
this.logSyncOperation('deleteRemote', null, { id: item.item_id }, 'local has been deleted');
await this.apiCall('delete', path);
if (item.item_type === BaseModel.TYPE_RESOURCE) {
const remoteContentPath = resourceRemotePath(item.item_id);
await this.apiCall('delete', remoteContentPath);
}
await BaseItem.remoteDeletedItem(syncTargetId, item.item_id);
}
} // DELETE_REMOTE STEP
// ========================================================================
// 1. UPLOAD
// ------------------------------------------------------------------------
@ -763,31 +788,6 @@ export default class Synchronizer {
}
} // UPLOAD STEP
// ========================================================================
// 2. DELETE_REMOTE
// ------------------------------------------------------------------------
// Delete the remote items that have been deleted locally.
// ========================================================================
if (syncSteps.indexOf('delete_remote') >= 0) {
const deletedItems = await BaseItem.deletedItems(syncTargetId);
for (let i = 0; i < deletedItems.length; i++) {
if (this.cancelling()) break;
const item = deletedItems[i];
const path = BaseItem.systemPath(item.item_id);
this.logSyncOperation('deleteRemote', null, { id: item.item_id }, 'local has been deleted');
await this.apiCall('delete', path);
if (item.item_type === BaseModel.TYPE_RESOURCE) {
const remoteContentPath = resourceRemotePath(item.item_id);
await this.apiCall('delete', remoteContentPath);
}
await BaseItem.remoteDeletedItem(syncTargetId, item.item_id);
}
} // DELETE_REMOTE STEP
// ------------------------------------------------------------------------
// 3. DELTA
// ------------------------------------------------------------------------

View File

@ -180,7 +180,7 @@ export default class UserModel extends BaseModel<User> {
}
public async checkMaxItemSizeLimit(user: User, buffer: Buffer, item: Item, joplinItem: any) {
// If the item is encrypted, we apply a multipler because encrypted
// If the item is encrypted, we apply a multiplier because encrypted
// items can be much larger (seems to be up to twice the size but for
// safety let's go with 2.2).
@ -198,14 +198,20 @@ export default class UserModel extends BaseModel<User> {
));
}
// Also apply a multiplier to take into account E2EE overhead
const maxTotalItemSize = getMaxTotalItemSize(user) * 1.5;
if (maxTotalItemSize && user.total_item_size + itemSize >= maxTotalItemSize) {
throw new ErrorPayloadTooLarge(_('Cannot save %s "%s" because it would go over the total allowed size (%s) for this account',
isNote ? _('note') : _('attachment'),
itemTitle ? itemTitle : item.name,
formatBytes(maxTotalItemSize)
));
// We allow lock files to go through so that sync can happen, which in
// turns allow user to fix oversized account by deleting items.
const isWhiteListed = itemSize < 200 && item.name.startsWith('locks/');
if (!isWhiteListed) {
// Also apply a multiplier to take into account E2EE overhead
const maxTotalItemSize = getMaxTotalItemSize(user) * 1.5;
if (maxTotalItemSize && user.total_item_size + itemSize >= maxTotalItemSize) {
throw new ErrorPayloadTooLarge(_('Cannot save %s "%s" because it would go over the total allowed size (%s) for this account',
isNote ? _('note') : _('attachment'),
itemTitle ? itemTitle : item.name,
formatBytes(maxTotalItemSize)
));
}
}
}

View File

@ -5,7 +5,7 @@ import Router from '../../utils/Router';
import { RouteType } from '../../utils/types';
import { AppContext } from '../../utils/types';
import * as fs from 'fs-extra';
import { ErrorForbidden, ErrorMethodNotAllowed, ErrorNotFound, ErrorPayloadTooLarge } from '../../utils/errors';
import { ErrorForbidden, ErrorMethodNotAllowed, ErrorNotFound, ErrorPayloadTooLarge, errorToPlainObject } from '../../utils/errors';
import ItemModel, { ItemSaveOption, SaveFromRawContentItem } from '../../models/ItemModel';
import { requestDeltaPagination, requestPagination } from '../../models/utils/pagination';
import { AclAction } from '../../models/BaseModel';
@ -66,7 +66,9 @@ export async function putItemContents(path: SubPath, ctx: AppContext, isBatch: b
const output = await ctx.joplin.models.item().saveFromRawContent(ctx.joplin.owner, items, saveOptions);
for (const [name] of Object.entries(output)) {
if (output[name].item) output[name].item = ctx.joplin.models.item().toApiOutput(output[name].item) as Item;
if (output[name].error) output[name].error = errorToPlainObject(output[name].error);
}
return output;
}

View File

@ -114,3 +114,17 @@ export function errorToString(error: Error): string {
if (error.stack) msg.push(error.stack);
return msg.join(': ');
}
interface PlainObjectError {
httpCode?: number;
message?: string;
code?: string;
}
export function errorToPlainObject(error: any): PlainObjectError {
const output: PlainObjectError = {};
if ('httpCode' in error) output.httpCode = error.httpCode;
if ('code' in error) output.code = error.code;
if ('message' in error) output.message = error.message;
return output;
}