tigerstyle-life9/includes/storage/class-storage-local.php
Ryan Malloy e92b7f8700 Initial commit: TigerStyle Life9 v1.0.0
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.
2026-05-27 14:32:00 -06:00

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];
}
}