119 lines
3.5 KiB
JavaScript
119 lines
3.5 KiB
JavaScript
/* 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 runCapture = (cmd) => {
|
|
try {
|
|
const stdout = execSync(cmd, {
|
|
cwd: backendRoot,
|
|
encoding: "utf8",
|
|
stdio: ["ignore", "pipe", "pipe"],
|
|
env: { ...process.env, DATABASE_URL: databaseUrl },
|
|
});
|
|
return { ok: true, stdout: stdout || "", stderr: "" };
|
|
} catch (error) {
|
|
const err = error;
|
|
const stderr =
|
|
err && err.stderr
|
|
? Buffer.isBuffer(err.stderr)
|
|
? err.stderr.toString("utf8")
|
|
: String(err.stderr)
|
|
: "";
|
|
const stdout =
|
|
err && err.stdout
|
|
? Buffer.isBuffer(err.stdout)
|
|
? err.stdout.toString("utf8")
|
|
: String(err.stdout)
|
|
: "";
|
|
return { ok: false, stdout, stderr, error: err };
|
|
}
|
|
};
|
|
|
|
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 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:");
|
|
|
|
const deploy = runCapture("npx prisma migrate deploy");
|
|
if (deploy.ok) {
|
|
if (deploy.stdout) process.stdout.write(deploy.stdout);
|
|
} else {
|
|
if (deploy.stdout) process.stdout.write(deploy.stdout);
|
|
if (deploy.stderr) process.stderr.write(deploy.stderr);
|
|
|
|
const stderr = deploy.stderr || "";
|
|
const isP3005 = stderr.includes("P3005");
|
|
|
|
// Common when an older dev.db exists but migrations weren't used previously.
|
|
if (isNonProd && isFileDb && isP3005) {
|
|
const backupPath = backupDbIfPresent();
|
|
console.warn(
|
|
`[predev] Prisma migrate baseline required (P3005). Resetting local SQLite database.\n` +
|
|
` DATABASE_URL=${databaseUrl}\n` +
|
|
(backupPath ? ` Backup: ${backupPath}\n` : "") +
|
|
` If you need to preserve local data, restore the backup and baseline manually.`,
|
|
);
|
|
|
|
run("npx prisma migrate reset --force --skip-seed");
|
|
} else {
|
|
throw deploy.error;
|
|
}
|
|
}
|