@directus/api
Version:
Directus is a real-time API and App dashboard for managing SQL database content
88 lines (86 loc) • 4.27 kB
JavaScript
import { useLogger } from "../../logger/index.js";
import { flushCaches } from "../../cache.js";
import getModuleDefault from "../../utils/get-module-default.js";
import path from "path";
import { useEnv } from "@directus/env";
import { orderBy } from "lodash-es";
import { fileURLToPath } from "node:url";
import { dirname } from "node:path";
import fse from "fs-extra";
import formatTitle from "@directus/format-title";
//#region src/database/migrations/run.ts
const __dirname = dirname(fileURLToPath(import.meta.url));
async function run(database, direction, log = true) {
const env = useEnv();
const logger = useLogger();
let migrationFiles = await fse.readdir(__dirname);
const customMigrationsPath = path.resolve(env["MIGRATIONS_PATH"]);
let customMigrationFiles = await fse.pathExists(customMigrationsPath) && await fse.readdir(customMigrationsPath) || [];
migrationFiles = migrationFiles.filter((file) => /^[0-9]+[A-Z]-[^.]+\.(?:js|ts)$/.test(file));
customMigrationFiles = customMigrationFiles.filter((file) => file.includes("-") && /\.(c|m)?js$/.test(file));
const completedMigrations = await database.select("*").from("directus_migrations").orderBy("version");
const migrations = [...migrationFiles.map((path$2) => parseFilePath(path$2)), ...customMigrationFiles.map((path$2) => parseFilePath(path$2, true))].sort((a, b) => a.version > b.version ? 1 : -1);
const migrationKeys = new Set(migrations.map((m) => m.version));
if (migrations.length > migrationKeys.size) throw new Error("Migration keys collide! Please ensure that every migration uses a unique key.");
function parseFilePath(filePath, custom = false) {
const version = filePath.split("-")[0];
const name = formatTitle(filePath.split("-").slice(1).join("_").split(".")[0]);
const completed = !!completedMigrations.find((migration) => migration.version === version);
return {
file: custom ? path.join(customMigrationsPath, filePath) : path.join(__dirname, filePath),
version,
name,
completed
};
}
if (direction === "up") await up();
if (direction === "down") await down();
if (direction === "latest") await latest();
async function up() {
const currentVersion = completedMigrations[completedMigrations.length - 1];
let nextVersion;
if (!currentVersion) nextVersion = migrations[0];
else nextVersion = migrations.find((migration) => {
return migration.version > currentVersion.version && migration.completed === false;
});
if (!nextVersion) throw Error("Nothing to upgrade");
const { up: up$1 } = await import(`file://${nextVersion.file}`);
if (!up$1) logger.warn(`Couldn't find the "up" function from migration ${nextVersion.file}`);
if (log) logger.info(`Applying ${nextVersion.name}...`);
await up$1(database);
await database.insert({
version: nextVersion.version,
name: nextVersion.name
}).into("directus_migrations");
await flushCaches(true);
}
async function down() {
const lastAppliedMigration = orderBy(completedMigrations, ["timestamp", "version"], ["desc", "desc"])[0];
if (!lastAppliedMigration) throw Error("Nothing to downgrade");
const migration = migrations.find((migration$1) => migration$1.version === lastAppliedMigration.version);
if (!migration) throw new Error("Couldn't find migration");
const { down: down$1 } = await import(`file://${migration.file}`);
if (!down$1) logger.warn(`Couldn't find the "down" function from migration ${migration.file}`);
if (log) logger.info(`Undoing ${migration.name}...`);
await down$1(database);
await database("directus_migrations").delete().where({ version: migration.version });
await flushCaches(true);
}
async function latest() {
let needsCacheFlush = false;
for (const migration of migrations) if (migration.completed === false) {
needsCacheFlush = true;
const { up: up$1 } = getModuleDefault(await import(`file://${migration.file}`));
if (!up$1) logger.warn(`Couldn't find the "up" function from migration ${migration.file}`);
if (log) logger.info(`Applying ${migration.name}...`);
await up$1(database);
await database.insert({
version: migration.version,
name: migration.name
}).into("directus_migrations");
}
if (needsCacheFlush) await flushCaches(true);
}
}
//#endregion
export { run as default };