mirror of
https://github.com/TriliumNext/Notes.git
synced 2025-07-29 11:02:28 +08:00

This fixes TypeScript migrations, which were not being run correctly even in previous versions. On the ESBuild version, neither SQL migrations worked due to the fact that they were not being bundled.
137 lines
4.3 KiB
TypeScript
137 lines
4.3 KiB
TypeScript
import backupService from "./backup.js";
|
|
import sql from "./sql.js";
|
|
import log from "./log.js";
|
|
import { crash } from "./utils.js";
|
|
import appInfo from "./app_info.js";
|
|
import cls from "./cls.js";
|
|
import { t } from "i18next";
|
|
import MIGRATIONS from "../migrations/migrations.js";
|
|
|
|
interface MigrationInfo {
|
|
dbVersion: number;
|
|
/**
|
|
* If string, then the migration is an SQL script that will be executed.
|
|
* If a function, then the migration is a JavaScript/TypeScript module that will be executed.
|
|
*/
|
|
migration: string | (() => void);
|
|
}
|
|
|
|
async function migrate() {
|
|
const currentDbVersion = getDbVersion();
|
|
|
|
if (currentDbVersion < 214) {
|
|
await crash(t("migration.old_version"));
|
|
return;
|
|
}
|
|
|
|
// backup before attempting migration
|
|
await backupService.backupNow(
|
|
// creating a special backup for version 0.60.4, the changes in 0.61 are major.
|
|
currentDbVersion === 214 ? `before-migration-v060` : "before-migration"
|
|
);
|
|
|
|
const migrations = await prepareMigrations(currentDbVersion);
|
|
|
|
// all migrations are executed in one transaction - upgrade either succeeds, or the user can stay at the old version
|
|
// otherwise if half of the migrations succeed, user can't use any version - DB is too "new" for the old app,
|
|
// and too old for the new app version.
|
|
|
|
cls.setMigrationRunning(true);
|
|
|
|
sql.transactional(() => {
|
|
for (const mig of migrations) {
|
|
try {
|
|
log.info(`Attempting migration to version ${mig.dbVersion}`);
|
|
|
|
executeMigration(mig);
|
|
|
|
sql.execute(
|
|
/*sql*/`UPDATE options
|
|
SET value = ?
|
|
WHERE name = ?`,
|
|
[mig.dbVersion.toString(), "dbVersion"]
|
|
);
|
|
|
|
log.info(`Migration to version ${mig.dbVersion} has been successful.`);
|
|
} catch (e: any) {
|
|
console.error(e);
|
|
crash(t("migration.error_message", { version: mig.dbVersion, stack: e.stack }));
|
|
break; // crash() is sometimes async
|
|
}
|
|
}
|
|
});
|
|
|
|
if (currentDbVersion === 214) {
|
|
// special VACUUM after the big migration
|
|
log.info("VACUUMing database, this might take a while ...");
|
|
sql.execute("VACUUM");
|
|
}
|
|
}
|
|
|
|
async function prepareMigrations(currentDbVersion: number): Promise<MigrationInfo[]> {
|
|
MIGRATIONS.sort((a, b) => a.version - b.version);
|
|
const migrations: MigrationInfo[] = [];
|
|
for (const migration of MIGRATIONS) {
|
|
const dbVersion = migration.version;
|
|
if (dbVersion > currentDbVersion) {
|
|
if ("sql" in migration) {
|
|
migrations.push({
|
|
dbVersion,
|
|
migration: migration.sql
|
|
});
|
|
} else {
|
|
// Due to ESM imports, the migration file needs to be imported asynchronously and thus cannot be loaded at migration time (since migration is not asynchronous).
|
|
// As such we have to preload the ESM.
|
|
migrations.push({
|
|
dbVersion,
|
|
migration: (await migration.module()).default
|
|
});
|
|
}
|
|
}
|
|
}
|
|
return migrations;
|
|
}
|
|
|
|
function executeMigration({ migration }: MigrationInfo) {
|
|
if (typeof migration === "string") {
|
|
console.log(`Migration with SQL script: ${migration}`);
|
|
sql.executeScript(migration);
|
|
} else {
|
|
console.log("Migration with JS module");
|
|
migration();
|
|
};
|
|
}
|
|
|
|
function getDbVersion() {
|
|
return parseInt(sql.getValue("SELECT value FROM options WHERE name = 'dbVersion'"));
|
|
}
|
|
|
|
function isDbUpToDate() {
|
|
const dbVersion = getDbVersion();
|
|
|
|
const upToDate = dbVersion >= appInfo.dbVersion;
|
|
|
|
if (!upToDate) {
|
|
log.info(`App db version is ${appInfo.dbVersion}, while db version is ${dbVersion}. Migration needed.`);
|
|
}
|
|
|
|
return upToDate;
|
|
}
|
|
|
|
async function migrateIfNecessary() {
|
|
const currentDbVersion = getDbVersion();
|
|
|
|
if (currentDbVersion > appInfo.dbVersion && process.env.TRILIUM_IGNORE_DB_VERSION !== "true") {
|
|
await crash(t("migration.wrong_db_version", { version: currentDbVersion, targetVersion: appInfo.dbVersion }));
|
|
}
|
|
|
|
if (!isDbUpToDate()) {
|
|
await migrate();
|
|
}
|
|
}
|
|
|
|
export default {
|
|
migrateIfNecessary,
|
|
isDbUpToDate
|
|
};
|