Add an API interface for performing DB migrations as required

This commit is contained in:
Oliver-Akins 2025-05-29 01:22:17 -06:00
parent 2567f5fb62
commit 1e007af52a
6 changed files with 83 additions and 0 deletions

View file

@ -11,6 +11,38 @@ Hooks.on(`ready`, () => {
CONFIG.stats.db = NilDatabase;
};
/*
Perform any required data migration if any is required for the version
jump that the user may have caused. This only migrates the data iff the
currently authenticated user is able to perform the full migration of
data.
*/
const db = CONFIG.stats.db;
const lastVersion = game.settings.get(__ID__, `lastVersion`);
const canDoMigration = db.canPerformMigration();
const requiresMigration = db.requiresMigrationFrom(lastVersion);
if (requiresMigration) {
if (canDoMigration) {
const notif = ui.notifications.info(
`${__TITLE__} | Performing data migration, please do not close the window`,
{ progress: true, permanent: true },
);
// Fire and forget
CONFIG.stats.db.migrateData(notif)
.then(() => {
game.settings.set(__ID__, __VERSION__);
setTimeout(() => ui.notifications.remove(notif), 500);
});
} else {
ui.notifications.error(
`The stat-tracker database is out of date, temporarily disabling the stat-tracker module's functionality until the migration can be performed by a GM user logging into the world.`,
{ console: false, permanent: true },
);
CONFIG.stats.db = NilDatabase;
};
};
/*
Prevent any run-time modifications to the CONFIG API so that users can't wreck
themselves nor their data by fooling around with the values.

View file

@ -23,4 +23,11 @@ export function registerMetaSettings() {
config: false,
requiresReload: false,
});
game.settings.register(__ID__, `lastVersion`, {
scope: `world`,
type: String,
config: false,
requiresReload: false,
});
};

View file

@ -220,6 +220,43 @@ export class Database {
static async triggerListeners() {};
static async unregisterListeners() {};
// MARK: Migrations
/**
* Determines if the currently authenticated user is capable of running
* the full migration on their own.
*
* @returns {boolean}
*/
static async canPerformMigration() {
// TODO: this *must* account for isActiveGM, because otherwise the
// world setting cannot be updated after the migration finishes.
return game.user.isActiveGM;
};
/**
* Determines if the previous version of the plugin that was active
* needs to be migrated in order to work with the new version.
*
* @param {string} lastVersion The version that was last active
* @returns {boolean}
*/
static async requiresMigrationFrom(lastVersion) {
return foundry.utils.isNewerVersion(__VERSION__, lastVersion);
};
/**
* This method migrates ALL of the database data from one version of
* the module to the currently installed module. This is not guaranteed
* to run only on one client, so it should be made to be either
* idempotent, or have an operation locking mechanism that can prevent
* other clients from executing it if there's a migration in-progress.
*
* @param {string} lastVersion The last version that the user had active
* @param {Notification} notif The progress bar notification used for
* user feedback while performing migrations.
*/
static async migrateData(lastVersion, notif) {};
};
/* eslint-enable no-unused-vars */

View file

@ -31,4 +31,9 @@ export class NilDatabase extends Database {
static async registerListeners() {};
static async triggerListeners() {};
static async unregisterListeners() {};
// MARK: Migrations
static async canPerformMigration() { return true };
static async requiresMigrationFrom() { return false };
static async migrateData() {};
};