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

123 lines
3.6 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 { HOST, HEADERS } from "../../utils/constants";
import Collector from "./collector";
2020-04-09 16:36:57 +05:00
import Merger from "./merger";
import { areAllEmpty } from "./utils";
var tfun = require("transfun/transfun.js").tfun;
if (!tfun) {
tfun = global.tfun;
}
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);
2020-04-09 16:36:57 +05:00
}
async _fetch(lastSynced, token) {
let response = await fetch(`${HOST}/sync?lst=${lastSynced}`, {
2020-04-09 16:36:57 +05:00
headers: { ...HEADERS, Authorization: `Bearer ${token}` },
});
return await response.json();
}
async _performChecks() {
let user = await this._db.user.get();
let token = await this._db.user.token();
if (!user || !token) throw new Error("You need to login to sync.");
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-08-22 11:25:23 +05:00
let lastSynced = user.notesnook.lastSynced || 0;
return { user, lastSynced, token };
}
async start() {
let { user, lastSynced, token } = await this._performChecks();
let serverResponse = await this._fetch(lastSynced, token);
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);
2020-04-09 16:36:57 +05:00
// merge the server response
await this._merger.merge(serverResponse, lastSynced);
// 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);
2020-04-09 16:36:57 +05:00
// update our lastSynced time
2020-08-22 11:25:23 +05:00
if (lastSynced) {
await this._db.user.set({ notesnook: { ...user.notesnook, lastSynced } });
}
2020-04-09 16:36:57 +05:00
}
async eventMerge(serverResponse) {
let { user, lastSynced } = await this._performChecks();
// we update the last synced time before merging so there are no conflicts
lastSynced = serverResponse.lastSynced;
// merge the server response
await this._merger.merge(serverResponse, lastSynced);
// check for conflicts and throw
await this._db.conflicts.check();
// update our lastSynced time
if (lastSynced) {
await this._db.user.set({ notesnook: { ...user.notesnook, lastSynced } });
}
}
async _send(data, token) {
let response = await fetch(`${HOST}/sync`, {
2020-04-09 16:36:57 +05:00
method: "POST",
headers: { ...HEADERS, Authorization: `Bearer ${token}` },
body: JSON.stringify(data),
});
if (response.ok) {
const json = await response.json();
return json.lastSynced;
}
throw new Error("Failed to sync with the server.");
2020-04-09 16:36:57 +05:00
}
}