fix: auto-migrate after DB restore and force re-login

- After importing a backup, run prisma migrate deploy so any schema
  migrations the backup is missing are applied automatically. This
  prevents pages from failing due to missing tables/columns when the
  backup was taken on an older version of the app.
- Force logout and redirect to login after a successful restore so the
  JWT is refreshed against the restored database (prevents userId
  mismatch causing user-scoped queries to return empty results).
- Fix getTotalPatientCount() in /status route to pass userId so it
  counts only the current user's patients instead of all patients.
- Add prisma.$connect() after $disconnect() to ensure a clean reconnect.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
ff
2026-05-17 22:31:12 -04:00
parent b9e888fc7f
commit 5508a90d28
231 changed files with 1629 additions and 14854 deletions

View File

@@ -1,7 +1,7 @@
NODE_ENV="development"
HOST=0.0.0.0
PORT=5000
FRONTEND_URLS=http://localhost:3000,http://192.168.1.236,https://communitydentistsoflowell.mydentalofficemanagement.com
FRONTEND_URLS=http://localhost:3000
SELENIUM_AGENT_BASE_URL=http://localhost:5002
JWT_SECRET = 'dentalsecret'
DB_HOST=localhost

View File

@@ -120,7 +120,7 @@ router.get("/status", async (req: Request, res: Response): Promise<any> => {
SELECT pg_size_pretty(pg_database_size(current_database())) as size
`;
const patientsCount = await storage.getTotalPatientCount();
const patientsCount = await storage.getTotalPatientCount(userId);
const lastBackup = await storage.getLastBackup(userId);
res.json({
@@ -388,11 +388,38 @@ router.post("/restore", restoreUpload.single("file"), async (req: Request, res:
return res.status(500).json({ error: "Restore failed", details: stderr });
}
// Reconnect Prisma after schema was replaced
try {
await prisma.$disconnect();
await prisma.$connect();
} catch (_) {}
res.json({ success: true });
// Apply any migrations the backup may be missing so the schema matches
// the current codebase. Uses prisma migrate deploy which is safe to run
// repeatedly — it skips already-applied migrations.
const migrate = spawn(
"npx",
["prisma", "migrate", "deploy", "--schema", path.resolve(__dirname, "../../../../packages/db/prisma/schema.prisma")],
{
env: { ...process.env, DATABASE_URL: process.env.DATABASE_URL || "" },
cwd: path.resolve(__dirname, "../../../../packages/db"),
}
);
let migrateOut = "";
migrate.stdout.on("data", (d) => (migrateOut += d.toString()));
migrate.stderr.on("data", (d) => (migrateOut += d.toString()));
migrate.on("close", (migrateCode) => {
if (migrateCode !== 0) {
console.error("prisma migrate deploy failed after restore:", migrateOut);
} else {
console.log("prisma migrate deploy completed after restore:", migrateOut.trim());
}
res.json({ success: true });
});
migrate.on("error", (err) => {
console.error("Failed to run prisma migrate deploy:", err.message);
res.json({ success: true }); // still report success — data is restored
});
});
if (isZip && tmpZipPath) {

View File

@@ -1,4 +1,4 @@
NODE_ENV=development
HOST=0.0.0.0
PORT=3000
VITE_API_BASE_URL_BACKEND=
VITE_API_BASE_URL_BACKEND=http://localhost:5000

View File

@@ -42,10 +42,18 @@ export function ImportDatabaseSection() {
onSuccess: () => {
toast({
title: "Database Restored",
description: "The database has been successfully restored from the backup file.",
description: "Database restored successfully. Redirecting to login...",
});
setSelectedFile(null);
if (fileInputRef.current) fileInputRef.current.value = "";
// Clear auth token and reload so the user re-authenticates against the
// restored database. This is necessary because the restored data may have
// a different userId than the current JWT, which would cause all
// user-scoped queries to return empty results.
setTimeout(() => {
localStorage.removeItem("token");
window.location.href = "/";
}, 2000);
},
onError: (err: any) => {
toast({