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:
parent
024967ce60
commit
43c594b6b2
@ -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
|
||||
// ------------------------------------------------------------------------
|
||||
|
@ -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)
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user