fix: use in-memory startedAt for cron job log to survive DB restore

The auto-import wipes the database, destroying the cronJobLog record
created at the start of the job. Now duration is calculated from a
local variable instead of querying the DB, and if the record is gone
a fresh log entry is created in the restored database.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Summit Dental Care
2026-06-26 22:55:07 -04:00
parent 1edf73fdc8
commit 27d9132820
308 changed files with 2570 additions and 1816 deletions

View File

@@ -1,13 +1,12 @@
NODE_ENV="development"
HOST=0.0.0.0
PORT=5000
CLOUDFLARE_HOST=communitydentistsoflowell.mydentalofficemanagement.com
FRONTEND_URLS=http://localhost:3000,http://communitydentistsoflowell.mydentalofficemanagement.com,https://communitydentistsoflowell.mydentalofficemanagement.com
CLOUDFLARE_HOST=
FRONTEND_URLS=http://localhost:3000
SELENIUM_AGENT_BASE_URL=http://localhost:5002
JWT_SECRET = 'dentalsecret'
DB_HOST=localhost
DB_USER=postgres
DB_PASSWORD=mypassword
DB_NAME=dentalapp
DATABASE_URL=postgresql://postgres:mypassword@localhost:5432/dentalapp
LICENSE_SECRET=3aa4ab937e46c6863b9e3c2b591a595b31ea3af1060bf5e7961ad722a8b54f92
DATABASE_URL=postgresql://postgres:mypassword@localhost:5432/dentalapp

View File

@@ -79,12 +79,12 @@ export const startBackupCron = () => {
pruneOldBackups(LOCAL_BACKUP_DIR);
await storage.createBackup(admin.id);
await storage.deleteNotificationsByType(admin.id, "BACKUP");
await cronJobLogStorage.completeJobLog(log.id, "success", new Date());
await cronJobLogStorage.completeJobLog(log.id, log.jobName, startedAt, "success", new Date());
console.log(`✅ Local backup done → ${filename}`);
} catch (err) {
const errorMessage = err instanceof Error ? err.message : String(err);
console.error("Local backup failed:", err);
await cronJobLogStorage.completeJobLog(log.id, "failed", new Date(), errorMessage);
await cronJobLogStorage.completeJobLog(log.id, log.jobName, startedAt, "failed", new Date(), errorMessage);
await storage.createNotification(
admin.id,
"BACKUP",
@@ -113,7 +113,7 @@ export const startBackupCron = () => {
const destination = await storage.getActiveBackupDestination(admin.id);
if (!destination) {
const errorMessage = "No backup destination configured.";
await cronJobLogStorage.completeJobLog(log.id, "failed", new Date(), errorMessage);
await cronJobLogStorage.completeJobLog(log.id, log.jobName, startedAt, "failed", new Date(), errorMessage);
await storage.createNotification(
admin.id,
"BACKUP",
@@ -124,7 +124,7 @@ export const startBackupCron = () => {
if (!fs.existsSync(destination.path)) {
const errorMessage = "Backup destination drive not found or disconnected.";
await cronJobLogStorage.completeJobLog(log.id, "failed", new Date(), errorMessage);
await cronJobLogStorage.completeJobLog(log.id, log.jobName, startedAt, "failed", new Date(), errorMessage);
await storage.createNotification(
admin.id,
"BACKUP",
@@ -145,12 +145,12 @@ export const startBackupCron = () => {
pruneOldBackups(usbBackupPath);
await storage.createBackup(admin.id);
await storage.deleteNotificationsByType(admin.id, "BACKUP");
await cronJobLogStorage.completeJobLog(log.id, "success", new Date());
await cronJobLogStorage.completeJobLog(log.id, log.jobName, startedAt, "success", new Date());
console.log(`✅ USB backup done → ${usbBackupPath}/${filename}`);
} catch (err) {
const errorMessage = err instanceof Error ? err.message : String(err);
console.error("USB backup failed:", err);
await cronJobLogStorage.completeJobLog(log.id, "failed", new Date(), errorMessage);
await cronJobLogStorage.completeJobLog(log.id, log.jobName, startedAt, "failed", new Date(), errorMessage);
await storage.createNotification(
admin.id,
"BACKUP",
@@ -178,13 +178,13 @@ export const startBackupCron = () => {
try {
await runRclonePull();
writeRcloneConfig({ lastSyncAt: new Date().toISOString(), lastSyncStatus: "success", lastSyncError: null });
await cronJobLogStorage.completeJobLog(log.id, "success", new Date());
await cronJobLogStorage.completeJobLog(log.id, log.jobName, startedAt, "success", new Date());
console.log(`Rclone backup complete.`);
} catch (err) {
const errorMessage = err instanceof Error ? err.message : String(err);
console.error("Rclone backup failed:", err);
writeRcloneConfig({ lastSyncAt: new Date().toISOString(), lastSyncStatus: "failed", lastSyncError: errorMessage });
await cronJobLogStorage.completeJobLog(log.id, "failed", new Date(), errorMessage);
await cronJobLogStorage.completeJobLog(log.id, log.jobName, startedAt, "failed", new Date(), errorMessage);
if (admin) {
await storage.createNotification(
admin.id,
@@ -214,13 +214,13 @@ export const startBackupCron = () => {
try {
await importLatestBackup();
writeRcloneConfig({ lastImportAt: new Date().toISOString(), lastImportStatus: "success", lastImportError: null });
await cronJobLogStorage.completeJobLog(log.id, "success", new Date());
await cronJobLogStorage.completeJobLog(log.id, log.jobName, startedAt, "success", new Date());
console.log(`Auto-import complete.`);
} catch (err) {
const errorMessage = err instanceof Error ? err.message : String(err);
console.error("Auto-import failed:", err);
writeRcloneConfig({ lastImportAt: new Date().toISOString(), lastImportStatus: "failed", lastImportError: errorMessage });
await cronJobLogStorage.completeJobLog(log.id, "failed", new Date(), errorMessage);
await cronJobLogStorage.completeJobLog(log.id, log.jobName, startedAt, "failed", new Date(), errorMessage);
if (admin) {
await storage.createNotification(
admin.id,
@@ -251,13 +251,13 @@ export const startBackupCron = () => {
await runNetworkSync(config.sourceUrl, config.apiKey);
await runNetworkFilesSync(config.sourceUrl, config.apiKey);
writeSyncConfig({ lastSyncAt: new Date().toISOString(), lastSyncStatus: "success", lastSyncError: null });
await cronJobLogStorage.completeJobLog(log.id, "success", new Date());
await cronJobLogStorage.completeJobLog(log.id, log.jobName, startedAt, "success", new Date());
console.log(`✅ Network sync complete (database + uploads).`);
} catch (err) {
const errorMessage = err instanceof Error ? err.message : String(err);
console.error("Network sync failed:", err);
writeSyncConfig({ lastSyncAt: new Date().toISOString(), lastSyncStatus: "failed", lastSyncError: errorMessage });
await cronJobLogStorage.completeJobLog(log.id, "failed", new Date(), errorMessage);
await cronJobLogStorage.completeJobLog(log.id, log.jobName, startedAt, "failed", new Date(), errorMessage);
if (admin) {
await storage.createNotification(
admin.id,

View File

@@ -24,14 +24,22 @@ export const cronJobLogStorage = {
async completeJobLog(
id: number,
jobName: string,
startedAt: Date,
status: CronJobStatus,
completedAt: Date,
errorMessage?: string
): Promise<CronJobLogEntry> {
const durationMs = completedAt.getTime() - (await db.cronJobLog.findUniqueOrThrow({ where: { id } })).startedAt.getTime();
return db.cronJobLog.update({
where: { id },
data: { status, completedAt, durationMs, errorMessage: errorMessage ?? null },
const durationMs = completedAt.getTime() - startedAt.getTime();
const existing = await db.cronJobLog.findUnique({ where: { id } });
if (existing) {
return db.cronJobLog.update({
where: { id },
data: { status, completedAt, durationMs, errorMessage: errorMessage ?? null },
});
}
return db.cronJobLog.create({
data: { jobName, status, startedAt, completedAt, durationMs, errorMessage: errorMessage ?? null },
});
},