mirror of
https://github.com/streetwriters/notesnook.git
synced 2025-12-23 23:19:40 +01:00
feat: implement database backup
This commit is contained in:
@@ -23,6 +23,9 @@ function remove(key) {
|
|||||||
function clear() {
|
function clear() {
|
||||||
storage = {};
|
storage = {};
|
||||||
}
|
}
|
||||||
|
function getAllKeys() {
|
||||||
|
return Object.keys(storage);
|
||||||
|
}
|
||||||
|
|
||||||
function encrypt(password, data) {
|
function encrypt(password, data) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
@@ -39,7 +42,7 @@ function encrypt(password, data) {
|
|||||||
|
|
||||||
function decrypt(key, data) {
|
function decrypt(key, data) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
if (key.password === data.key.password) resolve(data.cipher);
|
if (!key || key.password === data.key.password) resolve(data.cipher);
|
||||||
else reject("Wrong password");
|
else reject("Wrong password");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -59,4 +62,5 @@ module.exports = {
|
|||||||
encrypt,
|
encrypt,
|
||||||
decrypt,
|
decrypt,
|
||||||
deriveKey,
|
deriveKey,
|
||||||
|
getAllKeys,
|
||||||
};
|
};
|
||||||
|
|||||||
51
packages/core/__tests__/backup.test.js
Normal file
51
packages/core/__tests__/backup.test.js
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
import {
|
||||||
|
StorageInterface,
|
||||||
|
databaseTest,
|
||||||
|
noteTest,
|
||||||
|
groupedTest,
|
||||||
|
LONG_TEXT,
|
||||||
|
TEST_NOTE,
|
||||||
|
TEST_NOTEBOOK,
|
||||||
|
notebookTest,
|
||||||
|
TEST_NOTEBOOK2,
|
||||||
|
} from "./utils";
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
StorageInterface.clear();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("export backup", () =>
|
||||||
|
noteTest().then(() =>
|
||||||
|
notebookTest().then(async ({ db }) => {
|
||||||
|
const exp = await db.backup.export();
|
||||||
|
expect(JSON.parse(exp).t).toBeGreaterThan(0);
|
||||||
|
})
|
||||||
|
));
|
||||||
|
|
||||||
|
test("export encrypted backup", () =>
|
||||||
|
noteTest().then(() =>
|
||||||
|
notebookTest().then(async ({ db }) => {
|
||||||
|
const exp = await db.backup.export(true);
|
||||||
|
expect(JSON.parse(exp).iv).toBe("some iv");
|
||||||
|
})
|
||||||
|
));
|
||||||
|
|
||||||
|
test("import backup", () =>
|
||||||
|
noteTest().then(() =>
|
||||||
|
notebookTest().then(async ({ db, id }) => {
|
||||||
|
const exp = await db.backup.export();
|
||||||
|
await db.context.clear();
|
||||||
|
await db.backup.import(exp);
|
||||||
|
expect(db.notebooks.notebook(id).data.id).toBe(id);
|
||||||
|
})
|
||||||
|
));
|
||||||
|
|
||||||
|
test("import encrypted backup", () =>
|
||||||
|
noteTest().then(() =>
|
||||||
|
notebookTest().then(async ({ db, id }) => {
|
||||||
|
const exp = await db.backup.export(true);
|
||||||
|
await db.context.clear();
|
||||||
|
await db.backup.import(exp);
|
||||||
|
expect(db.notebooks.notebook(id).data.id).toBe(id);
|
||||||
|
})
|
||||||
|
));
|
||||||
@@ -8,6 +8,7 @@ import Sync from "./sync";
|
|||||||
import Vault from "./vault";
|
import Vault from "./vault";
|
||||||
import Lookup from "./lookup";
|
import Lookup from "./lookup";
|
||||||
import Content from "../collections/content";
|
import Content from "../collections/content";
|
||||||
|
import Backup from "../database/backup";
|
||||||
import Conflicts from "./sync/conflicts";
|
import Conflicts from "./sync/conflicts";
|
||||||
import EventManager from "../utils/event-manager";
|
import EventManager from "../utils/event-manager";
|
||||||
import Session from "./session";
|
import Session from "./session";
|
||||||
@@ -44,6 +45,7 @@ class Database {
|
|||||||
this.vault = new Vault(this);
|
this.vault = new Vault(this);
|
||||||
this.conflicts = new Conflicts(this);
|
this.conflicts = new Conflicts(this);
|
||||||
this.lookup = new Lookup(this);
|
this.lookup = new Lookup(this);
|
||||||
|
this.backup = new Backup(this);
|
||||||
|
|
||||||
// collections
|
// collections
|
||||||
/** @type {Notes} */
|
/** @type {Notes} */
|
||||||
|
|||||||
34
packages/core/database/backup.js
Normal file
34
packages/core/database/backup.js
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
export default class Backup {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {import("../api/index.js").default} db
|
||||||
|
*/
|
||||||
|
constructor(db) {
|
||||||
|
this._db = db;
|
||||||
|
}
|
||||||
|
|
||||||
|
async export(encrypt = false) {
|
||||||
|
const keys = await this._db.context.getAllKeys();
|
||||||
|
const db = Object.fromEntries(await this._db.context.readMulti(keys));
|
||||||
|
if (encrypt) {
|
||||||
|
const key = await this._db.user.key();
|
||||||
|
return JSON.stringify(
|
||||||
|
await this._db.context.encrypt(key, JSON.stringify(db))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return JSON.stringify(db);
|
||||||
|
}
|
||||||
|
|
||||||
|
async import(data) {
|
||||||
|
let backup = JSON.parse(data);
|
||||||
|
//check if we have encrypted data
|
||||||
|
if (backup.salt && backup.iv) {
|
||||||
|
const key = await this._db.user.key();
|
||||||
|
backup = JSON.parse(await this._db.context.decrypt(key, backup));
|
||||||
|
}
|
||||||
|
for (let key in backup) {
|
||||||
|
let value = backup[key];
|
||||||
|
await this._db.context.write(key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,6 +17,9 @@ export default class Storage {
|
|||||||
remove(key) {
|
remove(key) {
|
||||||
return this.storage.remove(key);
|
return this.storage.remove(key);
|
||||||
}
|
}
|
||||||
|
getAllKeys() {
|
||||||
|
return this.storage.getAllKeys();
|
||||||
|
}
|
||||||
encrypt(password, data) {
|
encrypt(password, data) {
|
||||||
return this.storage.encrypt(password, data);
|
return this.storage.encrypt(password, data);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user