2020-04-09 16:36:57 +05:00
|
|
|
/**
|
|
|
|
|
* GENERAL PROCESS:
|
2020-04-16 03:04:44 +05:00
|
|
|
* 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:
|
2020-04-16 03:04:44 +05:00
|
|
|
* 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
|
2020-04-16 03:04:44 +05:00
|
|
|
* 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.
|
|
|
|
|
*/
|
2020-09-19 11:46:36 +05:00
|
|
|
import Constants from "../../utils/constants";
|
2020-04-16 03:04:44 +05:00
|
|
|
import Collector from "./collector";
|
2020-04-09 16:36:57 +05:00
|
|
|
import Merger from "./merger";
|
|
|
|
|
var tfun = require("transfun/transfun.js").tfun;
|
|
|
|
|
if (!tfun) {
|
|
|
|
|
tfun = global.tfun;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export default class Sync {
|
|
|
|
|
/**
|
|
|
|
|
*
|
2020-04-16 03:04:44 +05:00
|
|
|
* @param {import("../index").default} db
|
2020-04-09 16:36:57 +05:00
|
|
|
*/
|
|
|
|
|
constructor(db) {
|
2020-04-16 03:04:44 +05:00
|
|
|
this._db = db;
|
|
|
|
|
this._collector = new Collector(this._db);
|
|
|
|
|
this._merger = new Merger(this._db);
|
2020-04-09 16:36:57 +05:00
|
|
|
}
|
|
|
|
|
|
2020-04-16 03:04:44 +05:00
|
|
|
async _fetch(lastSynced, token) {
|
2020-09-19 11:46:36 +05:00
|
|
|
let response = await fetch(`${Constants.HOST}/sync?lst=${lastSynced}`, {
|
|
|
|
|
headers: { ...Constants.HEADERS, Authorization: `Bearer ${token}` },
|
2020-04-09 16:36:57 +05:00
|
|
|
});
|
|
|
|
|
return await response.json();
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-24 13:07:16 +05:00
|
|
|
async _performChecks() {
|
2020-04-16 03:04:44 +05:00
|
|
|
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
|
|
|
|
2020-04-16 03:04:44 +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-10-28 12:11:18 +05:00
|
|
|
let lastSynced = user.lastSynced || 0;
|
2020-08-24 13:07:16 +05:00
|
|
|
|
|
|
|
|
return { user, lastSynced, token };
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-11 20:19:28 +05:00
|
|
|
async start(full, force) {
|
2020-12-05 11:26:02 +05:00
|
|
|
let { lastSynced, token } = await this._performChecks();
|
2020-08-24 13:07:16 +05:00
|
|
|
|
2020-12-07 18:26:01 +05:00
|
|
|
if (full) var 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
|
2020-12-11 20:19:28 +05:00
|
|
|
const data = await this._collector.collect(lastSynced, force);
|
2020-04-09 16:36:57 +05:00
|
|
|
|
2020-12-07 18:26:01 +05:00
|
|
|
if (full) {
|
|
|
|
|
// merge the server response
|
|
|
|
|
await this._merger.merge(serverResponse, lastSynced);
|
|
|
|
|
}
|
2020-04-16 03:04:44 +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
|
2020-04-16 03:04:44 +05:00
|
|
|
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) {
|
2020-10-28 12:16:11 +05:00
|
|
|
await this._db.user.set({ lastSynced });
|
2020-08-22 11:25:23 +05:00
|
|
|
}
|
2020-04-09 16:36:57 +05:00
|
|
|
}
|
|
|
|
|
|
2020-08-24 13:07:16 +05:00
|
|
|
async eventMerge(serverResponse) {
|
2020-12-05 11:26:02 +05:00
|
|
|
let { lastSynced } = await this._performChecks();
|
2020-08-25 08:52:12 +05:00
|
|
|
|
2020-08-24 13:07:16 +05:00
|
|
|
// merge the server response
|
|
|
|
|
await this._merger.merge(serverResponse, lastSynced);
|
|
|
|
|
|
|
|
|
|
// check for conflicts and throw
|
|
|
|
|
await this._db.conflicts.check();
|
|
|
|
|
|
|
|
|
|
// update our lastSynced time
|
2020-12-08 10:37:36 +05:00
|
|
|
if (serverResponse.lastSynced) {
|
|
|
|
|
await this._db.user.set({ lastSynced: serverResponse.lastSynced });
|
2020-08-24 13:07:16 +05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-16 03:04:44 +05:00
|
|
|
async _send(data, token) {
|
2020-09-19 11:46:36 +05:00
|
|
|
let response = await fetch(`${Constants.HOST}/sync`, {
|
2020-04-09 16:36:57 +05:00
|
|
|
method: "POST",
|
2020-09-19 11:46:36 +05:00
|
|
|
headers: { ...Constants.HEADERS, Authorization: `Bearer ${token}` },
|
2020-04-09 16:36:57 +05:00
|
|
|
body: JSON.stringify(data),
|
|
|
|
|
});
|
2020-04-11 11:17:58 +05:00
|
|
|
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
|
|
|
}
|
|
|
|
|
}
|