2020-02-11 16:28:28 +05:00
|
|
|
/**
|
|
|
|
|
* GENERAL PROCESS:
|
|
|
|
|
* make a get request to server with current lastSyncedTimestamp
|
|
|
|
|
* 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 lastSyncedTimestamp
|
|
|
|
|
* 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 lastSyncedTimestamp
|
|
|
|
|
* Try to merge items that were edited after the lastSyncedTimestamp
|
|
|
|
|
* 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 Database from "./index";
|
|
|
|
|
import { HOST } from "../utils/constants";
|
|
|
|
|
import fetch from "node-fetch";
|
|
|
|
|
|
|
|
|
|
export default class Sync {
|
|
|
|
|
/**
|
|
|
|
|
*
|
|
|
|
|
* @param {Database} db
|
|
|
|
|
*/
|
|
|
|
|
constructor(db) {
|
|
|
|
|
this.db = db;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async _fetch(lastSyncedTimestamp) {
|
|
|
|
|
let token = await this.db.user.token();
|
|
|
|
|
if (!token) return;
|
|
|
|
|
let response = await fetch(`${HOST}sync?lst=${lastSyncedTimestamp}`, {
|
|
|
|
|
headers: { ...HEADERS, Authorization: `Bearer ${token}` }
|
|
|
|
|
});
|
|
|
|
|
//TODO decrypt the response.
|
|
|
|
|
return await response.json();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async start() {
|
|
|
|
|
let user = await this.db.user.get();
|
|
|
|
|
if (!user) return false;
|
|
|
|
|
let lastSyncedTimestamp = user.lastSyncedTimestamp || 0;
|
|
|
|
|
let serverResponse = await this._fetch(lastSyncedTimestamp);
|
2020-02-12 00:52:17 +05:00
|
|
|
let data = this._merge({ serverResponse, lastSyncedTimestamp, user });
|
|
|
|
|
await this.db.user.set({ lastSyncedTimestamp: data.lastSynced });
|
2020-02-11 16:28:28 +05:00
|
|
|
await this._send(data);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-12 00:52:17 +05:00
|
|
|
_merge({ serverResponse, lastSyncedTimestamp, user }) {
|
2020-02-11 16:28:28 +05:00
|
|
|
const { notes, notebooks /* tags, colors, trash */ } = serverResponse;
|
|
|
|
|
notes.forEach(async note => {
|
2020-02-12 00:52:17 +05:00
|
|
|
note = JSON.parse(note.data);
|
2020-02-11 16:28:28 +05:00
|
|
|
let localNote = this.db.notes.note(note.id);
|
|
|
|
|
if (!localNote || note.dateEdited > localNote.data.dateEdited) {
|
|
|
|
|
await this.db.notes.add({ ...note, remote: true });
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
notebooks.forEach(async nb => {
|
2020-02-12 00:52:17 +05:00
|
|
|
nb = JSON.parse(nb.data);
|
2020-02-11 16:28:28 +05:00
|
|
|
let localNb = this.db.notebooks.notebook(nb.id);
|
|
|
|
|
if (!localNb || nb.dateEdited > localNb.data.dateEdited) {
|
|
|
|
|
await this.db.notebooks.add({ ...nb, remote: true });
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
// TODO trash, colors, tags
|
|
|
|
|
return {
|
2020-02-12 00:52:17 +05:00
|
|
|
notes: this.db.notes
|
|
|
|
|
.filter(v => v.dateEdited > lastSyncedTimestamp)
|
|
|
|
|
.map(v => ({
|
|
|
|
|
dateEdited: v.dateEdited,
|
|
|
|
|
dateCreated: v.dateCreated,
|
|
|
|
|
data: JSON.stringify(v),
|
|
|
|
|
userId: user.Id
|
|
|
|
|
})),
|
|
|
|
|
notebooks: this.db.notebooks
|
|
|
|
|
.filter(v => v.dateEdited > lastSyncedTimestamp)
|
|
|
|
|
.map(v => ({
|
|
|
|
|
dateEdited: v.dateEdited,
|
|
|
|
|
dateCreated: v.dateCreated,
|
|
|
|
|
data: JSON.stringify(v),
|
|
|
|
|
userId: user.Id
|
|
|
|
|
})),
|
2020-02-11 16:28:28 +05:00
|
|
|
tags: [],
|
|
|
|
|
colors: [],
|
2020-02-12 00:52:17 +05:00
|
|
|
tags: [],
|
|
|
|
|
lastSynced: Date.now()
|
2020-02-11 16:28:28 +05:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async _send(data) {
|
|
|
|
|
//TODO encrypt the payload
|
|
|
|
|
let response = await fetch(`${HOST}sync`, {
|
|
|
|
|
method: "POST",
|
|
|
|
|
headers: { ...HEADERS, Authorization: `Bearer ${token}` },
|
|
|
|
|
body: JSON.stringify(data)
|
|
|
|
|
});
|
|
|
|
|
return response.ok;
|
|
|
|
|
}
|
|
|
|
|
}
|