From 2325192f78fa550c980a771f50dca3a2e4a13de9 Mon Sep 17 00:00:00 2001 From: Bozhidar Date: Wed, 24 Apr 2024 20:51:36 +0300 Subject: [PATCH] update --- web/app/Console/Commands/RunBackup.php | 44 +++++++++ web/app/Filament/Enums/BackupType.php | 45 +++++++++ web/app/Filament/Resources/BackupResource.php | 40 +++++++- .../Pages/{EditBackup.php => ViewBackup.php} | 4 +- web/app/Models/Backup.php | 97 +++++++++++++++++++ web/app/Models/HostingSubscription.php | 1 - .../Providers/Filament/AdminPanelProvider.php | 16 +-- ...2024_04_24_144158_create_backups_table.php | 51 ++++++++++ 8 files changed, 285 insertions(+), 13 deletions(-) create mode 100644 web/app/Console/Commands/RunBackup.php create mode 100644 web/app/Filament/Enums/BackupType.php rename web/app/Filament/Resources/BackupResource/Pages/{EditBackup.php => ViewBackup.php} (80%) create mode 100644 web/database/migrations/2024_04_24_144158_create_backups_table.php diff --git a/web/app/Console/Commands/RunBackup.php b/web/app/Console/Commands/RunBackup.php new file mode 100644 index 00000000..7d90b182 --- /dev/null +++ b/web/app/Console/Commands/RunBackup.php @@ -0,0 +1,44 @@ +subDays(7))->get(); + foreach ($findBackupsForDeleting as $backup) { + $backup->delete(); + } + + $getBackups = Backup::where('backup_type', 'hosting_subscription')->get(); + if ($getBackups->count() > 0) { + foreach ($getBackups as $backup) { + $status = $backup->startBackup(); + } + } + + } +} diff --git a/web/app/Filament/Enums/BackupType.php b/web/app/Filament/Enums/BackupType.php new file mode 100644 index 00000000..8d5c41b6 --- /dev/null +++ b/web/app/Filament/Enums/BackupType.php @@ -0,0 +1,45 @@ + 'Full Backup', + self::SYSTEM_BACKUP => 'System Backup', + self::DATABASE_BACKUP => 'Database Backup', + self::HOSTING_SUBSCRIPTION_BACKUP => 'Hosting Subscription Backup', + }; + } + + public function getDescriptions(): ?string + { + return match ($this) { + self::FULL_BACKUP => 'A full backup is a complete copy of your website files and database. It is the best way to protect your website data.', + self::SYSTEM_BACKUP => 'A system backup is a copy of your website files and system files. It is useful for restoring your website if there is a problem with the system files.', + self::DATABASE_BACKUP => 'A database backup is a copy of your website database. It is useful for restoring your website if there is a problem with the database.', + self::HOSTING_SUBSCRIPTION_BACKUP => 'A hosting subscription backup is a copy of your website files and database. It is useful for restoring your website if there is a problem with the hosting subscription.', + }; + } + + public function getIcons(): ?string + { + return match ($this) { + self::FULL_BACKUP => 'heroicon-o-inbox-stack', + self::SYSTEM_BACKUP => 'heroicon-o-cog', + self::DATABASE_BACKUP => 'phyre-mysql', + self::HOSTING_SUBSCRIPTION_BACKUP => 'heroicon-o-server', + }; + } +} diff --git a/web/app/Filament/Resources/BackupResource.php b/web/app/Filament/Resources/BackupResource.php index af57d51b..6d1d88b7 100644 --- a/web/app/Filament/Resources/BackupResource.php +++ b/web/app/Filament/Resources/BackupResource.php @@ -2,12 +2,22 @@ namespace App\Filament\Resources; +use App\Filament\Enums\BackupType; use App\Filament\Resources\BackupResource\Pages; use App\Models\Backup; +use App\Models\HostingSubscription; +use Filament\Forms\Components\Select; use Filament\Forms\Form; +use Filament\Forms\Get; use Filament\Resources\Resource; use Filament\Tables; use Filament\Tables\Table; +use JaOcero\RadioDeck\Forms\Components\RadioDeck; + +use Illuminate\Database\Migrations\Migration; +use Illuminate\Database\Schema\Blueprint; +use Illuminate\Support\Facades\Schema; + class BackupResource extends Resource { @@ -23,7 +33,29 @@ public static function form(Form $form): Form { return $form ->schema([ - // + + RadioDeck::make('backup_type') + ->live() + ->default('full_backup') + ->options(BackupType::class) + ->icons(BackupType::class) + ->descriptions(BackupType::class) + ->required() + ->color('primary') + ->columnSpanFull(), + + + Select::make('hosting_subscription_id') + ->label('Hosting Subscription') + ->hidden(function (Get $get) { + return $get('backup_type') !== 'hosting_subscription'; + }) + ->options( + HostingSubscription::all()->pluck('domain', 'id') + ) + ->columnSpanFull() + ->required(), + ]); } @@ -31,7 +63,9 @@ public static function table(Table $table): Table { return $table ->columns([ - // + Tables\Columns\TextColumn::make('backup_type'), + Tables\Columns\TextColumn::make('backupRelated'), + Tables\Columns\TextColumn::make('status'), ]) ->filters([ // @@ -59,7 +93,7 @@ public static function getPages(): array return [ 'index' => Pages\ListBackups::route('/'), 'create' => Pages\CreateBackup::route('/create'), - 'edit' => Pages\EditBackup::route('/{record}/edit'), + 'view' => Pages\ViewBackup::route('/{record}'), ]; } } diff --git a/web/app/Filament/Resources/BackupResource/Pages/EditBackup.php b/web/app/Filament/Resources/BackupResource/Pages/ViewBackup.php similarity index 80% rename from web/app/Filament/Resources/BackupResource/Pages/EditBackup.php rename to web/app/Filament/Resources/BackupResource/Pages/ViewBackup.php index e0599202..b3653aab 100644 --- a/web/app/Filament/Resources/BackupResource/Pages/EditBackup.php +++ b/web/app/Filament/Resources/BackupResource/Pages/ViewBackup.php @@ -4,9 +4,9 @@ use App\Filament\Resources\BackupResource; use Filament\Actions; -use Filament\Resources\Pages\EditRecord; +use Filament\Resources\Pages\ViewRecord; -class EditBackup extends EditRecord +class ViewBackup extends ViewRecord { protected static string $resource = BackupResource::class; diff --git a/web/app/Models/Backup.php b/web/app/Models/Backup.php index 1e2ee26e..f7905954 100644 --- a/web/app/Models/Backup.php +++ b/web/app/Models/Backup.php @@ -4,7 +4,104 @@ use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\Casts\Attribute; +use Illuminate\Support\Str; + class Backup extends Model { use HasFactory; + + protected $fillable = [ + 'hosting_subscription_id', + 'backup_type', + 'status', + 'path', + 'size', + 'disk', + ]; + + protected function backupRelated() : Attribute + { + $relatedWith = $this->backup_type; + if ($this->backup_type === 'hosting_subscription') { + $findHostingSubscription = HostingSubscription::where('id', $this->hosting_subscription_id)->first(); + if ($findHostingSubscription) { + $relatedWith = $findHostingSubscription->domain; + } + } + + return Attribute::make( + get: fn () => $relatedWith + ); + } + + public function startBackup() + { + + if ($this->status == 'running') { + return [ + 'status' => 'running', + 'message' => 'Backup already running' + ]; + } + + $storagePath = storage_path('backups'); + if (! is_dir($storagePath)) { + mkdir($storagePath); + } + $backupPath = $storagePath.'/'.$this->id; + if (! is_dir($backupPath)) { + mkdir($backupPath); + } + $backupTempPath = $backupPath.'/temp'; + if (! is_dir($backupTempPath)) { + mkdir($backupTempPath); + } + if ($this->backup_type == 'hosting_subscription') { + $findHostingSubscription = HostingSubscription::where('id', $this->hosting_subscription_id)->first(); + if ($findHostingSubscription) { + + $backupFileName = Str::slug($findHostingSubscription->domain .'-'. date('Y-m-d-H-i-s')) . '.zip'; + $backupFilePath = $backupPath.'/'.$backupFileName; + + $backupLogFileName = 'backup.log'; + $backupLogFilePath = $backupPath.'/'.$backupLogFileName; + + $backupTempScript = '/tmp/backup-script-'.$this->id.'.sh'; + $shellFileContent = ''; + $shellFileContent .= 'echo "Backup up domain: '.$findHostingSubscription->domain . PHP_EOL; + $shellFileContent .= 'echo "Backup filename: '.$backupFileName. PHP_EOL; + $shellFileContent .= 'cp -r /home/'.$findHostingSubscription->system_username.' '.$backupTempPath.PHP_EOL; + + $shellFileContent .= 'cd '.$backupTempPath .' && zip -r '.$backupFilePath.' ./* '. PHP_EOL; + + $shellFileContent .= 'rm -rf '.$backupTempPath.PHP_EOL; + $shellFileContent .= 'echo "Backup complete"' . PHP_EOL; + $shellFileContent .= 'touch ' . $backupPath. '/backup.done' . PHP_EOL; + $shellFileContent .= 'rm -rf ' . $backupTempScript; + + $this->status = 'running'; + $this->queued = true; + $this->queued_at = now(); + $this->save(); + + file_put_contents($backupTempScript, $shellFileContent); + shell_exec('bash '.$backupTempScript.' >> ' . $backupLogFilePath . ' &'); + + return [ + 'status' => 'running', + 'message' => 'Backup started' + ]; + + } else { + $this->status = 'failed'; + $this->save(); + return [ + 'status' => 'failed', + 'message' => 'Hosting subscription not found' + ]; + } + } + + } } diff --git a/web/app/Models/HostingSubscription.php b/web/app/Models/HostingSubscription.php index 8e67a53b..bcf85b72 100644 --- a/web/app/Models/HostingSubscription.php +++ b/web/app/Models/HostingSubscription.php @@ -124,7 +124,6 @@ private function _createLinuxWebUser($model): array return []; } - private static function _generateUsername($string) { $removedMultispace = preg_replace('/\s+/', ' ', $string); diff --git a/web/app/Providers/Filament/AdminPanelProvider.php b/web/app/Providers/Filament/AdminPanelProvider.php index 28783fdd..600546b0 100644 --- a/web/app/Providers/Filament/AdminPanelProvider.php +++ b/web/app/Providers/Filament/AdminPanelProvider.php @@ -35,13 +35,15 @@ public function panel(Panel $panel): Panel $defaultColor = Color::Yellow; $brandLogo = asset('images/phyre-logo.svg'); - $isAppInstalled = file_exists(storage_path('installed')); - if ($isAppInstalled) { - if (setting('general.brand_logo_url')) { - $brandLogo = setting('general.brand_logo_url'); - } - if (setting('general.brand_primary_color')) { - $defaultColor = Color::hex(setting('general.brand_primary_color')); + if (!app()->runningInConsole()) { + $isAppInstalled = file_exists(storage_path('installed')); + if ($isAppInstalled) { + if (setting('general.brand_logo_url')) { + $brandLogo = setting('general.brand_logo_url'); + } + if (setting('general.brand_primary_color')) { + $defaultColor = Color::hex(setting('general.brand_primary_color')); + } } } diff --git a/web/database/migrations/2024_04_24_144158_create_backups_table.php b/web/database/migrations/2024_04_24_144158_create_backups_table.php new file mode 100644 index 00000000..87cdc65d --- /dev/null +++ b/web/database/migrations/2024_04_24_144158_create_backups_table.php @@ -0,0 +1,51 @@ +id(); + $table->bigInteger('hosting_subscription_id')->nullable(); + $table->string('backup_type')->nullable(); + $table->string('status')->nullable(); + $table->string('path')->nullable(); + $table->string('size')->nullable(); + $table->string('disk')->nullable(); + $table->longText('settings')->nullable(); + + + $table->tinyInteger('queued')->nullable(); + $table->timestamp('queued_at')->nullable(); + + $table->tinyInteger('started')->nullable(); + $table->timestamp('started_at')->nullable(); + + $table->tinyInteger('completed')->nullable(); + $table->timestamp('completed_at')->nullable(); + + $table->tinyInteger('failed')->nullable(); + $table->timestamp('failed_at')->nullable(); + + $table->tinyInteger('cancelled')->nullable(); + $table->timestamp('cancelled_at')->nullable(); + + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('backups'); + } +};