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

120 lines
3.6 KiB
JavaScript
Raw Normal View History

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";
2020-02-12 02:09:08 +05:00
import { HOST,HEADERS } from "../utils/constants";
2020-02-11 16:28:28 +05:00
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);
let data = this._merge({ serverResponse, lastSyncedTimestamp, user });
2020-02-12 02:09:08 +05:00
await this.db.user.set({ lastSynced: data.lastSynced });
2020-02-11 16:28:28 +05:00
await this._send(data);
return true;
}
_merge({ serverResponse, lastSyncedTimestamp, user }) {
2020-02-11 16:28:28 +05:00
const { notes, notebooks /* tags, colors, trash */ } = serverResponse;
2020-02-12 02:09:08 +05:00
2020-02-11 16:28:28 +05:00
notes.forEach(async note => {
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 => {
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 {
notes: this.db.notes
2020-02-12 02:09:08 +05:00
.all
.filter(v => v.dateEdited > lastSyncedTimestamp)
.map(v => ({
dateEdited: v.dateEdited,
dateCreated: v.dateCreated,
data: JSON.stringify(v),
userId: user.Id
})),
notebooks: this.db.notebooks
2020-02-12 02:09:08 +05:00
.all
.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: [],
tags: [],
lastSynced: Date.now()
2020-02-11 16:28:28 +05:00
};
}
async _send(data) {
2020-02-12 02:09:08 +05:00
2020-02-11 16:28:28 +05:00
//TODO encrypt the payload
2020-02-12 02:09:08 +05:00
let token = await this.db.user.token();
if (!token) return;
2020-02-11 16:28:28 +05:00
let response = await fetch(`${HOST}sync`, {
method: "POST",
headers: { ...HEADERS, Authorization: `Bearer ${token}` },
body: JSON.stringify(data)
});
return response.ok;
}
}