feat: improve backup management, settings UI, and Twilio webhooks
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -11,12 +11,31 @@ const LOCAL_BACKUP_DIR = path.resolve(process.cwd(), "backups");
|
||||
// Name of the USB backup subfolder the user creates on their drive
|
||||
const USB_BACKUP_FOLDER_NAME = "USB Backup";
|
||||
|
||||
const MAX_BACKUPS = 30;
|
||||
|
||||
function ensureLocalBackupDir() {
|
||||
if (!fs.existsSync(LOCAL_BACKUP_DIR)) {
|
||||
fs.mkdirSync(LOCAL_BACKUP_DIR, { recursive: true });
|
||||
}
|
||||
}
|
||||
|
||||
function pruneOldBackups(dir: string) {
|
||||
try {
|
||||
const files = fs.readdirSync(dir)
|
||||
.filter((f) => f.endsWith(".sql") || f.endsWith(".zip"))
|
||||
.map((f) => ({ name: f, mtime: fs.statSync(path.join(dir, f)).mtimeMs }))
|
||||
.sort((a, b) => b.mtime - a.mtime);
|
||||
|
||||
const toDelete = files.slice(MAX_BACKUPS);
|
||||
for (const file of toDelete) {
|
||||
fs.unlinkSync(path.join(dir, file.name));
|
||||
console.log(`🗑️ Pruned old backup: ${file.name}`);
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn("Failed to prune old backups:", err);
|
||||
}
|
||||
}
|
||||
|
||||
async function getAdminUser() {
|
||||
const batchSize = 100;
|
||||
let offset = 0;
|
||||
@@ -49,6 +68,7 @@ export const startBackupCron = () => {
|
||||
const startedAt = new Date();
|
||||
const log = await cronJobLogStorage.createJobLog("local-backup", startedAt);
|
||||
await cronJobLogStorage.completeJobLog(log.id, "skipped", new Date());
|
||||
await storage.deleteNotificationsByType(admin.id, "BACKUP");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -58,6 +78,7 @@ export const startBackupCron = () => {
|
||||
try {
|
||||
const filename = `dental_backup_${Date.now()}.sql`;
|
||||
await backupDatabaseToPath({ destinationPath: LOCAL_BACKUP_DIR, filename });
|
||||
pruneOldBackups(LOCAL_BACKUP_DIR);
|
||||
await storage.createBackup(admin.id);
|
||||
await storage.deleteNotificationsByType(admin.id, "BACKUP");
|
||||
await cronJobLogStorage.completeJobLog(log.id, "success", new Date());
|
||||
@@ -93,6 +114,7 @@ export const startBackupCron = () => {
|
||||
const startedAt = new Date();
|
||||
const log = await cronJobLogStorage.createJobLog("usb-backup", startedAt);
|
||||
await cronJobLogStorage.completeJobLog(log.id, "skipped", new Date());
|
||||
await storage.deleteNotificationsByType(admin.id, "BACKUP");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -131,6 +153,7 @@ export const startBackupCron = () => {
|
||||
try {
|
||||
const filename = `dental_backup_usb_${Date.now()}.sql`;
|
||||
await backupDatabaseToPath({ destinationPath: usbBackupPath, filename });
|
||||
pruneOldBackups(usbBackupPath);
|
||||
await storage.createBackup(admin.id);
|
||||
await storage.deleteNotificationsByType(admin.id, "BACKUP");
|
||||
await cronJobLogStorage.completeJobLog(log.id, "success", new Date());
|
||||
|
||||
Reference in New Issue
Block a user