mirror of
https://github.com/streetwriters/notesnook.git
synced 2025-12-23 06:59:31 +01:00
refactor: use a single migration backend
This commit is contained in:
@@ -76,8 +76,8 @@ class Database {
|
|||||||
|
|
||||||
await this.settings.init();
|
await this.settings.init();
|
||||||
await this.user.sync();
|
await this.user.sync();
|
||||||
await this.migrations.init();
|
|
||||||
|
|
||||||
|
await this.migrations.init();
|
||||||
await this.migrations.migrate();
|
await this.migrations.migrate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { CURRENT_DATABASE_VERSION } from "../common";
|
import { CURRENT_DATABASE_VERSION, EV } from "../common";
|
||||||
import { migrations } from "../migrations";
|
import Migrator from "../database/migrator";
|
||||||
|
|
||||||
class Migrations {
|
class Migrations {
|
||||||
/**
|
/**
|
||||||
@@ -8,58 +8,65 @@ class Migrations {
|
|||||||
*/
|
*/
|
||||||
constructor(db) {
|
constructor(db) {
|
||||||
this._db = db;
|
this._db = db;
|
||||||
this.dbVersion = CURRENT_DATABASE_VERSION;
|
this._migrator = new Migrator();
|
||||||
}
|
}
|
||||||
|
|
||||||
async init() {
|
async init() {
|
||||||
this.dbVersion = (await this._db.context.read("v")) || 2;
|
this.dbVersion = (await this._db.context.read("v")) || 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
get _shouldMigrate() {
|
|
||||||
return this.dbVersion < CURRENT_DATABASE_VERSION;
|
|
||||||
}
|
|
||||||
|
|
||||||
_migrationFunction(collectionId) {
|
|
||||||
let migrationFunction = migrations[this.dbVersion][collectionId];
|
|
||||||
if (!migrationFunction)
|
|
||||||
migrationFunction = migrations[CURRENT_DATABASE_VERSION][collectionId];
|
|
||||||
return migrationFunction;
|
|
||||||
}
|
|
||||||
|
|
||||||
async migrate() {
|
async migrate() {
|
||||||
if (!this._shouldMigrate) return;
|
if (this.dbVersion > CURRENT_DATABASE_VERSION) return;
|
||||||
|
|
||||||
|
await this._db.notes.init();
|
||||||
|
const content = await this._db.content.all();
|
||||||
|
|
||||||
const collections = [
|
const collections = [
|
||||||
"notes",
|
{
|
||||||
"notebooks",
|
id: "notes",
|
||||||
"tags",
|
index: this._db.notes.raw,
|
||||||
"colors",
|
dbCollection: this._db.notes,
|
||||||
"trash",
|
},
|
||||||
"delta",
|
{
|
||||||
"text",
|
id: "notebooks",
|
||||||
"content",
|
index: this._db.notebooks.raw,
|
||||||
"settings",
|
dbCollection: this._db.notebooks,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "tags",
|
||||||
|
index: this._db.tags.raw,
|
||||||
|
dbCollection: this._db.tags,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "colors",
|
||||||
|
index: this._db.colors.raw,
|
||||||
|
dbCollection: this._db.colors,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "trash",
|
||||||
|
index: this._db.trash.raw,
|
||||||
|
dbCollection: this._db.trash,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "content",
|
||||||
|
index: content,
|
||||||
|
dbCollection: this._db.content,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "settings",
|
||||||
|
index: [this._db.settings.raw],
|
||||||
|
dbCollection: this._db.settings,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
await this._migrator.migrate(collections, (item) => item, this.dbVersion);
|
||||||
await Promise.all(
|
|
||||||
collections.map(async (collectionId) => {
|
|
||||||
const collection = this._db[collectionId];
|
|
||||||
if (!collection) return;
|
|
||||||
|
|
||||||
const items =
|
|
||||||
collectionId === "content" || collectionId === "delta"
|
|
||||||
? await collection.all()
|
|
||||||
: collectionId === "settings"
|
|
||||||
? [collection.raw]
|
|
||||||
: collection.raw;
|
|
||||||
|
|
||||||
await Promise.all(
|
|
||||||
items.map(async (item) => {
|
|
||||||
await this._migrationFunction(collectionId)(this._db, item);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
await this._db.context.write("v", CURRENT_DATABASE_VERSION);
|
await this._db.context.write("v", CURRENT_DATABASE_VERSION);
|
||||||
|
|
||||||
|
EV.publish("db:onMigrationDone", {
|
||||||
|
prev: this.dbVersion,
|
||||||
|
current: CURRENT_DATABASE_VERSION,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.dbVersion = CURRENT_DATABASE_VERSION;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export default Migrations;
|
export default Migrations;
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ class Settings {
|
|||||||
constructor(db) {
|
constructor(db) {
|
||||||
this._db = db;
|
this._db = db;
|
||||||
this._settings = {
|
this._settings = {
|
||||||
|
type: "settings",
|
||||||
id: id(),
|
id: id(),
|
||||||
pins: [],
|
pins: [],
|
||||||
dateEdited: 0,
|
dateEdited: 0,
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { CURRENT_DATABASE_VERSION } from "../../common";
|
||||||
import Database from "../index";
|
import Database from "../index";
|
||||||
var tfun = require("transfun/transfun.js").tfun;
|
var tfun = require("transfun/transfun.js").tfun;
|
||||||
if (!tfun) {
|
if (!tfun) {
|
||||||
@@ -43,6 +44,7 @@ class Collector {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
id: item.id,
|
id: item.id,
|
||||||
|
v: CURRENT_DATABASE_VERSION,
|
||||||
...(await this._serialize(item)),
|
...(await this._serialize(item)),
|
||||||
};
|
};
|
||||||
})(array)
|
})(array)
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import Hashes from "jshashes";
|
import Hashes from "jshashes";
|
||||||
|
import Migrator from "./migrator.js";
|
||||||
import {
|
import {
|
||||||
CHECK_IDS,
|
CHECK_IDS,
|
||||||
sendCheckUserStatusEvent,
|
sendCheckUserStatusEvent,
|
||||||
CURRENT_DATABASE_VERSION,
|
CURRENT_DATABASE_VERSION,
|
||||||
} from "../common.js";
|
} from "../common.js";
|
||||||
import { migrations } from "../migrations.js";
|
|
||||||
const md5 = new Hashes.MD5();
|
const md5 = new Hashes.MD5();
|
||||||
|
|
||||||
const invalidKeys = ["user", "t", "lastBackupTime"];
|
const invalidKeys = ["user", "t", "lastBackupTime"];
|
||||||
@@ -16,6 +16,7 @@ export default class Backup {
|
|||||||
*/
|
*/
|
||||||
constructor(db) {
|
constructor(db) {
|
||||||
this._db = db;
|
this._db = db;
|
||||||
|
this._migrator = new Migrator();
|
||||||
}
|
}
|
||||||
|
|
||||||
lastBackupTime() {
|
lastBackupTime() {
|
||||||
@@ -117,45 +118,56 @@ export default class Backup {
|
|||||||
|
|
||||||
async _migrateData(backup) {
|
async _migrateData(backup) {
|
||||||
const { data, version = 0 } = backup;
|
const { data, version = 0 } = backup;
|
||||||
|
|
||||||
if (version > CURRENT_DATABASE_VERSION)
|
if (version > CURRENT_DATABASE_VERSION)
|
||||||
throw new Error(
|
throw new Error(
|
||||||
"This backup was made from a newer version of Notesnook. Cannot migrate."
|
"This backup was made from a newer version of Notesnook. Cannot migrate."
|
||||||
);
|
);
|
||||||
|
|
||||||
const collections = [
|
const collections = [
|
||||||
"notes",
|
{
|
||||||
"notebooks",
|
id: "notes",
|
||||||
"tags",
|
index: data["notes"],
|
||||||
"colors",
|
dbCollection: this._db.notes,
|
||||||
"trash",
|
},
|
||||||
"delta",
|
{
|
||||||
"text",
|
id: "notebooks",
|
||||||
"content",
|
index: data["notebooks"],
|
||||||
"settings",
|
dbCollection: this._db.notebooks,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "tags",
|
||||||
|
index: data["tags"],
|
||||||
|
dbCollection: this._db.tags,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "colors",
|
||||||
|
index: data["colors"],
|
||||||
|
dbCollection: this._db.colors,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "trash",
|
||||||
|
index: data["trash"],
|
||||||
|
dbCollection: this._db.trash,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "delta",
|
||||||
|
index: data["delta"],
|
||||||
|
dbCollection: this._db.content,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "content",
|
||||||
|
index: data["content"],
|
||||||
|
dbCollection: this._db.content,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "settings",
|
||||||
|
index: ["settings"],
|
||||||
|
dbCollection: this._db.settings,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
await Promise.all(
|
await this._migrator.migrate(collections, (id) => data[id], version);
|
||||||
collections.map(async (collectionId) => {
|
|
||||||
let collection = data[collectionId];
|
|
||||||
if (!collection) return;
|
|
||||||
|
|
||||||
if (!Array.isArray(collection)) {
|
|
||||||
collection = [collectionId];
|
|
||||||
}
|
|
||||||
|
|
||||||
await Promise.all(
|
|
||||||
collection.map(async (id) => {
|
|
||||||
const item = data[id];
|
|
||||||
if (!item) return;
|
|
||||||
let migrationFunction = migrations[version][collectionId];
|
|
||||||
if (!migrationFunction)
|
|
||||||
migrationFunction =
|
|
||||||
migrations[CURRENT_DATABASE_VERSION][collectionId];
|
|
||||||
await migrationFunction(this._db, item);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_validate(backup) {
|
_validate(backup) {
|
||||||
|
|||||||
31
packages/core/database/migrator.js
Normal file
31
packages/core/database/migrator.js
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import { CURRENT_DATABASE_VERSION } from "../common";
|
||||||
|
import { migrations } from "../migrations";
|
||||||
|
|
||||||
|
class Migrator {
|
||||||
|
async migrate(collections, get, version) {
|
||||||
|
await Promise.all(
|
||||||
|
collections.map(async (collection) => {
|
||||||
|
if (!collection.index || !collection.dbCollection) return;
|
||||||
|
|
||||||
|
await Promise.all(
|
||||||
|
collection.index.map(async (id) => {
|
||||||
|
let item = get(id);
|
||||||
|
if (!item) return;
|
||||||
|
if (item.deleted)
|
||||||
|
return await collection.dbCollection._collection.addItem(item);
|
||||||
|
|
||||||
|
const migrate = migrations[version][item.type || collection.id];
|
||||||
|
if (migrate) item = migrate(item);
|
||||||
|
if (!!collection.dbCollection.merge) {
|
||||||
|
await collection.dbCollection.merge(item);
|
||||||
|
} else {
|
||||||
|
await collection.dbCollection.add(item);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export default Migrator;
|
||||||
@@ -1,15 +1,6 @@
|
|||||||
export const migrations = {
|
export const migrations = {
|
||||||
handleDeleted: async function (db, collection, item) {
|
|
||||||
if (item.deleted) {
|
|
||||||
await db[collection]._collection.addItem(item);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
0: {
|
0: {
|
||||||
notes: async function (db, item) {
|
note: function (item) {
|
||||||
if (await migrations.handleDeleted(db, "notes", item)) return;
|
|
||||||
|
|
||||||
const contentId = item.content.delta;
|
const contentId = item.content.delta;
|
||||||
const notebook = item.notebook;
|
const notebook = item.notebook;
|
||||||
delete item.content;
|
delete item.content;
|
||||||
@@ -17,69 +8,43 @@ export const migrations = {
|
|||||||
item.contentId = contentId;
|
item.contentId = contentId;
|
||||||
item.remote = true;
|
item.remote = true;
|
||||||
if (notebook) item.notebooks = [notebook];
|
if (notebook) item.notebooks = [notebook];
|
||||||
await db.notes.add(item);
|
return item;
|
||||||
},
|
},
|
||||||
delta: async function (db, item) {
|
delta: function (item) {
|
||||||
if (await migrations.handleDeleted(db, "content", item)) return;
|
|
||||||
|
|
||||||
item.data = item.data.ops;
|
item.data = item.data.ops;
|
||||||
item.type = "delta";
|
item.type = "delta";
|
||||||
await db.content.add(item);
|
return item;
|
||||||
},
|
},
|
||||||
trash: async function (db, item) {
|
trash: function (item) {
|
||||||
if (await migrations.handleDeleted(db, "trash", item)) return;
|
|
||||||
|
|
||||||
item.itemType = item.type;
|
item.itemType = item.type;
|
||||||
item.type = "trash";
|
item.type = "trash";
|
||||||
if (item.itemType === "note") {
|
if (item.itemType === "note") {
|
||||||
item.contentId = item.content.delta;
|
item.contentId = item.content.delta;
|
||||||
delete item.content;
|
delete item.content;
|
||||||
}
|
}
|
||||||
await db.trash.add(item);
|
return item;
|
||||||
},
|
},
|
||||||
text: function () {},
|
text: function () {},
|
||||||
},
|
},
|
||||||
2: {
|
2: {
|
||||||
notes: async function (db, item) {
|
note: function (item) {
|
||||||
if (await migrations.handleDeleted(db, "notes", item)) return;
|
|
||||||
|
|
||||||
// notebook -> notebooks
|
// notebook -> notebooks
|
||||||
const notebook = item.notebook;
|
const notebook = item.notebook;
|
||||||
delete item.notebook;
|
delete item.notebook;
|
||||||
item.remote = true;
|
item.remote = true;
|
||||||
if (notebook) item.notebooks = [notebook];
|
if (notebook) item.notebooks = [notebook];
|
||||||
|
return item;
|
||||||
await db.notes.add({ ...item, remote: true });
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
3: {
|
3: {
|
||||||
notes: async function (db, item) {
|
note: false,
|
||||||
if (await migrations.handleDeleted(db, "notes", item)) return;
|
notebooks: function (item) {
|
||||||
await db.notes.add({ ...item, remote: true });
|
|
||||||
},
|
|
||||||
notebooks: async function (db, item) {
|
|
||||||
if (await migrations.handleDeleted(db, "notebooks", item)) return;
|
|
||||||
if (item.favorite !== undefined) delete item.favorite;
|
if (item.favorite !== undefined) delete item.favorite;
|
||||||
await db.notebooks.add(item);
|
return item;
|
||||||
},
|
|
||||||
tags: async function (db, item) {
|
|
||||||
if (await migrations.handleDeleted(db, "tags", item)) return;
|
|
||||||
await db.tags.merge(item);
|
|
||||||
},
|
|
||||||
colors: async function (db, item) {
|
|
||||||
if (await migrations.handleDeleted(db, "colors", item)) return;
|
|
||||||
await db.colors.merge(item);
|
|
||||||
},
|
|
||||||
trash: async function (db, item) {
|
|
||||||
if (await migrations.handleDeleted(db, "trash", item)) return;
|
|
||||||
await db.trash.add(item);
|
|
||||||
},
|
|
||||||
content: async function (db, item) {
|
|
||||||
if (await migrations.handleDeleted(db, "content", item)) return;
|
|
||||||
await db.content.add(item);
|
|
||||||
},
|
|
||||||
settings: async function (db, item) {
|
|
||||||
db.settings.merge(item);
|
|
||||||
},
|
},
|
||||||
|
tag: false,
|
||||||
|
trash: false,
|
||||||
|
content: false,
|
||||||
|
settings: false,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user