Files
api/services/nginx/app/classes/backup_store.php
T
Jepp9350 555d4e9918 Add comprehensive backup functionality and refactor email handling
Implemented server, database, and environment backups with configurable modules. Introduced new routes and configuration classes for backups alongside improved zip and S3 storage. Replaced PHPMailer with a cURL-based email service for streamlined message sending.
2025-02-11 17:41:57 +01:00

308 lines
9.7 KiB
PHP

<?php
namespace classes;
require_once WD . '/modules/backups/backups_c.php';
use backups\backups_c;
use Exception;
use interfaces\minio_backups_i;
use traits\minio_t;
class backup_store implements minio_backups_i
{
use minio_t;
/**
* The prefix for the backup files in the S3 bucket
* @var string
*/
const backup_s3_prefix = 'backups/';
/**
* The prefix for the metadata files in the S3 bucket
* @var string
*/
const metadata_s3_prefix = 'metadata/'; // The prefix for the backup files in the S3 bucket
/**
* The path to store the backups locally
* @var string
*/
const local_backup_path = '/tmp/backups/'; // The prefix for the metadata files in the S3 bucket
/**
* The configuration for the backup store
* @var backups_c
*/
public backups_c $config;
/**
* @throws Exception If the bucket does not exist, is not accessible, or is not configured.
*/
public function __construct()
{
$this->config = new backups_c();
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 = self::backupDatabase($backup_uuid);
// Web server files
$webserver_files_path = self::backupServerFiles($backup_uuid);
// Environment variables
$env_path = self::backupEnvironmentVariables($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');
// Add the web server files to the backup directory
copy($webserver_files_path, self::local_backup_path . 'backup_' . $backup_uuid . '/www.zip');
// Add the environment variables to the backup directory
copy($env_path, self::local_backup_path . 'backup_' . $backup_uuid . '/environment_variables.json');
// 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 . '/database.sql';
$result = $db->backupDatabase($path);
if (!$result) {
throw new Exception('Failed to backup the database!');
}
return $path;
}
/**
* @inheritDoc
*/
public function backupServerFiles(string $backup_uuid): string
{
// Backup the server files from the /var/www/html directory
$path = self::local_backup_path . 'backup_' . $backup_uuid . '/www.zip';
exec('zip -r ' . $path . ' /var/www/html');
return $path;
}
/**
* @inheritDoc
*/
public function backupEnvironmentVariables(string $backup_uuid): string
{
// Backup the config variables
$path = self::local_backup_path . 'backup_' . $backup_uuid . '/environment_variables.json';
file_put_contents($path, json_encode($_ENV));
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');
// Remove the web server files backup
unlink(self::local_backup_path . 'backup_' . $backup_uuid . '/www.zip');
// Remove the environment variables backup
unlink(self::local_backup_path . 'backup_' . $backup_uuid . '/environment_variables.json');
// Remove the backup directory
rmdir(self::local_backup_path . 'backup_' . $backup_uuid);
}
/**
* @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 . 'backup_',
'',
str_replace('.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);
}
}