Because cats have 9 lives, but servers don't - so they need backup-restore! Complete backup solution with S3/MinIO support. - Full WordPress backup (files + database) - S3 / MinIO / S3-compatible storage backends - Scheduled automatic backups - Disaster recovery / one-click restore - Backup integrity validation - Cat-themed admin interface Includes build.sh and .distignore for WordPress-installable release ZIPs.
231 lines
6.8 KiB
PHP
231 lines
6.8 KiB
PHP
<?php
|
|
/**
|
|
* Local Storage Backend
|
|
*
|
|
* Handles local file system storage for backups
|
|
*
|
|
* @package TigerStyleLife9
|
|
* @since 1.0.0
|
|
*/
|
|
|
|
// Exit if accessed directly
|
|
if (!defined('ABSPATH')) {
|
|
exit;
|
|
}
|
|
|
|
/**
|
|
* Local storage backend class
|
|
*
|
|
* @since 1.0.0
|
|
*/
|
|
class TigerStyle_Life9_Storage_Local extends TigerStyle_Life9_Storage_Backend {
|
|
|
|
/**
|
|
* Store file to local storage
|
|
*
|
|
* @param string $file_path Local file path
|
|
* @param array $config Storage configuration
|
|
* @return array Storage result
|
|
*/
|
|
public function store($file_path, $config = []) {
|
|
if (!file_exists($file_path)) {
|
|
throw new Exception('Source file does not exist');
|
|
}
|
|
|
|
// Get destination path
|
|
$upload_dir = wp_upload_dir();
|
|
$backup_dir = $upload_dir['basedir'] . '/tigerstyle-life9/backups';
|
|
|
|
// Ensure backup directory exists
|
|
if (!file_exists($backup_dir)) {
|
|
wp_mkdir_p($backup_dir);
|
|
}
|
|
|
|
// Generate unique filename if file already exists
|
|
$filename = basename($file_path);
|
|
$destination = $backup_dir . '/' . $filename;
|
|
|
|
$counter = 1;
|
|
while (file_exists($destination)) {
|
|
$file_info = pathinfo($filename);
|
|
$new_filename = $file_info['filename'] . '_' . $counter . '.' . $file_info['extension'];
|
|
$destination = $backup_dir . '/' . $new_filename;
|
|
$counter++;
|
|
}
|
|
|
|
// Copy file to backup location (file is already in temp location)
|
|
if ($file_path !== $destination) {
|
|
if (!copy($file_path, $destination)) {
|
|
throw new Exception('Failed to copy file to backup location');
|
|
}
|
|
}
|
|
|
|
// Set proper permissions
|
|
chmod($destination, 0644);
|
|
|
|
return [
|
|
'url' => $upload_dir['baseurl'] . '/tigerstyle-life9/backups/' . basename($destination),
|
|
'remote_path' => $destination,
|
|
'storage_id' => basename($destination),
|
|
'metadata' => [
|
|
'size' => filesize($destination),
|
|
'created' => date('Y-m-d H:i:s')
|
|
]
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Retrieve file from local storage
|
|
*
|
|
* @param string $remote_path Remote file path
|
|
* @param string $local_path Local destination path
|
|
* @param array $config Storage configuration
|
|
* @return bool Success status
|
|
*/
|
|
public function retrieve($remote_path, $local_path, $config = []) {
|
|
if (!file_exists($remote_path)) {
|
|
throw new Exception('Backup file does not exist');
|
|
}
|
|
|
|
// Copy file to destination
|
|
return copy($remote_path, $local_path);
|
|
}
|
|
|
|
/**
|
|
* Delete file from local storage
|
|
*
|
|
* @param string $remote_path Remote file path
|
|
* @param array $config Storage configuration
|
|
* @return bool Success status
|
|
*/
|
|
public function delete($remote_path, $config = []) {
|
|
if (!file_exists($remote_path)) {
|
|
return true; // Already deleted
|
|
}
|
|
|
|
// Secure delete
|
|
$encryption = new TigerStyle_Life9_Encryption();
|
|
return $encryption->secure_delete($remote_path);
|
|
}
|
|
|
|
/**
|
|
* Test storage connection
|
|
*
|
|
* @param array $config Storage configuration
|
|
* @return bool Connection status
|
|
*/
|
|
public function test_connection($config = []) {
|
|
$upload_dir = wp_upload_dir();
|
|
$backup_dir = $upload_dir['basedir'] . '/tigerstyle-life9/backups';
|
|
|
|
// Check if directory exists and is writable
|
|
if (!file_exists($backup_dir)) {
|
|
if (!wp_mkdir_p($backup_dir)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return is_writable($backup_dir);
|
|
}
|
|
|
|
/**
|
|
* Get storage usage
|
|
*
|
|
* @param array $config Storage configuration
|
|
* @return array Usage information
|
|
*/
|
|
public function get_usage($config = []) {
|
|
$upload_dir = wp_upload_dir();
|
|
$backup_dir = $upload_dir['basedir'] . '/tigerstyle-life9/backups';
|
|
|
|
if (!file_exists($backup_dir)) {
|
|
return [
|
|
'used_space' => 0,
|
|
'file_count' => 0,
|
|
'available_space' => disk_free_space($upload_dir['basedir'])
|
|
];
|
|
}
|
|
|
|
$total_size = 0;
|
|
$file_count = 0;
|
|
|
|
$iterator = new RecursiveIteratorIterator(
|
|
new RecursiveDirectoryIterator($backup_dir, RecursiveDirectoryIterator::SKIP_DOTS)
|
|
);
|
|
|
|
foreach ($iterator as $file) {
|
|
if ($file->isFile()) {
|
|
$total_size += $file->getSize();
|
|
$file_count++;
|
|
}
|
|
}
|
|
|
|
return [
|
|
'used_space' => $total_size,
|
|
'file_count' => $file_count,
|
|
'available_space' => disk_free_space($backup_dir),
|
|
'formatted_used' => $this->format_bytes($total_size),
|
|
'formatted_available' => $this->format_bytes(disk_free_space($backup_dir))
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Clean up old files
|
|
*
|
|
* @param string $cutoff_date Date cutoff (Y-m-d format)
|
|
* @param array $config Storage configuration
|
|
* @return array Cleanup results
|
|
*/
|
|
public function cleanup_old_files($cutoff_date, $config = []) {
|
|
$upload_dir = wp_upload_dir();
|
|
$backup_dir = $upload_dir['basedir'] . '/tigerstyle-life9/backups';
|
|
|
|
if (!file_exists($backup_dir)) {
|
|
return [
|
|
'files_deleted' => 0,
|
|
'space_freed' => 0
|
|
];
|
|
}
|
|
|
|
$cutoff_timestamp = strtotime($cutoff_date);
|
|
$files_deleted = 0;
|
|
$space_freed = 0;
|
|
|
|
$iterator = new RecursiveIteratorIterator(
|
|
new RecursiveDirectoryIterator($backup_dir, RecursiveDirectoryIterator::SKIP_DOTS)
|
|
);
|
|
|
|
foreach ($iterator as $file) {
|
|
if ($file->isFile() && $file->getMTime() < $cutoff_timestamp) {
|
|
$file_size = $file->getSize();
|
|
|
|
if (unlink($file->getPathname())) {
|
|
$files_deleted++;
|
|
$space_freed += $file_size;
|
|
}
|
|
}
|
|
}
|
|
|
|
return [
|
|
'files_deleted' => $files_deleted,
|
|
'space_freed' => $space_freed,
|
|
'formatted_space_freed' => $this->format_bytes($space_freed)
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Format bytes to human readable format
|
|
*
|
|
* @param int $bytes Number of bytes
|
|
* @return string Formatted size
|
|
*/
|
|
private function format_bytes($bytes) {
|
|
$units = ['B', 'KB', 'MB', 'GB', 'TB'];
|
|
|
|
for ($i = 0; $bytes > 1024; $i++) {
|
|
$bytes /= 1024;
|
|
}
|
|
|
|
return round($bytes, 2) . ' ' . $units[$i];
|
|
}
|
|
} |