Files
notesnook/packages/core/api/sync/index.js

171 lines
5.0 KiB
JavaScript
Raw Normal View History

2020-04-09 16:36:57 +05:00
/**
* GENERAL PROCESS:
* make a get request to server with current lastSynced
2020-04-09 16:36:57 +05:00
* parse the response. the response should contain everything that user has on the server
* decrypt the response
* merge everything into the database and look for conflicts
* send the conflicts (if any) to the end-user for resolution
* once the conflicts have been resolved, send the updated data back to the server
*/
/**
* MERGING:
* Locally, get everything that was editted/added after the lastSynced
2020-04-09 16:36:57 +05:00
* Run forEach loop on the server response.
* Add items that do not exist in the local collections
* Remove items (without asking) that need to be removed
* Update items that were editted before the lastSynced
* Try to merge items that were edited after the lastSynced
2020-04-09 16:36:57 +05:00
* Items in which the content has changed, send them for conflict resolution
* Otherwise, keep the most recently updated copy.
*/
/**
* CONFLICTS:
* Syncing should pause until all the conflicts have been resolved
* And then it should continue.
*/
import { EV, EVENTS, sendAttachmentsProgressEvent } from "../../common";
import Constants from "../../utils/constants";
2020-12-16 12:06:25 +05:00
import http from "../../utils/http";
import TokenManager from "../token-manager";
import Collector from "./collector";
2020-04-09 16:36:57 +05:00
import Merger from "./merger";
import { areAllEmpty } from "./utils";
2020-04-09 16:36:57 +05:00
export default class Sync {
/**
*
* @param {import("../index").default} db
2020-04-09 16:36:57 +05:00
*/
constructor(db) {
this._db = db;
this._collector = new Collector(this._db);
this._merger = new Merger(this._db);
this._tokenManager = new TokenManager(this._db.storage);
this._isSyncing = false;
2020-04-09 16:36:57 +05:00
}
async _fetch(lastSynced, token) {
2020-12-16 12:06:25 +05:00
return await http.get(
2020-12-16 13:23:14 +05:00
`${Constants.API_HOST}/sync?lst=${lastSynced}`,
2020-12-16 12:06:25 +05:00
token
);
2020-04-09 16:36:57 +05:00
}
async _performChecks() {
let lastSynced = (await this._db.storage.read("lastSynced")) || 0;
2020-12-16 12:06:25 +05:00
let token = await this._tokenManager.getAccessToken();
2020-04-09 16:36:57 +05:00
// update the conflicts status and if find any, throw
await this._db.conflicts.recalculate();
await this._db.conflicts.check();
2020-04-09 16:36:57 +05:00
2020-12-16 12:06:25 +05:00
return { lastSynced, token };
}
2020-12-11 20:19:28 +05:00
async start(full, force) {
if (this._isSyncing) return false;
if (force) await this._db.storage.write("lastSynced", 0);
2020-12-05 11:26:02 +05:00
let { lastSynced, token } = await this._performChecks();
try {
const now = Date.now();
this._isSyncing = true;
2020-04-09 16:36:57 +05:00
await this._uploadAttachments();
2020-04-09 16:36:57 +05:00
// we prepare local data before merging so we always have correct data
const data = await this._collector.collect(lastSynced);
data.lastSynced = now;
if (full) {
2021-09-20 12:10:36 +05:00
var serverResponse = await this._fetch(lastSynced, token);
// merge the server response
await this._merger.merge(serverResponse, lastSynced);
2021-09-20 12:10:36 +05:00
// await this._downloadAttachments(token);
}
2020-04-09 16:36:57 +05:00
// check for conflicts and throw
await this._db.conflicts.check();
2020-04-09 16:36:57 +05:00
// send the data back to server
lastSynced = await this._send(data, token);
// update our lastSynced time
if (lastSynced) {
await this._db.storage.write("lastSynced", lastSynced);
}
return true;
} catch (e) {
2021-09-15 02:16:55 +05:00
this._isSyncing = false;
throw e;
} finally {
this._isSyncing = false;
2020-08-22 11:25:23 +05:00
}
2020-04-09 16:36:57 +05:00
}
async eventMerge(serverResponse) {
let { lastSynced, token } = await this._performChecks();
const data = await this._collector.collect(lastSynced);
// merge the server response
await this._merger.merge(serverResponse, lastSynced);
// check for conflicts and throw
await this._db.conflicts.check();
// send the data back to server
lastSynced = await this._send(data, token);
// update our lastSynced time
if (lastSynced) {
await this._db.storage.write("lastSynced", lastSynced);
}
EV.publish(EVENTS.appRefreshRequested);
2020-12-29 15:32:56 +05:00
// check for conflicts and throw
// await this._db.conflicts.check();
// TODO test this.
// we won't be updating lastSynced time here because
// it can cause the lastSynced time to move ahead of any
// last edited (but unsynced) time resulting in edited notes
// not getting synced.
// if (serverResponse.lastSynced) {
// await this._db.storage.write("lastSynced", serverResponse.lastSynced);
// }
}
2021-07-03 14:08:36 +05:00
async _send(data, token) {
2020-12-16 12:06:25 +05:00
let response = await http.post.json(
`${Constants.API_HOST}/sync`,
data,
token
);
return response.lastSynced;
2020-04-09 16:36:57 +05:00
}
2021-09-20 12:10:36 +05:00
async _uploadAttachments() {
2021-09-20 12:10:36 +05:00
const attachments = this._db.attachments.pending;
console.log("Uploading attachments", this._db.attachments.pending);
for (var i = 0; i < attachments.length; ++i) {
sendAttachmentsProgressEvent("upload", attachments.length, i);
const attachment = attachments[i];
2021-09-20 12:10:36 +05:00
const { hash } = attachment.metadata;
const isUploaded = await this._db.fs.uploadFile(hash, hash);
if (!isUploaded) throw new Error("Failed to upload file.");
2021-09-20 12:10:36 +05:00
await this._db.attachments.markAsUploaded(attachment.id);
}
sendAttachmentsProgressEvent("upload", attachments.length);
2021-09-20 12:10:36 +05:00
}
2020-04-09 16:36:57 +05:00
}