Merge branch 'feature/ss_fix2' into develop

This commit is contained in:
Book Pauk
2019-10-18 15:25:57 +07:00
3 changed files with 155 additions and 158 deletions

View File

@@ -13,6 +13,11 @@ import readerApi from '../../../api/reader';
import * as utils from '../../../share/utils'; import * as utils from '../../../share/utils';
import * as cryptoUtils from '../../../share/cryptoUtils'; import * as cryptoUtils from '../../../share/cryptoUtils';
import localForage from 'localforage';
const ssCacheStore = localForage.createInstance({
name: 'ssCacheStore'
});
export default @Component({ export default @Component({
watch: { watch: {
serverSyncEnabled: function() { serverSyncEnabled: function() {
@@ -58,35 +63,52 @@ class ServerStorage extends Vue {
async init() { async init() {
try { try {
this.cachedRecent = await ssCacheStore.getItem('recent');
if (!this.cachedRecent)
await this.setCachedRecent({rev: 0, data: {}});
this.cachedRecentPatch = await ssCacheStore.getItem('recent-patch');
if (!this.cachedRecentPatch)
await this.setCachedRecentPatch({rev: 0, data: {}});
this.cachedRecentMod = await ssCacheStore.getItem('recent-mod');
if (!this.cachedRecentMod)
await this.setCachedRecentMod({rev: 0, data: {}});
if (!this.serverStorageKey) { if (!this.serverStorageKey) {
//генерируем новый ключ //генерируем новый ключ
await this.generateNewServerStorageKey(); await this.generateNewServerStorageKey();
} else { } else {
await this.serverStorageKeyChanged(); await this.serverStorageKeyChanged();
} }
bookManager.addEventListener(this.bookManagerEvent); bookManager.addEventListener(this.bookManagerEvent);
} finally { } finally {
this.inited = true; this.inited = true;
} }
} }
async setCachedRecent(value) {
await ssCacheStore.setItem('recent', value);
this.cachedRecent = value;
}
async setCachedRecentPatch(value) {
await ssCacheStore.setItem('recent-patch', value);
this.cachedRecentPatch = value;
}
async setCachedRecentMod(value) {
await ssCacheStore.setItem('recent-mod', value);
this.cachedRecentMod = value;
}
async bookManagerEvent(eventName, itemKey) { async bookManagerEvent(eventName, itemKey) {
if (!this.serverSyncEnabled) if (!this.serverSyncEnabled)
return; return;
if (eventName == 'recent-changed') { if (eventName == 'recent-changed') {
if (itemKey) { if (itemKey) {
if (!this.recentDeltaInited) {
await this.loadRecent();
this.warning('Функции сохранения на сервер пока недоступны');
return;
}
if (!this.recentDelta)
this.recentDelta = {};
this.recentDelta[itemKey] = _.cloneDeep(bookManager.recent[itemKey]);
this.debouncedSaveRecent(itemKey); this.debouncedSaveRecent(itemKey);
} }
} }
@@ -119,8 +141,8 @@ class ServerStorage extends Vue {
await this.loadProfiles(force); await this.loadProfiles(force);
this.checkCurrentProfile(); this.checkCurrentProfile();
await this.currentProfileChanged(force); await this.currentProfileChanged(force);
await this.loadRecent(); const loadSuccess = await this.loadRecent();
if (force) if (loadSuccess && force)
await this.saveRecent(); await this.saveRecent();
} }
} }
@@ -340,46 +362,26 @@ class ServerStorage extends Vue {
} }
} }
async initRecentDelta() {
let recentDelta = null;
try {
recentDelta = await this.storageGet({recentDelta: {}});
} catch(e) {
this.error(`Ошибка соединения с сервером: ${e.message}`);
return;
}
if (recentDelta.state == 'success') {
recentDelta = recentDelta.items.recentDelta;
if (recentDelta.rev == 0)
recentDelta.data = {};
this.recentDelta = recentDelta.data;
this.recentDeltaInited = true;
} else {
this.warning(`Неверный ответ сервера: ${recentDelta.state}`);
}
}
async loadRecent(skipRevCheck = false, doNotifySuccess = true) { async loadRecent(skipRevCheck = false, doNotifySuccess = true) {
if (!this.keyInited || !this.serverSyncEnabled || this.loadingRecent) if (!this.keyInited || !this.serverSyncEnabled || this.loadingRecent)
return; return;
this.loadingRecent = true; this.loadingRecent = true;
try { try {
const oldRecentRev = bookManager.recentRev;
const oldRecentDeltaRev = bookManager.recentDeltaRev;
//проверим ревизию на сервере //проверим ревизию на сервере
let query = {recent: {}, recentPatch: {}, recentMod: {}};
let revs = null; let revs = null;
if (!skipRevCheck) { if (!skipRevCheck) {
try { try {
revs = await this.storageCheck({recent: {}, recentDelta: {}}); revs = await this.storageCheck(query);
if (revs.state == 'success' && revs.items.recent.rev == oldRecentRev && if (revs.state == 'success') {
revs.items.recentDelta.rev == oldRecentDeltaRev) { if (revs.items.recent.rev != this.cachedRecent.rev) {
if (!this.recentDeltaInited) //no changes
await this.initRecentDelta(); } else if (revs.items.recentPatch.rev != this.cachedRecentPatch.rev) {
return; query = {recentPatch: {}, recentMod: {}};
} else if (revs.items.recentMod.rev != this.cachedRecentMod.rev) {
query = {recentMod: {}};
} else
return true;
} }
} catch(e) { } catch(e) {
this.error(`Ошибка соединения с сервером: ${e.message}`); this.error(`Ошибка соединения с сервером: ${e.message}`);
@@ -389,46 +391,53 @@ class ServerStorage extends Vue {
let recent = null; let recent = null;
try { try {
recent = await this.storageGet({recent: {}, recentDelta: {}}); recent = await this.storageGet(query);
} catch(e) { } catch(e) {
this.error(`Ошибка соединения с сервером: ${e.message}`); this.error(`Ошибка соединения с сервером: ${e.message}`);
return; return;
} }
if (recent.state == 'success') { if (recent.state == 'success') {
let recentDelta = recent.items.recentDelta; let newRecent = recent.items.recent;
recent = recent.items.recent; let newRecentPatch = recent.items.recentPatch;
let newRecentMod = recent.items.recentMod;
if (recent.rev == 0) if (!newRecent) {
recent.data = {}; newRecent = _.cloneDeep(this.cachedRecent);
let newRecent = {};
if (recentDelta && recentDelta.data) {
if (recentDelta.data.diff) {
newRecent = recent.data;
const key = recentDelta.data.diff.key;
if (newRecent[key])
newRecent[key] = utils.applyObjDiff(newRecent[key], recentDelta.data.diff);
} else {
newRecent = Object.assign(recent.data, recentDelta.data);
} }
this.recentDelta = recentDelta.data; if (!newRecentPatch) {
} else { newRecentPatch = _.cloneDeep(this.cachedRecentPatch);
newRecent = recent.data; }
this.recentDelta = {}; if (!newRecentMod) {
newRecentMod = _.cloneDeep(this.cachedRecentMod);
} }
this.recentDeltaInited = true; if (newRecent.rev == 0) newRecent.data = {};
if (newRecentPatch.rev == 0) newRecentPatch.data = {};
if (newRecentMod.rev == 0) newRecentMod.data = {};
let result = Object.assign({}, newRecent.data, newRecentPatch.data);
const md = newRecentMod.data;
if (md.key && result[md.key])
result[md.key] = utils.applyObjDiff(result[md.key], md.mod);
if (newRecent.rev != this.cachedRecent.rev)
await this.setCachedRecent(newRecent);
if (newRecentPatch.rev != this.cachedRecentPatch.rev)
await this.setCachedRecentPatch(newRecentPatch);
if (newRecentMod.rev != this.cachedRecentMod.rev)
await this.setCachedRecentMod(newRecentMod);
if (!bookManager.loaded) { if (!bookManager.loaded) {
this.warning('Ожидание загрузки списка книг перед синхронизацией'); this.warning('Ожидание загрузки списка книг перед синхронизацией');
while (!bookManager.loaded) await utils.sleep(100); while (!bookManager.loaded) await utils.sleep(100);
} }
await bookManager.setRecent(newRecent);
await bookManager.setRecentRev(recent.rev); await bookManager.setRecent(result);
await bookManager.setRecentDeltaRev(recentDelta.rev);
} else { } else {
this.warning(`Неверный ответ сервера: ${recent.state}`); this.warning(`Неверный ответ сервера: ${recent.state}`);
return;
} }
if (doNotifySuccess) if (doNotifySuccess)
@@ -436,6 +445,7 @@ class ServerStorage extends Vue {
} finally { } finally {
this.loadingRecent = false; this.loadingRecent = false;
} }
return true;
} }
async saveRecent(itemKey, recurse) { async saveRecent(itemKey, recurse) {
@@ -444,91 +454,91 @@ class ServerStorage extends Vue {
const bm = bookManager; const bm = bookManager;
//вычисление критерия сохранения целиком let needSaveRecent = false;
if (!this.sameKeyCount) let needSaveRecentPatch = false;
this.sameKeyCount = 0; let needSaveRecentMod = false;
if (this.prevItemKey == itemKey) {
this.sameKeyCount++; let applyMod = null;
} else {
this.sameKeyCount = 0; //newRecentMod
let newRecentMod = {};
if (itemKey && this.cachedRecentPatch.data[itemKey]) {
if (this.prevItemKey == itemKey) {//сохраняем только дифф
newRecentMod = _.cloneDeep(this.cachedRecentMod);
newRecentMod.rev++;
newRecentMod.data.key = itemKey;
newRecentMod.data.mod = utils.getObjDiff(this.cachedRecentPatch.data[itemKey], bm.recent[itemKey]);
needSaveRecentMod = true;
} else {//ключ не совпадает, надо сохранять патч
applyMod = newRecentMod.data;
}
} }
const l = Object.keys(this.recentDelta).length - (1*(!!this.recentDelta.diff));
this.makeDeltaDiff = (l == 1 && this.prevItemKey == itemKey ? this.makeDeltaDiff : false);
const forceSaveRecent = l > 20 || (this.sameKeyCount > 5 && (l > 1)) || (l == 1 && this.sameKeyCount > 10 && !this.makeDeltaDiff);
this.sameKeyCount = (!forceSaveRecent ? this.sameKeyCount : 0);
this.prevItemKey = itemKey; this.prevItemKey = itemKey;
//дифф от дельты для уменьшения размера передаваемых данных в частном случае //newRecentPatch
if (this.makeDeltaDiff) { let newRecentPatch = {};
this.recentDelta.diff = utils.getObjDiff(this.prevSavedItem, bm.recent[itemKey]); if (itemKey && !needSaveRecentMod) {
this.recentDelta.diff.key = itemKey; newRecentPatch = _.cloneDeep(this.cachedRecentPatch);
delete this.recentDelta[itemKey]; newRecentPatch.rev++;
} else if (this.recentDelta.diff) { newRecentPatch.data[itemKey] = bm.recent[itemKey];
const key = this.recentDelta.diff.key; if (applyMod && applyMod.key && newRecentPatch.data[applyMod.key])
if (!this.prevSavedItem && bm.recent[key]) newRecentPatch.data[applyMod.key] = utils.applyObjDiff(newRecentPatch.data[applyMod.key], applyMod.mod);
this.prevSavedItem = _.cloneDeep(bm.recent[key]); newRecentMod = {rev: this.cachedRecentMod.rev + 1, data: {}};
if (this.prevSavedItem) { needSaveRecentPatch = true;
this.recentDelta[key] = utils.applyObjDiff(this.prevSavedItem, this.recentDelta.diff); needSaveRecentMod = true;
} }
delete this.recentDelta.diff;
//newRecent
let newRecent = {};
if (!itemKey || (needSaveRecentPatch && Object.keys(newRecentPatch.data).length > 2)) {
newRecent = {rev: this.cachedRecent.rev + 1, data: bm.recent};
newRecentPatch = {rev: this.cachedRecentPatch.rev + 1, data: {}};
newRecentMod = {rev: this.cachedRecentMod.rev + 1, data: {}};
needSaveRecent = true;
needSaveRecentPatch = true;
needSaveRecentMod = true;
}
//query
let query = {};
if (needSaveRecent) {
query = {recent: newRecent, recentPatch: newRecentPatch, recentMod: newRecentMod};
} else if (needSaveRecentPatch) {
query = {recentPatch: newRecentPatch, recentMod: newRecentMod};
} else {
query = {recentMod: newRecentMod};
} }
//сохранение //сохранение
this.savingRecent = true; this.savingRecent = true;
try { try {
if (forceSaveRecent) {//сохраняем recent целиком
let result = {state: ''}; let result = {state: ''};
try { try {
result = await this.storageSet({recent: {rev: bm.recentRev + 1, data: bm.recent}, recentDelta: {rev: bm.recentDeltaRev + 1, data: {}}}); result = await this.storageSet(query);
} catch(e) { } catch(e) {
this.error(`Ошибка соединения с сервером (${e.message}). Данные не сохранены и могут быть перезаписаны.`); this.error(`Ошибка соединения с сервером (${e.message}). Данные не сохранены и могут быть перезаписаны.`);
} }
if (result.state == 'reject') { if (result.state == 'reject') {
await this.loadRecent(true, false); await this.loadRecent(false, false);
this.warning(`Последние изменения отменены. Данные синхронизированы с сервером.`); this.warning(`Последние изменения отменены. Данные синхронизированы с сервером.`);
if (!recurse) { if (!recurse && itemKey) {
this.savingRecent = false; this.savingRecent = false;
this.recentDelta[itemKey] = _.cloneDeep(bm.recent[itemKey]);
this.saveRecent(itemKey, true); this.saveRecent(itemKey, true);
return; return;
} }
} else if (result.state == 'success') { } else if (result.state == 'success') {
this.makeDeltaDiff = true; if (needSaveRecent && newRecent.rev)
this.prevSavedItem = _.cloneDeep(bm.recent[itemKey]); await this.setCachedRecent(newRecent);
if (needSaveRecentPatch && newRecentPatch.rev)
this.recentDelta = {}; await this.setCachedRecentPatch(newRecentPatch);
await bm.setRecentRev(bm.recentRev + 1); if (needSaveRecentMod && newRecentMod.rev)
await bm.setRecentDeltaRev(bm.recentDeltaRev + 1); await this.setCachedRecentMod(newRecentMod);
}
} else {//сохраняем только дифф
let result = {state: ''};
try {
result = await this.storageSet({recentDelta: {rev: bm.recentDeltaRev + 1, data: this.recentDelta}});
} catch(e) {
this.error(`Ошибка соединения с сервером (${e.message}). Данные не сохранены и могут быть перезаписаны.`);
}
if (result.state == 'reject') {
await this.loadRecent(true, false);
this.warning(`Последние изменения отменены. Данные синхронизированы с сервером.`);
if (!recurse) {
this.savingRecent = false;
this.recentDelta[itemKey] = _.cloneDeep(bm.recent[itemKey]);
this.saveRecent(itemKey, true);
return;
}
} else if (result.state == 'success') {
await bm.setRecentDeltaRev(bm.recentDeltaRev + 1);
}
} }
} finally { } finally {
this.savingRecent = false; this.savingRecent = false;

View File

@@ -36,9 +36,6 @@ class BookManager {
} }
} }
this.recentRev = await bmRecentStore.getItem('recent-rev') || 0;
this.recentDeltaRev = await bmRecentStore.getItem('recent-delta-rev') || 0;
this.recentChanged = true; this.recentChanged = true;
this.loadStored();//no await this.loadStored();//no await
@@ -440,7 +437,7 @@ class BookManager {
Object.assign(mergedRecent, value); Object.assign(mergedRecent, value);
//подстраховка //подстраховка от hotReload
for (let i of Object.keys(mergedRecent)) { for (let i of Object.keys(mergedRecent)) {
if (!mergedRecent[i].key || mergedRecent[i].key !== i) if (!mergedRecent[i].key || mergedRecent[i].key !== i)
delete mergedRecent[i]; delete mergedRecent[i];
@@ -466,16 +463,6 @@ class BookManager {
this.emit('recent-changed'); this.emit('recent-changed');
} }
async setRecentRev(value) {
await bmRecentStore.setItem('recent-rev', value);
this.recentRev = value;
}
async setRecentDeltaRev(value) {
await bmRecentStore.setItem('recent-delta-rev', value);
this.recentDeltaRev = value;
}
addEventListener(listener) { addEventListener(listener) {
if (this.eventListeners.indexOf(listener) < 0) if (this.eventListeners.indexOf(listener) < 0)
this.eventListeners.push(listener); this.eventListeners.push(listener);

View File

@@ -20,7 +20,7 @@ function webpackDevMiddleware(app) {
function logQueries(app) { function logQueries(app) {
app.use(function(req, res, next) { app.use(function(req, res, next) {
const start = Date.now(); const start = Date.now();
log(`${req.method} ${req.originalUrl} ${JSON.stringify(req.body).substr(0, 2000)}`); log(`${req.method} ${req.originalUrl} ${JSON.stringify(req.body).substr(0, 4000)}`);
//log(`${JSON.stringify(req.headers, null, 2)}`) //log(`${JSON.stringify(req.headers, null, 2)}`)
res.once('finish', () => { res.once('finish', () => {
log(`${Date.now() - start}ms`); log(`${Date.now() - start}ms`);