Files
api/services/nginx/app/classes/backup_store.php
T
Jepp9350 797630d8bd Add backup and email enhancements with PHPMailer integration
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.
2025-02-11 14:25:26 +01:00

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