Compare commits

...

10 commits

Author SHA1 Message Date
Bozhidar
716a9ba1f1 Update Backup.php 2024-05-02 15:47:45 +03:00
Bozhidar
0e57e4c609 update 2024-05-02 12:19:40 +03:00
Bozhidar
6b52300332 Create RunPhyreRepair.php 2024-05-02 12:12:53 +03:00
Bozhidar
67c47a9581 update 2024-05-02 11:58:46 +03:00
Bozhidar
bb75a16428 update 2024-05-02 11:55:21 +03:00
Bozhidar
4e19730e02 Update Domain.php 2024-05-02 11:41:06 +03:00
Bozhidar
6b226a289a Update BackupResource.php 2024-05-02 11:35:04 +03:00
Bozhidar
2b0425f8e1 Update BackupResource.php 2024-05-02 11:34:52 +03:00
Bozhidar
0ba23458d2 Update apache2-conf.blade.php 2024-05-02 11:27:07 +03:00
Bozhidar
6f48c8c545 Update Domain.php 2024-05-02 11:16:24 +03:00
11 changed files with 203 additions and 59 deletions

View file

@ -1,6 +1,6 @@
<?php
namespace app\Console\Commands;
namespace App\Console\Commands;
use App\Models\Backup;
use App\Models\HostingSubscription;

View file

@ -0,0 +1,36 @@
<?php
namespace App\Console\Commands;
use App\Models\Backup;
use App\Models\HostingSubscription;
use Carbon\Carbon;
use Illuminate\Console\Command;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateDailyFullHostingSubscriptionsBackup extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'phyre:create-daily-full-hosting-subscriptions-backup';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Command description';
/**
* Execute the console command.
*/
public function handle()
{
}
}

View file

@ -9,7 +9,7 @@ use Illuminate\Console\Command;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class RunBackup extends Command
class RunBackupChecks extends Command
{
/**
* The name and signature of the console command.
@ -42,7 +42,7 @@ class RunBackup extends Command
if ($getPendingBackups->count() > 0) {
if ($getPendingBackups->count() > 1) {
$this->info('Multiple backups are pending..');
$this->info('Multiple backups are pending...');
} else {
foreach ($getPendingBackups as $pendingBackup) {
$pendingBackup->startBackup();

View file

@ -10,14 +10,14 @@ use Illuminate\Console\Command;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class RunHostingSubscriptionsBackup extends Command
class RunHostingSubscriptionsBackupChecks extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'phyre:run-hosting-subscriptions-backup';
protected $signature = 'phyre:run-hosting-subscriptions-backup-checks';
/**
* The console command description.
@ -37,26 +37,26 @@ class RunHostingSubscriptionsBackup extends Command
$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());
}
}
}
// // 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
@ -64,8 +64,8 @@ class RunHostingSubscriptionsBackup extends Command
->get();
if ($getPendingBackups->count() > 0) {
if ($getPendingBackups->count() > 3) {
$this->info('There are more than 3 pending backups. Please wait for them to finish.');
if ($getPendingBackups->count() > 1) {
$this->info('Multiple backups are pending...');
} else {
foreach ($getPendingBackups as $pendingBackup) {
$pendingBackup->startBackup();

View file

@ -0,0 +1,36 @@
<?php
namespace app\Console\Commands;
use App\Models\Backup;
use App\Models\HostingSubscription;
use Carbon\Carbon;
use Illuminate\Console\Command;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class RunRepair extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'phyre:run-repair';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Command description';
/**
* Execute the console command.
*/
public function handle()
{
$checkApacheStatus = shell_exec('service apache2 status');
}
}

View file

@ -77,9 +77,17 @@ class BackupResource extends Resource
Tables\Columns\BadgeColumn::make('status')
->badge(),
Tables\Columns\TextColumn::make('completed_at')
Tables\Columns\TextColumn::make('created_at')
->state(function (Backup $backup) {
return $backup->completed_at ? $backup->completed_at : 'N/A';
return $backup->created_at ? $backup->created_at : 'N/A';
}),
Tables\Columns\TextColumn::make('completed_at')
->label('Completed time')
->state(function (Backup $backup) {
$diff = \Carbon\Carbon::parse($backup->completed_at)
->diffForHumans($backup->created_at);
return $backup->completed_at ? $diff : 'N/A';
}),
Tables\Columns\TextColumn::make('size')

View file

@ -7,6 +7,7 @@ use App\Filament\Resources\HostingPlanResource\Pages;
use App\Models\HostingPlan;
use App\Models\RemoteDatabaseServer;
use App\SupportedApplicationTypes;
use Filament\Actions\DeleteAction;
use Filament\Forms;
use Filament\Forms\Components\CheckboxList;
use Filament\Forms\Components\Select;
@ -250,6 +251,7 @@ class HostingPlanResource extends Resource
])
->actions([
Tables\Actions\EditAction::make(),
Tables\Actions\DeleteAction::make()
])
->bulkActions([
Tables\Actions\BulkActionGroup::make([

View file

@ -10,6 +10,7 @@ use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use Jackiedo\DotenvEditor\Facades\DotenvEditor;
class Backup extends Model
{
@ -47,12 +48,14 @@ class Backup extends Model
});
static::deleting(function ($model) {
ShellApi::safeDelete($model->path,[
Storage::path('backups')
]);
if (Storage::disk('backups')->exists($model->filepath)) {
Storage::disk('backups')->delete($model->filepath);
}
if (is_file($model->filepath)) {
// ShellApi::safeDelete($model->path, [
// Storage::path('backups')
// ]);
if (Storage::disk('backups')->exists($model->filepath)) {
Storage::disk('backups')->delete($model->filepath);
}
}
});
}
@ -96,7 +99,7 @@ class Backup extends Model
shell_exec('cd '.$tempValidatePath.' && unzip -o '.Storage::disk('backups')->path($this->filepath));
$validateDatabaseFile = $tempValidatePath.'/database.sql';
$validateEnvFile = $tempValidatePath.'/env.json';
$validateEnvFile = $tempValidatePath.'/.env';
$errorsBag = [];
if (! file_exists($validateDatabaseFile)) {
@ -105,19 +108,19 @@ class Backup extends Model
if (! file_exists($validateEnvFile)) {
$errorsBag[] = 'Env file not found';
}
$getEnv = Dotenv::createArrayBacked(base_path())->load();
$getEnvFromBackup = json_decode(file_get_contents($validateEnvFile), true);
if (empty($getEnvFromBackup)) {
$errorsBag[] = 'Env file is empty';
} else {
foreach ($getEnv as $key => $value) {
if (! isset($getEnvFromBackup[$key])) {
$errorsBag[] = 'Env key '.$key.' not found';
}
if ($getEnvFromBackup[$key] != $value) {
$errorsBag[] = 'Env key '.$key.' value mismatch';
}
}
if (count($errorsBag) > 0) {
$this->status = 'failed';
$this->save();
return [
'status' => 'failed',
'message' => 'Backup failed. Database or env file missing.',
'errors' => $errorsBag
];
}
$originalEnvContent = file_get_contents(base_path().'/.env');
$backupEnvContent = file_get_contents($validateEnvFile);
if ($originalEnvContent != $backupEnvContent) {
$errorsBag[] = 'Env file content mismatch';
}
if (count($errorsBag) > 0) {
@ -192,7 +195,7 @@ class Backup extends Model
mkdir($backupTempPath);
}
$backupFilename = 'phyre-panel-'.date('Ymd-His').'.zip';
$backupFilename = 'phyre-backup-'.date('Ymd-His').'.zip';
$backupFilePath = $storagePath.'/' . $backupFilename;
if ($this->backup_type == 'full') {
@ -210,12 +213,48 @@ class Backup extends Model
// Export Phyre Panel database
$shellFileContent .= 'mysqldump -u "'.env('MYSQl_ROOT_USERNAME').'" -p"'.env('MYSQL_ROOT_PASSWORD').'" "'.env('DB_DATABASE').'" > '.$databaseBackupPath . PHP_EOL;
// Export Phyre Panel ENV
$getEnv = Dotenv::createArrayBacked(base_path())->load();
file_put_contents($backupTempPath.'/env.json', json_encode($getEnv, JSON_PRETTY_PRINT));
$backupStructure = [
'env'=>$getEnv,
];
file_put_contents($backupTempPath.'/backup.json', json_encode($backupStructure, JSON_PRETTY_PRINT));
$shellFileContent .= 'echo "Backup Phyre Panel ENV"'. PHP_EOL;
$shellFileContent .= 'cp '.base_path().'/.env '.$backupTempPath.'/.env'. PHP_EOL;
$shellFileContent .= 'cd '.$backupTempPath .' && zip -r '.$backupFilePath.' ./* '. PHP_EOL;
// Export Phyre Panel Hosting Subscription
$findHostingSubscription = HostingSubscription::all();
if ($findHostingSubscription->count() > 0) {
foreach ($findHostingSubscription as $hostingSubscription) {
$hostingSubscriptionPath = $backupTempPath .'/hosting_subscriptions/'.$hostingSubscription->system_username;
$shellFileContent .= PHP_EOL;
$shellFileContent .= 'echo "Backup up hosting subscription: ' . $hostingSubscription->system_username .'" '. PHP_EOL;
$shellFileContent .= 'mkdir -p '.$hostingSubscriptionPath.PHP_EOL;
$shellFileContent .= 'cp -r /home/'.$hostingSubscription->system_username.'/* .[^.]* ' . $hostingSubscriptionPath .PHP_EOL;
$shellFileContent .= 'mkdir -p '.$hostingSubscriptionPath.'/databases'.PHP_EOL;
$getDatabases = Database::where('hosting_subscription_id', $hostingSubscription->id)
->where(function ($query) {
$query->where('is_remote_database_server', '0')
->orWhereNull('is_remote_database_server');
})
->get();
if ($getDatabases->count() > 0) {
foreach ($getDatabases as $database) {
$databaseName = $database->database_name_prefix . $database->database_name;
$shellFileContent .= 'echo "Backup up database: ' . $databaseName . '" ' . PHP_EOL;
$databaseBackupPath = $hostingSubscriptionPath . '/databases/' . $databaseName . '.sql';
$shellFileContent .= 'mysqldump -u "' . env('MYSQl_ROOT_USERNAME') . '" -p"' . env('MYSQL_ROOT_PASSWORD') . '" "' . $databaseName . '" > ' . $databaseBackupPath . PHP_EOL;
}
}
$shellFileContent .= PHP_EOL;
}
}
$shellFileContent .= 'cd '.$backupTempPath .' && zip -r '.$backupFilePath.' ./* .[^.]* '. PHP_EOL;
$shellFileContent .= 'rm -rf '.$backupTempPath.PHP_EOL;
$shellFileContent .= 'echo "Backup complete"' . PHP_EOL;

View file

@ -224,6 +224,20 @@ class Domain extends Model
shell_exec('chown -R '.$findHostingSubscription->system_username.':'.$webUserGroup.' '.$this->domain_root.'/logs/apache2');
shell_exec('chmod -R 775 '.$this->domain_root.'/logs/apache2');
if (!is_file($this->domain_root.'/logs/apache2/bytes.log')) {
shell_exec('touch '.$this->domain_root.'/logs/apache2/bytes.log');
}
if (!is_file($this->domain_root.'/logs/apache2/access.log')) {
shell_exec('touch '.$this->domain_root.'/logs/apache2/access.log');
}
if (!is_file($this->domain_root.'/logs/apache2/error.log')) {
shell_exec('touch '.$this->domain_root.'/logs/apache2/error.log');
}
shell_exec('chmod -R 775 '.$this->domain_root.'/logs/apache2/bytes.log');
shell_exec('chmod -R 775 '.$this->domain_root.'/logs/apache2/access.log');
shell_exec('chmod -R 775 '.$this->domain_root.'/logs/apache2/error.log');
$appType = 'php';
$appVersion = '8.3';
@ -404,8 +418,10 @@ class Domain extends Model
}
file_put_contents('/etc/apache2/sites-available/'.$this->domain.'-ssl.conf', $apacheBaseConfigWithSSL);
shell_exec('ln -s /etc/apache2/sites-available/'.$this->domain.'-ssl.conf /etc/apache2/sites-enabled/'.$this->domain.'-ssl.conf');
if (!is_link('/etc/apache2/sites-enabled/' . $this->domain . '-ssl.conf')) {
shell_exec('ln -s /etc/apache2/sites-available/' . $this->domain . '-ssl.conf /etc/apache2/sites-enabled/' . $this->domain . '-ssl.conf');
}
}

View file

@ -65,7 +65,7 @@ class HostingSubscriptionBackup extends Model
public function checkCronJob()
{
$cronJobCommand = 'phyre-php /usr/local/phyre/web/artisan phyre:run-hosting-subscriptions-backup';
$cronJobCommand = 'phyre-php /usr/local/phyre/web/artisan phyre:run-hosting-subscriptions-backup-checks';
$findCronJob = CronJob::where('command', $cronJobCommand)->first();
if (! $findCronJob) {
$cronJob = new CronJob();
@ -73,8 +73,18 @@ class HostingSubscriptionBackup extends Model
$cronJob->command = $cronJobCommand;
$cronJob->user = 'root';
$cronJob->save();
return false;
}
$cronJobCommand = 'phyre-php /usr/local/phyre/web/artisan phyre:create-daily-full-hosting-subscriptions-backup';
$findCronJob = CronJob::where('command', $cronJobCommand)->first();
if (! $findCronJob) {
$cronJob = new CronJob();
$cronJob->schedule = '0 0 * * *';
$cronJob->command = $cronJobCommand;
$cronJob->user = 'root';
$cronJob->save();
}
return true;
}

View file

@ -34,10 +34,7 @@
LogFormat "%h %l %u %t \"%r\" %>s %b" common
CustomLog {{$domainRoot}}/logs/apache2/bytes.log bytes
CustomLog {{$domainRoot}}/logs/apache2/combined.log combined
CustomLog {{$domainRoot}}/logs/apache2/access.log common
CustomLog {{$domainRoot}}/logs/apache2/referer.log "%{Referer}i -> %U"
CustomLog {{$domainRoot}}/logs/apache2/agent.log "%{User-agent}i"
ErrorLog {{$domainRoot}}/logs/apache2/error.log
@if($appType == 'php')