ServerStorage.vue 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742
  1. <template>
  2. <div class="hidden"></div>
  3. </template>
  4. <script>
  5. //-----------------------------------------------------------------------------
  6. import vueComponent from '../../vueComponent.js';
  7. import _ from 'lodash';
  8. import bookManager from '../share/bookManager';
  9. import readerApi from '../../../api/reader';
  10. import * as utils from '../../../share/utils';
  11. import * as cryptoUtils from '../../../share/cryptoUtils';
  12. import localForage from 'localforage';
  13. const ssCacheStore = localForage.createInstance({
  14. name: 'ssCacheStore'
  15. });
  16. const componentOptions = {
  17. watch: {
  18. serverSyncEnabled: function() {
  19. this.serverSyncEnabledChanged();
  20. },
  21. serverStorageKey: function() {
  22. this.serverStorageKeyChanged(true);
  23. },
  24. settings: function() {
  25. this.debouncedSaveSettings();
  26. },
  27. profiles: function() {
  28. this.saveProfiles();
  29. },
  30. currentProfile: function() {
  31. this.currentProfileChanged(true);
  32. },
  33. libs: function() {
  34. this.debouncedSaveLibs();
  35. },
  36. },
  37. };
  38. class ServerStorage {
  39. _options = componentOptions;
  40. created() {
  41. this.inited = false;
  42. this.keyInited = false;
  43. this.commit = this.$store.commit;
  44. this.prevServerStorageKey = null;
  45. this.$root.generateNewServerStorageKey = () => {this.generateNewServerStorageKey()};
  46. this.debouncedSaveSettings = _.debounce(() => {
  47. this.saveSettings();
  48. }, 500);
  49. this.debouncedSaveLibs = _.debounce(() => {
  50. this.saveLibs();
  51. }, 500);
  52. this.debouncedNotifySuccess = _.debounce(() => {
  53. this.success('Данные синхронизированы с сервером');
  54. }, 1000);
  55. this.oldProfiles = {};
  56. this.oldSettings = {};
  57. this.oldLibs = {};
  58. }
  59. async init() {
  60. try {
  61. this.cachedRecent = await ssCacheStore.getItem('recent');
  62. if (!this.cachedRecent)
  63. await this.cleanCachedRecent('cachedRecent');
  64. this.cachedRecentPatch = await ssCacheStore.getItem('recent-patch');
  65. if (!this.cachedRecentPatch)
  66. await this.cleanCachedRecent('cachedRecentPatch');
  67. this.cachedRecentMod = await ssCacheStore.getItem('recent-mod');
  68. if (!this.cachedRecentMod)
  69. await this.cleanCachedRecent('cachedRecentMod');
  70. if (!this.serverStorageKey) {
  71. //генерируем новый ключ
  72. await this.generateNewServerStorageKey();
  73. } else {
  74. await this.serverStorageKeyChanged();
  75. }
  76. } finally {
  77. this.inited = true;
  78. }
  79. }
  80. async setCachedRecent(value) {
  81. await ssCacheStore.setItem('recent', value);
  82. this.cachedRecent = value;
  83. }
  84. async setCachedRecentPatch(value) {
  85. await ssCacheStore.setItem('recent-patch', value);
  86. this.cachedRecentPatch = value;
  87. }
  88. async setCachedRecentMod(value) {
  89. await ssCacheStore.setItem('recent-mod', value);
  90. this.cachedRecentMod = value;
  91. }
  92. async cleanCachedRecent(whatToClean) {
  93. if (whatToClean == 'cachedRecent' || whatToClean == 'all')
  94. await this.setCachedRecent({rev: 0, data: {}});
  95. if (whatToClean == 'cachedRecentPatch' || whatToClean == 'all')
  96. await this.setCachedRecentPatch({rev: 0, data: {}});
  97. if (whatToClean == 'cachedRecentMod' || whatToClean == 'all')
  98. await this.setCachedRecentMod({rev: 0, data: {}});
  99. }
  100. async generateNewServerStorageKey() {
  101. const key = utils.toBase58(utils.randomArray(32));
  102. this.commit('reader/setServerStorageKey', key);
  103. await this.serverStorageKeyChanged(true);
  104. }
  105. async serverSyncEnabledChanged() {
  106. if (this.serverSyncEnabled) {
  107. this.prevServerStorageKey = null;
  108. if (!this.serverStorageKey) {
  109. //генерируем новый ключ
  110. await this.generateNewServerStorageKey();
  111. } else {
  112. await this.serverStorageKeyChanged(true);
  113. }
  114. }
  115. }
  116. async serverStorageKeyChanged(force) {
  117. if (this.prevServerStorageKey != this.serverStorageKey) {
  118. this.prevServerStorageKey = this.serverStorageKey;
  119. this.hashedStorageKey = utils.toBase58(cryptoUtils.sha256(this.serverStorageKey));
  120. this.keyInited = true;
  121. await this.loadProfiles(force);
  122. this.checkCurrentProfile();
  123. await this.currentProfileChanged(force);
  124. await this.loadLibs(force);
  125. if (force)
  126. await this.cleanCachedRecent('all');
  127. const loadSuccess = await this.loadRecent();
  128. if (loadSuccess && force) {
  129. await this.saveRecent();
  130. }
  131. }
  132. }
  133. async currentProfileChanged(force) {
  134. if (!this.currentProfile)
  135. return;
  136. await this.loadSettings(force);
  137. }
  138. get serverSyncEnabled() {
  139. return this.$store.state.reader.serverSyncEnabled;
  140. }
  141. get settings() {
  142. return this.$store.state.reader.settings;
  143. }
  144. get settingsRev() {
  145. return this.$store.state.reader.settingsRev;
  146. }
  147. get serverStorageKey() {
  148. return this.$store.state.reader.serverStorageKey;
  149. }
  150. get profiles() {
  151. return this.$store.state.reader.profiles;
  152. }
  153. get profilesRev() {
  154. return this.$store.state.reader.profilesRev;
  155. }
  156. get currentProfile() {
  157. return this.$store.state.reader.currentProfile;
  158. }
  159. get showServerStorageMessages() {
  160. return this.settings.showServerStorageMessages;
  161. }
  162. get libs() {
  163. return this.$store.state.reader.libs;
  164. }
  165. get libsRev() {
  166. return this.$store.state.reader.libsRev;
  167. }
  168. checkCurrentProfile() {
  169. if (!this.profiles[this.currentProfile]) {
  170. this.commit('reader/setCurrentProfile', '');
  171. }
  172. }
  173. success(message) {
  174. if (this.showServerStorageMessages)
  175. this.$root.notify.success(message);
  176. }
  177. warning(message) {
  178. if (this.showServerStorageMessages && !this.offlineModeActive)
  179. this.$root.notify.warning(message);
  180. }
  181. error(message) {
  182. if (this.showServerStorageMessages && !this.offlineModeActive) {
  183. this.errorMessageCounter = (this.errorMessageCounter ? this.errorMessageCounter + 1 : 1);
  184. const hint = (this.errorMessageCounter < 2 ? '' :
  185. '<div><br>Надоело это сообщение? Добавьте в настройках кнопку "Автономный режим" ' +
  186. '<i class="la la-unlink" style="font-size: 20px; color: white"></i> на панель инструментов и активируйте ее.</div>'
  187. );
  188. this.$root.notify.error(message + hint);
  189. }
  190. }
  191. async loadSettings(force = false, doNotifySuccess = true) {
  192. if (!this.keyInited || !this.serverSyncEnabled || !this.currentProfile)
  193. return;
  194. const setsId = `settings-${this.currentProfile}`;
  195. const oldRev = this.settingsRev[setsId] || 0;
  196. //проверим ревизию на сервере
  197. if (!force) {
  198. try {
  199. const revs = await this.storageCheck({[setsId]: {}});
  200. if (revs.state == 'success' && revs.items[setsId].rev == oldRev) {
  201. return;
  202. }
  203. } catch(e) {
  204. this.error(`Ошибка соединения с сервером: ${e.message}`);
  205. return;
  206. }
  207. }
  208. let sets = null;
  209. try {
  210. sets = await this.storageGet({[setsId]: {}});
  211. } catch(e) {
  212. this.error(`Ошибка соединения с сервером: ${e.message}`);
  213. return;
  214. }
  215. if (sets.state == 'success') {
  216. sets = sets.items[setsId];
  217. if (sets.rev == 0)
  218. sets.data = {};
  219. this.oldSettings = _.cloneDeep(sets.data);
  220. this.commit('reader/setSettings', sets.data);
  221. this.commit('reader/setSettingsRev', {[setsId]: sets.rev});
  222. if (doNotifySuccess)
  223. this.debouncedNotifySuccess();
  224. } else {
  225. this.warning(`Неверный ответ сервера: ${sets.state}`);
  226. }
  227. }
  228. async saveSettings() {
  229. if (!this.keyInited || !this.serverSyncEnabled || !this.currentProfile || this.savingSettings)
  230. return;
  231. const diff = utils.getObjDiff(this.oldSettings, this.settings);
  232. if (utils.isEmptyObjDiff(diff))
  233. return;
  234. this.savingSettings = true;
  235. try {
  236. const setsId = `settings-${this.currentProfile}`;
  237. let result = {state: ''};
  238. const oldRev = this.settingsRev[setsId] || 0;
  239. try {
  240. result = await this.storageSet({[setsId]: {rev: oldRev + 1, data: this.settings}});
  241. } catch(e) {
  242. this.error(`Ошибка соединения с сервером (${e.message}). Данные не сохранены и могут быть перезаписаны.`);
  243. }
  244. if (result.state == 'reject') {
  245. await this.loadSettings(true, false);
  246. this.warning(`Последние изменения отменены. Данные синхронизированы с сервером.`);
  247. } else if (result.state == 'success') {
  248. this.oldSettings = _.cloneDeep(this.settings);
  249. this.commit('reader/setSettingsRev', {[setsId]: this.settingsRev[setsId] + 1});
  250. }
  251. } finally {
  252. this.savingSettings = false;
  253. }
  254. }
  255. async loadProfiles(force = false, doNotifySuccess = true) {
  256. if (!this.keyInited || !this.serverSyncEnabled)
  257. return;
  258. const oldRev = this.profilesRev;
  259. //проверим ревизию на сервере
  260. if (!force) {
  261. try {
  262. const revs = await this.storageCheck({profiles: {}});
  263. if (revs.state == 'success' && revs.items.profiles.rev == oldRev) {
  264. return;
  265. }
  266. } catch(e) {
  267. this.error(`Ошибка соединения с сервером: ${e.message}`);
  268. return;
  269. }
  270. }
  271. let prof = null;
  272. try {
  273. prof = await this.storageGet({profiles: {}});
  274. } catch(e) {
  275. this.error(`Ошибка соединения с сервером: ${e.message}`);
  276. return;
  277. }
  278. if (prof.state == 'success') {
  279. prof = prof.items.profiles;
  280. if (prof.rev == 0)
  281. prof.data = {};
  282. this.oldProfiles = _.cloneDeep(prof.data);
  283. this.commit('reader/setProfiles', prof.data);
  284. this.commit('reader/setProfilesRev', prof.rev);
  285. this.checkCurrentProfile();
  286. if (doNotifySuccess)
  287. this.debouncedNotifySuccess();
  288. } else {
  289. this.warning(`Неверный ответ сервера: ${prof.state}`);
  290. }
  291. }
  292. async saveProfiles() {
  293. if (!this.keyInited || !this.serverSyncEnabled || this.savingProfiles)
  294. return;
  295. const diff = utils.getObjDiff(this.oldProfiles, this.profiles);
  296. if (utils.isEmptyObjDiff(diff))
  297. return;
  298. //обнуляются профили во время разработки при hotReload, подстраховка
  299. if (!this.$store.state.reader.allowProfilesSave) {
  300. console.error('Сохранение профилей не санкционировано');
  301. return;
  302. }
  303. this.savingProfiles = true;
  304. try {
  305. let result = {state: ''};
  306. try {
  307. result = await this.storageSet({profiles: {rev: this.profilesRev + 1, data: this.profiles}});
  308. } catch(e) {
  309. this.error(`Ошибка соединения с сервером (${e.message}). Данные не сохранены и могут быть перезаписаны.`);
  310. }
  311. if (result.state == 'reject') {
  312. await this.loadProfiles(true, false);
  313. this.warning(`Последние изменения отменены. Данные синхронизированы с сервером.`);
  314. } else if (result.state == 'success') {
  315. this.oldProfiles = _.cloneDeep(this.profiles);
  316. this.commit('reader/setProfilesRev', this.profilesRev + 1);
  317. }
  318. } finally {
  319. this.savingProfiles = false;
  320. }
  321. }
  322. async loadLibs(force = false, doNotifySuccess = true) {
  323. if (!this.keyInited || !this.serverSyncEnabled)
  324. return;
  325. const oldRev = this.libsRev;
  326. //проверим ревизию на сервере
  327. if (!force) {
  328. try {
  329. const revs = await this.storageCheck({libs: {}});
  330. if (revs.state == 'success' && revs.items.libs.rev == oldRev) {
  331. return;
  332. }
  333. } catch(e) {
  334. this.error(`Ошибка соединения с сервером: ${e.message}`);
  335. return;
  336. }
  337. }
  338. let libs = null;
  339. try {
  340. libs = await this.storageGet({libs: {}});
  341. } catch(e) {
  342. this.error(`Ошибка соединения с сервером: ${e.message}`);
  343. return;
  344. }
  345. if (libs.state == 'success') {
  346. libs = libs.items.libs;
  347. if (libs.rev == 0)
  348. libs.data = {};
  349. this.oldLibs = _.cloneDeep(libs.data);
  350. this.commit('reader/setLibs', libs.data);
  351. this.commit('reader/setLibsRev', libs.rev);
  352. if (doNotifySuccess)
  353. this.debouncedNotifySuccess();
  354. } else {
  355. this.warning(`Неверный ответ сервера: ${libs.state}`);
  356. }
  357. }
  358. async saveLibs() {
  359. if (!this.keyInited || !this.serverSyncEnabled || this.savingLibs)
  360. return;
  361. const diff = utils.getObjDiff(this.oldLibs, this.libs);
  362. if (utils.isEmptyObjDiff(diff))
  363. return;
  364. this.savingLibs = true;
  365. try {
  366. let result = {state: ''};
  367. try {
  368. result = await this.storageSet({libs: {rev: this.libsRev + 1, data: this.libs}});
  369. } catch(e) {
  370. this.error(`Ошибка соединения с сервером (${e.message}). Данные не сохранены и могут быть перезаписаны.`);
  371. }
  372. if (result.state == 'reject') {
  373. await this.loadLibs(true, false);
  374. this.warning(`Последние изменения отменены. Данные синхронизированы с сервером.`);
  375. } else if (result.state == 'success') {
  376. this.oldLibs = _.cloneDeep(this.libs);
  377. this.commit('reader/setLibsRev', this.libsRev + 1);
  378. }
  379. } finally {
  380. this.savingLibs = false;
  381. }
  382. }
  383. async loadRecent(skipRevCheck = false, doNotifySuccess = true) {
  384. if (!this.keyInited || !this.serverSyncEnabled || this.loadingRecent)
  385. return;
  386. this.loadingRecent = true;
  387. try {
  388. //проверим ревизию на сервере
  389. let query = {recent: {}, recentPatch: {}, recentMod: {}};
  390. let revs = null;
  391. if (!skipRevCheck) {
  392. try {
  393. revs = await this.storageCheck(query);
  394. if (revs.state == 'success') {
  395. if (revs.items.recent.rev != this.cachedRecent.rev) {
  396. //no changes
  397. } else if (revs.items.recentPatch.rev != this.cachedRecentPatch.rev) {
  398. query = {recentPatch: {}, recentMod: {}};
  399. } else if (revs.items.recentMod.rev != this.cachedRecentMod.rev) {
  400. query = {recentMod: {}};
  401. } else
  402. return true;
  403. }
  404. } catch(e) {
  405. this.error(`Ошибка соединения с сервером: ${e.message}`);
  406. return;
  407. }
  408. }
  409. let recent = null;
  410. try {
  411. recent = await this.storageGet(query);
  412. } catch(e) {
  413. this.error(`Ошибка соединения с сервером: ${e.message}`);
  414. return;
  415. }
  416. if (recent.state == 'success') {
  417. let newRecent = recent.items.recent;
  418. let newRecentPatch = recent.items.recentPatch;
  419. let newRecentMod = recent.items.recentMod;
  420. if (!newRecent) {
  421. newRecent = _.cloneDeep(this.cachedRecent);
  422. }
  423. if (!newRecentPatch) {
  424. newRecentPatch = _.cloneDeep(this.cachedRecentPatch);
  425. }
  426. if (!newRecentMod) {
  427. newRecentMod = _.cloneDeep(this.cachedRecentMod);
  428. }
  429. if (newRecent.rev == 0) newRecent.data = {};
  430. if (newRecentPatch.rev == 0) newRecentPatch.data = {};
  431. if (newRecentMod.rev == 0) newRecentMod.data = {};
  432. let result = Object.assign({}, newRecent.data, newRecentPatch.data);
  433. const md = newRecentMod.data;
  434. if (md.key && result[md.key])
  435. result[md.key] = utils.applyObjDiff(result[md.key], md.mod, {isAddChanged: true});
  436. /*if (!bookManager.loaded) {
  437. this.warning('Ожидание загрузки списка книг перед синхронизацией');
  438. while (!bookManager.loaded) await utils.sleep(100);
  439. }*/
  440. if (newRecent.rev != this.cachedRecent.rev)
  441. await this.setCachedRecent(newRecent);
  442. if (newRecentPatch.rev != this.cachedRecentPatch.rev)
  443. await this.setCachedRecentPatch(newRecentPatch);
  444. if (newRecentMod.rev != this.cachedRecentMod.rev)
  445. await this.setCachedRecentMod(newRecentMod);
  446. await bookManager.setRecent(result);
  447. } else {
  448. this.warning(`Неверный ответ сервера: ${recent.state}`);
  449. return;
  450. }
  451. if (doNotifySuccess)
  452. this.debouncedNotifySuccess();
  453. } finally {
  454. this.loadingRecent = false;
  455. }
  456. return true;
  457. }
  458. async saveRecent(itemKey, recurse) {
  459. while (!this.inited || this.savingRecent)
  460. await utils.sleep(100);
  461. if (!this.keyInited || !this.serverSyncEnabled || this.savingRecent)
  462. return;
  463. this.savingRecent = true;
  464. try {
  465. const bm = bookManager;
  466. let needSaveRecent = false;
  467. let needSaveRecentPatch = false;
  468. let needSaveRecentMod = false;
  469. //newRecentMod
  470. let newRecentMod = {};
  471. if (itemKey && this.cachedRecentPatch.data[itemKey] && this.prevItemKey == itemKey) {
  472. newRecentMod = _.cloneDeep(this.cachedRecentMod);
  473. newRecentMod.rev++;
  474. newRecentMod.data.key = itemKey;
  475. newRecentMod.data.mod = utils.getObjDiff(this.cachedRecentPatch.data[itemKey], bm.recent[itemKey]);
  476. needSaveRecentMod = true;
  477. }
  478. this.prevItemKey = itemKey;
  479. //newRecentPatch
  480. let newRecentPatch = {};
  481. if (itemKey && !needSaveRecentMod) {
  482. newRecentPatch = _.cloneDeep(this.cachedRecentPatch);
  483. newRecentPatch.rev++;
  484. newRecentPatch.data[itemKey] = _.cloneDeep(bm.recent[itemKey]);
  485. const applyMod = this.cachedRecentMod.data;
  486. if (applyMod && applyMod.key && newRecentPatch.data[applyMod.key])
  487. newRecentPatch.data[applyMod.key] = utils.applyObjDiff(newRecentPatch.data[applyMod.key], applyMod.mod, {isAddChanged: true});
  488. newRecentMod = {rev: this.cachedRecentMod.rev + 1, data: {}};
  489. needSaveRecentPatch = true;
  490. needSaveRecentMod = true;
  491. }
  492. //newRecent
  493. let newRecent = {};
  494. if (!itemKey || (needSaveRecentPatch && Object.keys(newRecentPatch.data).length > 10)) {
  495. //ждем весь bm.recent
  496. /*while (!bookManager.loaded)
  497. await utils.sleep(100);*/
  498. newRecent = {rev: this.cachedRecent.rev + 1, data: _.cloneDeep(bm.recent)};
  499. newRecentPatch = {rev: this.cachedRecentPatch.rev + 1, data: {}};
  500. newRecentMod = {rev: this.cachedRecentMod.rev + 1, data: {}};
  501. needSaveRecent = true;
  502. needSaveRecentPatch = true;
  503. needSaveRecentMod = true;
  504. }
  505. //query
  506. let query = {};
  507. if (needSaveRecent) {
  508. query = {recent: newRecent, recentPatch: newRecentPatch, recentMod: newRecentMod};
  509. } else if (needSaveRecentPatch) {
  510. query = {recentPatch: newRecentPatch, recentMod: newRecentMod};
  511. } else {
  512. query = {recentMod: newRecentMod};
  513. }
  514. //сохранение
  515. let result = {state: ''};
  516. try {
  517. result = await this.storageSet(query);
  518. } catch(e) {
  519. this.error(`Ошибка соединения с сервером (${e.message}). Данные не сохранены и могут быть перезаписаны.`);
  520. }
  521. if (result.state == 'reject') {
  522. const res = await this.loadRecent(false, false);
  523. if (res)
  524. this.warning(`Последние изменения отменены. Данные синхронизированы с сервером.`);
  525. if (!recurse && itemKey) {
  526. this.savingRecent = false;
  527. await this.saveRecent(itemKey, true);
  528. return;
  529. }
  530. } else if (result.state == 'success') {
  531. if (needSaveRecent && newRecent.rev)
  532. await this.setCachedRecent(newRecent);
  533. if (needSaveRecentPatch && newRecentPatch.rev)
  534. await this.setCachedRecentPatch(newRecentPatch);
  535. if (needSaveRecentMod && newRecentMod.rev)
  536. await this.setCachedRecentMod(newRecentMod);
  537. }
  538. } finally {
  539. this.savingRecent = false;
  540. }
  541. }
  542. async storageCheck(items) {
  543. return await this.storageApi('check', items);
  544. }
  545. async storageGet(items) {
  546. return await this.storageApi('get', items);
  547. }
  548. async storageSet(items, force) {
  549. return await this.storageApi('set', items, force);
  550. }
  551. async storageApi(action, items, force) {
  552. const request = {action, items};
  553. if (force)
  554. request.force = true;
  555. const encodedRequest = await this.encodeStorageItems(request);
  556. return await this.decodeStorageItems(await readerApi.storage(encodedRequest));
  557. }
  558. async encodeStorageItems(request) {
  559. if (!this.hashedStorageKey)
  560. throw new Error('hashedStorageKey is empty');
  561. if (!_.isObject(request.items))
  562. throw new Error('items is not an object');
  563. let result = Object.assign({}, request);
  564. let items = {};
  565. for (const id of Object.keys(request.items)) {
  566. const item = request.items[id];
  567. if (request.action == 'set' && !_.isObject(item.data))
  568. throw new Error('encodeStorageItems: data is not an object');
  569. let encoded = Object.assign({}, item);
  570. if (item.data) {
  571. const comp = utils.pako.deflate(JSON.stringify(item.data), {level: 1});
  572. let encrypted = null;
  573. try {
  574. encrypted = cryptoUtils.aesEncrypt(comp, this.serverStorageKey);
  575. } catch (e) {
  576. throw new Error('encrypt failed');
  577. }
  578. encoded.data = '1' + utils.toBase64(encrypted);
  579. }
  580. items[`${this.hashedStorageKey}.${utils.toBase58(id)}`] = encoded;
  581. }
  582. result.items = items;
  583. return result;
  584. }
  585. async decodeStorageItems(response) {
  586. if (!this.hashedStorageKey)
  587. throw new Error('hashedStorageKey is empty');
  588. let result = Object.assign({}, response);
  589. let items = {};
  590. if (response.items) {
  591. if (!_.isObject(response.items))
  592. throw new Error('items is not an object');
  593. for (const id of Object.keys(response.items)) {
  594. const item = response.items[id];
  595. let decoded = Object.assign({}, item);
  596. if (item.data) {
  597. if (!_.isString(item.data) || !item.data.length)
  598. throw new Error('decodeStorageItems: data is not a string');
  599. if (item.data[0] !== '1')
  600. throw new Error('decodeStorageItems: unknown data format');
  601. const a = utils.fromBase64(item.data.substr(1));
  602. let decrypted = null;
  603. try {
  604. decrypted = cryptoUtils.aesDecrypt(a, this.serverStorageKey);
  605. } catch (e) {
  606. throw new Error('decrypt failed');
  607. }
  608. decoded.data = JSON.parse(utils.pako.inflate(decrypted, {to: 'string'}));
  609. }
  610. const ids = id.split('.');
  611. if (!(ids.length == 2) || !(ids[0] == this.hashedStorageKey))
  612. throw new Error(`decodeStorageItems: bad id - ${id}`);
  613. items[utils.fromBase58(ids[1]).toString()] = decoded;
  614. }
  615. }
  616. result.items = items;
  617. return result;
  618. }
  619. }
  620. export default vueComponent(ServerStorage);
  621. //-----------------------------------------------------------------------------
  622. </script>