Introduced a backup system managing database and metadata files using Minio/S3, alongside routes for handling these. Integrated PHPMailer for robust email functionalities, including configuration testing and SMTP handling. Updated Dockerfile, composer dependencies, and minor code improvements to support new features.
241 lines
7.6 KiB
PHP
241 lines
7.6 KiB
PHP
<?php
|
|
|
|
namespace classes;
|
|
|
|
use Exception;
|
|
use interfaces\minio_backups_i;
|
|
use traits\minio_t;
|
|
|
|
class backup_store implements minio_backups_i
|
|
{
|
|
use minio_t;
|
|
|
|
const backup_s3_prefix = 'backups/'; // The prefix for the backup files in the S3 bucket
|
|
const metadata_s3_prefix = 'metadata/'; // The prefix for the metadata files in the S3 bucket
|
|
const local_backup_path = '/tmp/backups/'; // The path to store the backups locally
|
|
|
|
/**
|
|
* @throws Exception If the bucket does not exist, is not accessible, or is not configured.
|
|
*/
|
|
public function __construct()
|
|
{
|
|
self::setBucket('backups'); // Change the bucket to backups
|
|
self::validateBucket();
|
|
}
|
|
|
|
/**
|
|
* @inheritDoc
|
|
*/
|
|
public function validateBucket(): void
|
|
{
|
|
// Check if the bucket exists
|
|
if (!self::getS3Client()->doesBucketExist(self::getBucket())) {
|
|
// throw an exception
|
|
throw new Exception('The bucket does not exist in the S3 service! Missing: ' . self::getBucket());
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* @inheritDoc
|
|
*/
|
|
public function backup_exists(string $backup_uuid): bool
|
|
{
|
|
// Check if the file exists
|
|
$objects = self::getS3Client()->listObjects([
|
|
'Bucket' => self::getBucket(),
|
|
'Prefix' => self::backup_s3_prefix . 'backup_' . $backup_uuid . '.zip'
|
|
]);
|
|
return count($objects['Contents'] ?? []) > 0;
|
|
}
|
|
|
|
/**
|
|
* @inheritDoc
|
|
*/
|
|
public function getBackupDownloadUrl(string $backup_uuid): string
|
|
{
|
|
// Get the presigned URL for the backup
|
|
return self::getPresignedUrl('backup_' . $backup_uuid . '.zip');
|
|
}
|
|
|
|
/**
|
|
* @inheritDoc
|
|
*/
|
|
public function download(string $backup_uuid): string
|
|
{
|
|
// Download the backup
|
|
$path = self::local_backup_path . 'backup_' . $backup_uuid . '.zip';
|
|
$result = self::getS3Client()->getObject([
|
|
'Bucket' => self::getBucket(),
|
|
'Key' => self::backup_s3_prefix . 'backup_' . $backup_uuid . '.zip',
|
|
'SaveAs' => $path
|
|
]);
|
|
return $path;
|
|
}
|
|
|
|
/**
|
|
* @inheritDoc
|
|
* @throws Exception
|
|
*/
|
|
public function createBackup($backup_name = null, $backup_description = null): string
|
|
{
|
|
// Check if the backup name is set
|
|
if (!$backup_name) {
|
|
$backup_name = 'Backup ' . date('Y-m-d H:i:s');
|
|
}
|
|
// Check if the backup description is set
|
|
if (!$backup_description) {
|
|
$backup_description = 'Backup created on ' . date('Y-m-d H:i:s');
|
|
}
|
|
// Generate the backup file
|
|
$backup_uuid = self::generateBackupFile();
|
|
// Upload the backup file
|
|
self::getS3Client()->putObject([
|
|
'Bucket' => self::getBucket(),
|
|
'Key' => self::backup_s3_prefix . 'backup_' . $backup_uuid . '.zip',
|
|
'SourceFile' => self::local_backup_path . 'backup_' . $backup_uuid . '.zip'
|
|
]);
|
|
// Update the metadata
|
|
self::createBackupMetadata($backup_uuid, $backup_name, $backup_description);
|
|
// Clean up the backup file
|
|
self::cleanUpBackupFile($backup_uuid);
|
|
// Return the backup UUID
|
|
return $backup_uuid;
|
|
}
|
|
|
|
/**
|
|
* @inheritDoc
|
|
*/
|
|
public function generateBackupFile(): string
|
|
{
|
|
// Generate a UUID for the backup
|
|
$backup_uuid = uniqid();
|
|
|
|
// Prepare the backup directories
|
|
if (!is_dir(self::local_backup_path)) {
|
|
mkdir(self::local_backup_path);
|
|
}
|
|
if (!is_dir(self::local_backup_path . 'backup_' . $backup_uuid)) {
|
|
mkdir(self::local_backup_path . 'backup_' . $backup_uuid);
|
|
}
|
|
|
|
// Backup the database
|
|
$database_path = $this->backupDatabase($backup_uuid);
|
|
|
|
// Create the backup path
|
|
$backup_path = self::local_backup_path . 'backup_' . $backup_uuid . '.zip';
|
|
|
|
// Create the backups directory
|
|
if (!is_dir(self::local_backup_path)) {
|
|
mkdir(self::local_backup_path);
|
|
}
|
|
|
|
// Create the backup directory
|
|
if (!is_dir(self::local_backup_path . 'backup_' . $backup_uuid)) {
|
|
mkdir(self::local_backup_path . 'backup_' . $backup_uuid);
|
|
}
|
|
|
|
// Add the database backup to the backup directory
|
|
copy($database_path, self::local_backup_path . 'backup_' . $backup_uuid . '/database.sql');
|
|
|
|
// Zip the backup directory
|
|
exec('zip -r ' . $backup_path . ' ' . self::local_backup_path . 'backup_' . $backup_uuid);
|
|
|
|
// Return the backup UUID
|
|
return $backup_uuid;
|
|
}
|
|
|
|
/**
|
|
* @inheritDoc
|
|
* @throws Exception
|
|
*/
|
|
public function backupDatabase(string $backup_uuid): string
|
|
{
|
|
global /** @var db $db */
|
|
$db;
|
|
// Backup the database
|
|
$path = self::local_backup_path . 'backup_' . $backup_uuid;
|
|
$result = $db->backupDatabase($path);
|
|
if (!$result) {
|
|
throw new Exception('Failed to backup the database!');
|
|
}
|
|
return $path;
|
|
}
|
|
|
|
/**
|
|
* @inheritDoc
|
|
*/
|
|
public function createBackupMetadata(string $backup_uuid, string $backup_name, string $backup_description): void
|
|
{
|
|
// Create the metadata file
|
|
$metadata = [
|
|
'backup_name' => $backup_name,
|
|
'backup_description' => $backup_description,
|
|
'backup_date' => date('Y-m-d H:i:s'),
|
|
'backup_size' => filesize(self::local_backup_path . 'backup_' . $backup_uuid . '.zip'),
|
|
'hash' => md5_file(self::local_backup_path . 'backup_' . $backup_uuid . '.zip')
|
|
];
|
|
file_put_contents(
|
|
self::local_backup_path . 'backup_' . $backup_uuid . '.json',
|
|
json_encode($metadata)
|
|
);
|
|
// Upload the metadata file
|
|
self::getS3Client()->putObject([
|
|
'Bucket' => self::getBucket(),
|
|
'Key' => self::metadata_s3_prefix . 'backup_' . $backup_uuid . '.json',
|
|
'SourceFile' => self::local_backup_path . 'backup_' . $backup_uuid . '.json'
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* @inheritDoc
|
|
*/
|
|
public function cleanupBackupFile(string $backup_uuid): void
|
|
{
|
|
/** Clean up the backup files, and remove the temporary directories and files */
|
|
|
|
// Remove the backup directory
|
|
unlink(self::local_backup_path . 'backup_' . $backup_uuid . '.zip');
|
|
|
|
// Remove the metadata file
|
|
unlink(self::local_backup_path . 'backup_' . $backup_uuid . '.json');
|
|
|
|
// Remove the database backup
|
|
unlink(self::local_backup_path . 'backup_' . $backup_uuid . '/database.sql');
|
|
}
|
|
|
|
/**
|
|
* @inheritDoc
|
|
*/
|
|
public function listBackups(): array
|
|
{
|
|
// List the backups
|
|
$backups = [];
|
|
$objects = self::getS3Client()->listObjects([
|
|
'Bucket' => self::getBucket(),
|
|
'Prefix' => self::metadata_s3_prefix
|
|
]);
|
|
// Check if there are any backups
|
|
if (!isset($objects['Contents'])) {
|
|
return [];
|
|
}
|
|
foreach ( $objects['Contents'] as $object ) {
|
|
$backup_uuid = str_replace([self::metadata_s3_prefix, '.json'], '', $object['Key']);
|
|
$backups[] = $this->getBackupMetadata($backup_uuid);
|
|
}
|
|
return $backups;
|
|
}
|
|
|
|
/**
|
|
* @inheritDoc
|
|
*/
|
|
public function getBackupMetadata(string $backup_uuid): array
|
|
{
|
|
// Get the metadata file
|
|
$metadata = self::getS3Client()->getObject([
|
|
'Bucket' => self::getBucket(),
|
|
'Key' => self::metadata_s3_prefix . 'backup_' . $backup_uuid . '.json'
|
|
]);
|
|
return json_decode($metadata['Body'], true);
|
|
}
|
|
} |