option('database-only'); $backupDatabase = !$this->option('records-only'); // Get the server hostname $hostname = gethostname(); // Extract the first part of the hostname $parts = preg_split('/[-.]/', $hostname); $serverName = strtolower($parts[0]); // Sanitize the server name to be a valid bucket name (lowercase, numbers, and hyphens only) $serverName = preg_replace('/[^a-z0-9-]/', '', $serverName); // Use the bucket from the option, or fallback to config, or finally use the server name $bucket = $this->option('bucket') ?: config('filesystems.s3.bucket') ?: $serverName; if (!$bucket) { $this->error("Backup bucket not configured!"); return 1; } $this->info("Using backup bucket: $bucket"); $this->info("Starting FULL Backup..."); /* |-------------------------------------------------------------------------- | 1️⃣ Sync All Call Recordings |-------------------------------------------------------------------------- */ if ($backupRecords) { $this->info("Syncing all call recordings..."); $callsCommand = "rclone copy /var/spool/asterisk/monitor/ s3:$bucket/calls --progress"; exec($callsCommand, $callsOutput, $callsResult); if ($callsResult !== 0) { $this->error("Call recordings sync failed ❌"); } else { $this->info("Call recordings synced successfully ✅"); } } /* |-------------------------------------------------------------------------- | 2️⃣ Backup Databases (keep latest only) |-------------------------------------------------------------------------- */ if ($backupDatabase) { $databases = DB::select("SHOW DATABASES"); foreach ($databases as $db) { $dbName = $db->Database; if ( $dbName === 'asterisk' || $dbName === 'asteriskcdrdb' || str_starts_with($dbName, 'pbx') ) { $this->info("Processing database: $dbName"); $date = now()->format('Y-m-d_H-i-s'); $backupFile = "/root/{$dbName}_{$date}.sql.gz"; // Dump and compress the database $dumpCommand = "mysqldump $dbName | gzip > $backupFile"; exec($dumpCommand, $dumpOutput, $dumpResult); if ($dumpResult !== 0) { $this->error("Dump failed for $dbName ❌"); continue; } // Upload new backup $uploadCommand = "rclone copy $backupFile s3:$bucket/databases/"; exec($uploadCommand, $uploadOutput, $uploadResult); if ($uploadResult !== 0) { $this->error("Upload failed for $dbName ❌"); continue; } // 🧹 Delete local file after successful upload if (file_exists($backupFile)) { unlink($backupFile); } // Keep only the last 3 backups - delete older ones $this->deleteOldBackups($bucket, $dbName); $this->info("$dbName updated successfully ✅"); } } } $this->info("Backup process finished 🎯"); return 0; } /** * Keep only the last 3 database backups, delete older ones */ private function deleteOldBackups($bucket, $dbName) { $listCommand = "rclone lsjson s3:$bucket/databases/"; exec($listCommand, $output, $result); if ($result !== 0 || empty($output)) { return; } $files = json_decode(implode("\n", $output), true); if (empty($files) || !is_array($files)) { return; } // ✅ Filter files for the specific database $files = array_filter($files, function ($file) use ($dbName) { return isset($file['Path']) && preg_match('/^' . preg_quote($dbName, '/') . '_.*\.sql\.gz$/', $file['Path']); }); if (empty($files)) { return; } // Sort files by modification time (newest first) usort($files, function ($a, $b) { return strtotime($b['ModTime']) <=> strtotime($a['ModTime']); }); if (count($files) > 3) { $filesToDelete = array_slice($files, 3); foreach ($filesToDelete as $file) { $filename = $file['Path']; $deleteCommand = "rclone delete s3:$bucket/databases/$filename"; exec($deleteCommand, $delOutput, $delResult); if ($delResult === 0) { $this->info("Deleted old backup: $filename 🗑️"); } else { $this->warn("Failed to delete old backup: $filename"); } } } } }