fix(dev): reset legacy dev.db and apply migrations
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"predev": "node -e \"process.env.DATABASE_URL=process.env.DATABASE_URL||'file:./prisma/dev.db'; require('child_process').execSync('npx prisma migrate deploy', { stdio: 'inherit' });\"",
|
||||
"predev": "node scripts/predev-migrate.cjs",
|
||||
"dev": "nodemon src/index.ts",
|
||||
"test": "vitest run",
|
||||
"test:watch": "vitest",
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
/* eslint-disable no-console */
|
||||
const { execSync } = require("child_process");
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
const backendRoot = path.resolve(__dirname, "..");
|
||||
|
||||
const resolveDatabaseUrl = (rawUrl) => {
|
||||
const defaultDbPath = path.resolve(backendRoot, "prisma/dev.db");
|
||||
|
||||
if (!rawUrl || String(rawUrl).trim().length === 0) {
|
||||
return `file:${defaultDbPath}`;
|
||||
}
|
||||
|
||||
if (!String(rawUrl).startsWith("file:")) {
|
||||
return String(rawUrl);
|
||||
}
|
||||
|
||||
const filePath = String(rawUrl).replace(/^file:/, "");
|
||||
const prismaDir = path.resolve(backendRoot, "prisma");
|
||||
const normalizedRelative = filePath.replace(/^\.\/?/, "");
|
||||
const hasLeadingPrismaDir =
|
||||
normalizedRelative === "prisma" || normalizedRelative.startsWith("prisma/");
|
||||
|
||||
const absolutePath = path.isAbsolute(filePath)
|
||||
? filePath
|
||||
: path.resolve(hasLeadingPrismaDir ? backendRoot : prismaDir, normalizedRelative);
|
||||
|
||||
return `file:${absolutePath}`;
|
||||
};
|
||||
|
||||
const databaseUrl = resolveDatabaseUrl(process.env.DATABASE_URL);
|
||||
process.env.DATABASE_URL = databaseUrl;
|
||||
|
||||
const nodeEnv = process.env.NODE_ENV || "development";
|
||||
|
||||
const run = (cmd) => {
|
||||
execSync(cmd, {
|
||||
cwd: backendRoot,
|
||||
stdio: "inherit",
|
||||
env: { ...process.env, DATABASE_URL: databaseUrl },
|
||||
});
|
||||
};
|
||||
|
||||
const getDbFilePath = () => {
|
||||
if (!databaseUrl.startsWith("file:")) return null;
|
||||
return databaseUrl.replace(/^file:/, "");
|
||||
};
|
||||
|
||||
const isNonEmptyLegacyDbWithoutMigrations = () => {
|
||||
const dbPath = getDbFilePath();
|
||||
if (!dbPath) return false;
|
||||
if (!fs.existsSync(dbPath)) return false;
|
||||
|
||||
// Only attempt this heuristic for SQLite file DBs.
|
||||
const Database = require("better-sqlite3");
|
||||
const db = new Database(dbPath, { readonly: true });
|
||||
try {
|
||||
const hasMigrations =
|
||||
db
|
||||
.prepare(
|
||||
"SELECT 1 FROM sqlite_master WHERE type='table' AND name='_prisma_migrations' LIMIT 1",
|
||||
)
|
||||
.get() !== undefined;
|
||||
|
||||
const nonEmptyRow = db
|
||||
.prepare("SELECT COUNT(*) AS cnt FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'")
|
||||
.get();
|
||||
const nonEmpty = Number(nonEmptyRow?.cnt || 0) > 0;
|
||||
|
||||
return nonEmpty && !hasMigrations;
|
||||
} finally {
|
||||
db.close();
|
||||
}
|
||||
};
|
||||
|
||||
const backupDbIfPresent = () => {
|
||||
const dbPath = getDbFilePath();
|
||||
if (!dbPath) return null;
|
||||
if (!fs.existsSync(dbPath)) return null;
|
||||
|
||||
const dir = path.dirname(dbPath);
|
||||
const base = path.basename(dbPath, path.extname(dbPath));
|
||||
const stamp = new Date().toISOString().replace(/[:.]/g, "-");
|
||||
const backupPath = path.join(dir, `${base}.${stamp}.backup`);
|
||||
|
||||
fs.copyFileSync(dbPath, backupPath);
|
||||
return backupPath;
|
||||
};
|
||||
|
||||
const isNonProd = nodeEnv !== "production";
|
||||
const isFileDb = databaseUrl.startsWith("file:");
|
||||
|
||||
if (isNonProd && isFileDb && isNonEmptyLegacyDbWithoutMigrations()) {
|
||||
const backupPath = backupDbIfPresent();
|
||||
console.warn(
|
||||
`[predev] Prisma migrations cannot be deployed because the database was created without migrations.\n` +
|
||||
` DATABASE_URL=${databaseUrl}\n` +
|
||||
(backupPath ? ` Backup: ${backupPath}\n` : "") +
|
||||
` Resetting local SQLite database to apply migrations.`,
|
||||
);
|
||||
|
||||
run("npx prisma migrate reset --force --skip-seed");
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
run("npx prisma migrate deploy");
|
||||
Reference in New Issue
Block a user