2020-10-03 11:59:20 +05:00
|
|
|
import Hashes from "jshashes";
|
|
|
|
|
const sha256 = new Hashes.SHA256();
|
|
|
|
|
|
|
|
|
|
const invalidKeys = ["user", "t"];
|
|
|
|
|
const validTypes = ["mobile", "web", "node"];
|
2020-09-13 13:24:24 +05:00
|
|
|
export default class Backup {
|
|
|
|
|
/**
|
|
|
|
|
*
|
|
|
|
|
* @param {import("../api/index.js").default} db
|
|
|
|
|
*/
|
|
|
|
|
constructor(db) {
|
|
|
|
|
this._db = db;
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-03 11:59:20 +05:00
|
|
|
/**
|
|
|
|
|
*
|
|
|
|
|
* @param {"web"|"mobile"|"node"} type
|
|
|
|
|
* @param {boolean} encrypt
|
|
|
|
|
*/
|
|
|
|
|
async export(type, encrypt = false) {
|
|
|
|
|
if (!validTypes.some((t) => t === type))
|
|
|
|
|
throw new Error("Invalid type. It must be one of 'mobile' or 'web'.");
|
|
|
|
|
|
|
|
|
|
const keys = (await this._db.context.getAllKeys()).filter(
|
|
|
|
|
(key) => !(key in invalidKeys)
|
|
|
|
|
);
|
|
|
|
|
|
2020-09-13 13:24:24 +05:00
|
|
|
const db = Object.fromEntries(await this._db.context.readMulti(keys));
|
2020-10-03 11:59:20 +05:00
|
|
|
db.h = sha256.hex(JSON.stringify(db));
|
|
|
|
|
|
2020-09-13 13:24:24 +05:00
|
|
|
if (encrypt) {
|
|
|
|
|
const key = await this._db.user.key();
|
2020-10-03 11:59:20 +05:00
|
|
|
return JSON.stringify({
|
|
|
|
|
type,
|
|
|
|
|
date: Date.now(),
|
|
|
|
|
data: await this._db.context.encrypt(key, JSON.stringify(db)),
|
|
|
|
|
});
|
2020-09-13 13:24:24 +05:00
|
|
|
}
|
2020-10-03 11:59:20 +05:00
|
|
|
|
|
|
|
|
return JSON.stringify({
|
|
|
|
|
type,
|
|
|
|
|
date: Date.now(),
|
|
|
|
|
data: db,
|
|
|
|
|
});
|
2020-09-13 13:24:24 +05:00
|
|
|
}
|
|
|
|
|
|
2020-10-03 11:59:20 +05:00
|
|
|
/**
|
|
|
|
|
*
|
|
|
|
|
* @param {string} data the backup data
|
|
|
|
|
*/
|
2020-09-13 13:24:24 +05:00
|
|
|
async import(data) {
|
2020-10-03 11:59:20 +05:00
|
|
|
if (!data) return;
|
|
|
|
|
|
2020-09-13 13:24:24 +05:00
|
|
|
let backup = JSON.parse(data);
|
2020-10-03 11:59:20 +05:00
|
|
|
|
|
|
|
|
if (!this._validate(backup)) throw new Error("Invalid backup.");
|
|
|
|
|
|
|
|
|
|
let db = backup.data;
|
2020-09-13 13:24:24 +05:00
|
|
|
//check if we have encrypted data
|
2020-10-03 11:59:20 +05:00
|
|
|
if (db.salt && db.iv) {
|
2020-09-13 13:24:24 +05:00
|
|
|
const key = await this._db.user.key();
|
2020-10-03 11:59:20 +05:00
|
|
|
db = JSON.parse(await this._db.context.decrypt(key, db));
|
2020-09-13 13:24:24 +05:00
|
|
|
}
|
2020-10-03 11:59:20 +05:00
|
|
|
|
|
|
|
|
if (!this._verify(db))
|
|
|
|
|
throw new Error("Backup file has been tempered, aborting...");
|
|
|
|
|
|
|
|
|
|
for (let key in db) {
|
|
|
|
|
let value = db[key];
|
2020-09-13 13:24:24 +05:00
|
|
|
await this._db.context.write(key, value);
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-10-03 11:59:20 +05:00
|
|
|
|
|
|
|
|
_validate(backup) {
|
|
|
|
|
return (
|
|
|
|
|
!!backup.date &&
|
|
|
|
|
!!backup.data &&
|
|
|
|
|
!!backup.type &&
|
|
|
|
|
validTypes.some((t) => t === backup.type)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_verify(db) {
|
|
|
|
|
const hash = db.h;
|
|
|
|
|
delete db.h;
|
|
|
|
|
return hash == sha256.hex(JSON.stringify(db));
|
|
|
|
|
}
|
2020-09-13 13:24:24 +05:00
|
|
|
}
|