This commit is contained in:
Bozhidar 2024-05-02 19:33:47 +03:00
parent 33adbec9e4
commit fc6aac0f53
8 changed files with 81 additions and 107 deletions

View file

@ -9,40 +9,9 @@ use Illuminate\Support\Str;
class BackupStorage class BackupStorage
{ {
public static function getDomainPath($domainId)
{
$findDomain = Domain::where('id', $domainId)->first();
if ($findDomain) {
return $findDomain->domain_root . '/backups';
}
return false;
}
public static function getDomainInstance($domainId)
{
$domainPath = self::getDomainPath($domainId);
if ($domainPath) {
$storageBuild = Storage::build([
'driver' => 'local',
'root' => $domainPath,
]);
$storageBuild->buildTemporaryUrlsUsing(function ($path, $expiration, $options) {
return URL::temporarySignedRoute(
'backup.download',
$expiration,
array_merge($options, ['path' => $path])
);
});
return $storageBuild;
}
return false;
}
public static function getPath() public static function getPath()
{ {
$rootPath = storage_path('app'); $rootPath = '/var/lib/phyre/backups/system';
$customBackupPath = setting('general.backup_path'); $customBackupPath = setting('general.backup_path');
if (!empty($customBackupPath)) { if (!empty($customBackupPath)) {
$rootPath = $customBackupPath; $rootPath = $customBackupPath;

View file

@ -4,11 +4,16 @@ namespace App\Console\Commands;
use App\Models\Backup; use App\Models\Backup;
use App\Models\HostingSubscription; use App\Models\HostingSubscription;
use App\Models\HostingSubscriptionBackup;
use Carbon\Carbon; use Carbon\Carbon;
use Illuminate\Console\Command; use Illuminate\Console\Command;
use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema; use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Migrations\Migration;
class CreateDailyFullHostingSubscriptionsBackup extends Command class CreateDailyFullHostingSubscriptionsBackup extends Command
{ {
/** /**
@ -30,7 +35,27 @@ class CreateDailyFullHostingSubscriptionsBackup extends Command
*/ */
public function handle() public function handle()
{ {
// Find Hosting Subscriptions
$findHostingSubscriptions = HostingSubscription::limit(1)->get();
if ($findHostingSubscriptions->count() > 0) {
foreach ($findHostingSubscriptions as $hostingSubscription) {
$findBackup = HostingSubscriptionBackup::where('hosting_subscription_id', $hostingSubscription->id)
->where('backup_type', 'full')
->where('created_at', '>=', Carbon::now()->subHours(24))
->first();
if (! $findBackup) {
$backup = new HostingSubscriptionBackup();
$backup->hosting_subscription_id = $hostingSubscription->id;
$backup->backup_type = 'full';
$backup->save();
} else {
$this->error('Backup already exists for ' . $hostingSubscription->domain);
$this->error('Created before: ' . $findBackup->created_at->diffForHumans());
}
}
}
} }
} }

View file

@ -37,28 +37,6 @@ class RunHostingSubscriptionsBackupChecks extends Command
$backup->delete(); $backup->delete();
} }
// // Find Hosting Subscriptions
// $findHostingSubscriptions = HostingSubscription::all();
// if ($findHostingSubscriptions->count() > 0) {
// foreach ($findHostingSubscriptions as $hostingSubscription) {
//
// $findBackup = HostingSubscriptionBackup::where('hosting_subscription_id', $hostingSubscription->id)
// ->where('backup_type', 'full')
// ->where('created_at', '>=', Carbon::now()->subHours(24))
// ->first();
// if (! $findBackup) {
// $backup = new HostingSubscriptionBackup();
// $backup->hosting_subscription_id = $hostingSubscription->id;
// $backup->backup_type = 'full';
// $backup->save();
// } else {
// $this->error('Backup already exists for ' . $hostingSubscription->domain);
// $this->error('Created before: ' . $findBackup->created_at->diffForHumans());
// }
// }
// }
// Check for pending backups // Check for pending backups
$getPendingBackups = HostingSubscriptionBackup::where('status', 'pending') $getPendingBackups = HostingSubscriptionBackup::where('status', 'pending')
->get(); ->get();

View file

@ -102,6 +102,9 @@ class BackupResource extends Resource
->actions([ ->actions([
Tables\Actions\Action::make('download') Tables\Actions\Action::make('download')
->icon('heroicon-o-arrow-down-tray') ->icon('heroicon-o-arrow-down-tray')
->hidden(function (Backup $backup) {
return $backup->status !== BackupStatus::Completed;
})
->action(function (Backup $backup) { ->action(function (Backup $backup) {
$backupStorage = BackupStorage::getInstance($backup->root_path); $backupStorage = BackupStorage::getInstance($backup->root_path);

View file

@ -2,6 +2,7 @@
namespace App\Models; namespace App\Models;
use App\BackupStorage;
use App\Filament\Enums\BackupStatus; use App\Filament\Enums\BackupStatus;
use App\Helpers; use App\Helpers;
use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Builder;
@ -33,16 +34,16 @@ class HostingSubscriptionBackup extends Model
'status' => BackupStatus::class, 'status' => BackupStatus::class,
]; ];
protected static function booted(): void // protected static function booted(): void
{ // {
static::addGlobalScope('customer', function (Builder $query) { // static::addGlobalScope('customer', function (Builder $query) {
if (auth()->check() && auth()->guard()->name == 'web_customer') { // if (auth()->check() && auth()->guard()->name == 'web_customer') {
$query->whereHas('hostingSubscription', function ($query) { // $query->whereHas('hostingSubscription', function ($query) {
$query->where('customer_id', auth()->user()->id); // $query->where('customer_id', auth()->user()->id);
}); // });
} // }
}); // });
} // }
public static function boot() public static function boot()
{ {
parent::boot(); parent::boot();
@ -57,9 +58,9 @@ class HostingSubscriptionBackup extends Model
}); });
static::deleting(function ($model) { static::deleting(function ($model) {
if (is_file($model->filepath)) { // if (is_file($model->filepath)) {
shell_exec('rm -rf ' . $model->filepath); // shell_exec('rm -rf ' . $model->filepath);
} // }
}); });
} }
@ -67,6 +68,7 @@ class HostingSubscriptionBackup extends Model
{ {
$cronJobCommand = 'phyre-php /usr/local/phyre/web/artisan phyre:run-hosting-subscriptions-backup-checks'; $cronJobCommand = 'phyre-php /usr/local/phyre/web/artisan phyre:run-hosting-subscriptions-backup-checks';
$findCronJob = CronJob::where('command', $cronJobCommand)->first(); $findCronJob = CronJob::where('command', $cronJobCommand)->first();
if (! $findCronJob) { if (! $findCronJob) {
$cronJob = new CronJob(); $cronJob = new CronJob();
$cronJob->schedule = '*/5 * * * *'; $cronJob->schedule = '*/5 * * * *';
@ -170,25 +172,20 @@ class HostingSubscriptionBackup extends Model
]; ];
} }
$storagePath = storage_path('backups'); $backupStorageRootPath = '/var/lib/phyre/backups/hosting_subscriptions';
$backupPath = $storagePath.'/hosting_subscriptions/'.$this->backup_type.'/'.$this->id; $backupPath = $backupStorageRootPath . '/' . $findHostingSubscription->customer_id;
$backupTempPath = $backupPath.'/temp'; $backupTempPath = $backupPath.'/temp';
shell_exec('mkdir -p ' . $backupTempPath); shell_exec('mkdir -p ' . $backupTempPath);
$backupFileName = Str::slug($findHostingSubscription->system_username .'-'. date('Ymd-His')) . '.zip';
$backupFilePath = $backupStorageRootPath.'/'.$backupFileName;
$backupFileName = Str::slug($findHostingSubscription->system_username .'-'. date('Ymd-His')) . '.tar.gz'; $backupLogFilePath = $backupPath.'/backup.log';
$backupFilePath = $backupPath.'/'.$backupFileName;
$backupLogFileName = 'backup.log';
$backupLogFilePath = $backupPath.'/'.$backupLogFileName;
$backupTargetPath = $findMainDomain->domain_root . '/backups';
$backupTargetFilePath = $backupTargetPath.'/'.$backupFileName;
$backupTempScript = '/tmp/backup-script-'.$this->id.'.sh'; $backupTempScript = '/tmp/backup-script-'.$this->id.'.sh';
$shellFileContent = ''; $shellFileContent = '';
$shellFileContent .= 'mkdir -p '. $backupTargetPath.PHP_EOL; $shellFileContent .= 'echo "Backup up user: '.$findHostingSubscription->system_username .'"'. PHP_EOL;
$shellFileContent .= 'echo "Backup up domain: '.$findHostingSubscription->domain .'"'. PHP_EOL;
$shellFileContent .= 'echo "Backup filename: '.$backupFileName.'"' . PHP_EOL; $shellFileContent .= 'echo "Backup filename: '.$backupFileName.'"' . PHP_EOL;
if ($this->backup_type == 'full') { if ($this->backup_type == 'full') {
@ -205,30 +202,23 @@ class HostingSubscriptionBackup extends Model
if ($getDatabases->count() > 0) { if ($getDatabases->count() > 0) {
foreach ($getDatabases as $database) { foreach ($getDatabases as $database) {
// $findDatabaseUser = DatabaseUser::where('database_id', $database->id)
// ->first();
// if (!$findDatabaseUser) {
// continue;
// }
$databaseName = $database->database_name_prefix . $database->database_name; $databaseName = $database->database_name_prefix . $database->database_name;
// $databaseUser = $findDatabaseUser->username_prefix . $findDatabaseUser->username;
// $databaseUserPassword = $findDatabaseUser->password;
$shellFileContent .= 'echo "Backup up database: ' . $databaseName .'" '. PHP_EOL; $shellFileContent .= 'echo "Backup up database: ' . $databaseName .'" '. PHP_EOL;
// $shellFileContent .= 'echo "Backup up database user: ' . $databaseUser .'" '. PHP_EOL; $shellFileContent .= 'mkdir -p '.$backupTempPath . '/databases' . PHP_EOL;
$databaseBackupPath = $backupTempPath . '/' . $databaseName . '.sql'; $databaseBackupPath = $backupTempPath . '/databases/' . $databaseName . '.sql';
$shellFileContent .= 'mysqldump -u "'.env('MYSQl_ROOT_USERNAME').'" -p"'.env('MYSQL_ROOT_PASSWORD').'" "'.$databaseName.'" > '.$databaseBackupPath . PHP_EOL; $shellFileContent .= 'mysqldump -u "'.env('MYSQl_ROOT_USERNAME').'" -p"'.env('MYSQL_ROOT_PASSWORD').'" "'.$databaseName.'" > '.$databaseBackupPath . PHP_EOL;
} }
} }
} }
$shellFileContent .= 'cd '.$backupTempPath .' && tar -czvf '.$backupFilePath.' ./* '. PHP_EOL; // With find, we can search for all files,directories (including hidden) in the current directory and zip them
$shellFileContent .= 'cd '.$backupTempPath .' && find . -exec zip -r '.$backupFilePath.' {} \;'. PHP_EOL;
$shellFileContent .= 'rm -rf '.$backupTempPath.PHP_EOL; $shellFileContent .= 'rm -rf '.$backupTempPath.PHP_EOL;
$shellFileContent .= 'echo "Backup complete"' . PHP_EOL; $shellFileContent .= 'echo "Backup complete"' . PHP_EOL;
$shellFileContent .= 'touch ' . $backupTargetPath. '/backup-'.$this->id.'.done' . PHP_EOL; $shellFileContent .= 'touch ' . $backupPath. '/backup.done' . PHP_EOL;
$shellFileContent .= 'mv '.$backupFilePath.' '. $backupTargetFilePath.PHP_EOL;
$shellFileContent .= 'rm -rf ' . $backupTempScript . PHP_EOL; $shellFileContent .= 'rm -rf ' . $backupTempScript . PHP_EOL;
file_put_contents($backupTempScript, $shellFileContent); file_put_contents($backupTempScript, $shellFileContent);
@ -238,8 +228,12 @@ class HostingSubscriptionBackup extends Model
if ($processId > 0 && is_numeric($processId)) { if ($processId > 0 && is_numeric($processId)) {
$this->path = $findMainDomain->domain_root . '/backups'; $this->path = $backupPath;
$this->filepath = $backupTargetFilePath; $this->root_path = $backupStorageRootPath;
$this->temp_path = $backupTempPath;
$this->file_path = $backupFilePath;
$this->file_name = $backupFileName;
$this->status = 'processing'; $this->status = 'processing';
$this->queued = true; $this->queued = true;
$this->queued_at = now(); $this->queued_at = now();

View file

@ -17,8 +17,13 @@ return new class extends Migration
$table->bigInteger('hosting_subscription_id')->nullable(); $table->bigInteger('hosting_subscription_id')->nullable();
$table->string('backup_type')->nullable(); $table->string('backup_type')->nullable();
$table->string('status')->nullable(); $table->string('status')->nullable();
$table->string('path')->nullable(); $table->string('path')->nullable();
$table->string('filepath')->nullable(); $table->string('root_path')->nullable();
$table->string('temp_path')->nullable();
$table->string('file_path')->nullable();
$table->string('file_name')->nullable();
$table->string('size')->nullable(); $table->string('size')->nullable();
$table->string('disk')->nullable(); $table->string('disk')->nullable();
$table->string('process_id')->nullable(); $table->string('process_id')->nullable();

View file

@ -40,8 +40,8 @@ class BackupTest extends ActionTestCase
} }
$this->assertTrue($backupFinished); $this->assertTrue($backupFinished);
$this->assertSame($findLastBackup->status, BackupStatus::Completed); $this->assertSame($findLastBackup->status, BackupStatus::Completed);
$this->assertNotEmpty($findLastBackup->filepath); $this->assertNotEmpty($findLastBackup->file_path);
$this->assertTrue(file_exists(Storage::disk('backups')->path($findLastBackup->filepath))); $this->assertTrue(file_exists($findLastBackup->file_path));
$backup = new Backup(); $backup = new Backup();
$checkCronJob = $backup->checkCronJob(); $checkCronJob = $backup->checkCronJob();
@ -91,14 +91,14 @@ class BackupTest extends ActionTestCase
} }
$this->assertTrue($backupCompleted); $this->assertTrue($backupCompleted);
$this->assertNotEmpty($findBackup->filepath); $this->assertNotEmpty($findBackup->file_path);
$this->assertTrue(file_exists(Storage::disk('backups')->path($findBackup->filepath))); $this->assertTrue(file_exists($findBackup->file_path));
$getFilesize = filesize(Storage::disk('backups')->path($findBackup->filepath)); $getFilesize = filesize($findBackup->file_path);
$this->assertGreaterThan(0, $getFilesize); $this->assertGreaterThan(0, $getFilesize);
$this->assertSame($getFilesize, $findBackup->size); $this->assertSame($getFilesize, $findBackup->size);
Helpers::extractTar(Storage::disk('backups')->path($findBackup->filepath), $findBackup->path . '/unit-test'); Helpers::extractTar($findBackup->file_path, $findBackup->path . '/unit-test');
} }

View file

@ -28,7 +28,7 @@ class HostingSubscriptionBackupTest extends ActionTestCase
$chs = $this->_createHostingSubscription(); $chs = $this->_createHostingSubscription();
Artisan::call('phyre:run-hosting-subscriptions-backup'); Artisan::call('phyre:create-daily-full-hosting-subscriptions-backup');
$findLastBackup = HostingSubscriptionBackup::where('hosting_subscription_id', $chs['hostingSubscriptionId']) $findLastBackup = HostingSubscriptionBackup::where('hosting_subscription_id', $chs['hostingSubscriptionId'])
->first(); ->first();
@ -51,8 +51,8 @@ class HostingSubscriptionBackupTest extends ActionTestCase
$this->assertTrue($backupFinished); $this->assertTrue($backupFinished);
$this->assertSame($findLastBackup->status, BackupStatus::Completed); $this->assertSame($findLastBackup->status, BackupStatus::Completed);
$this->assertNotEmpty($findLastBackup->filepath); $this->assertNotEmpty($findLastBackup->file_path);
$this->assertTrue(file_exists($findLastBackup->filepath)); $this->assertTrue(file_exists($findLastBackup->file_path));
$backup = new HostingSubscriptionBackup(); $backup = new HostingSubscriptionBackup();
$checkCronJob = $backup->checkCronJob(); $checkCronJob = $backup->checkCronJob();
@ -82,14 +82,14 @@ class HostingSubscriptionBackupTest extends ActionTestCase
} }
$this->assertTrue($backupCompleted); $this->assertTrue($backupCompleted);
$this->assertNotEmpty($findBackup->filepath); $this->assertNotEmpty($findBackup->file_path);
$this->assertTrue(file_exists($findBackup->filepath)); $this->assertTrue(file_exists($findBackup->file_path));
$getFilesize = filesize($findBackup->filepath); $getFilesize = filesize($findBackup->file_path);
$this->assertGreaterThan(0, $getFilesize); $this->assertGreaterThan(0, $getFilesize);
$this->assertSame(Helpers::checkPathSize($findBackup->path), $findBackup->size); $this->assertSame(Helpers::checkPathSize($findBackup->path), $findBackup->size);
Helpers::extractTar($findBackup->filepath, $findBackup->path . '/unit-test'); Helpers::extractTar($findBackup->file_path, $findBackup->path . '/unit-test');
// //
// dd($chs); // dd($chs);
$findDatabase = Database::where('id', $chs['databaseId'])->first(); $findDatabase = Database::where('id', $chs['databaseId'])->first();