2024-04-25 13:49:19 +00:00
|
|
|
<?php
|
|
|
|
|
|
|
|
namespace App\Models;
|
|
|
|
|
2024-05-02 16:33:47 +00:00
|
|
|
use App\BackupStorage;
|
2024-04-25 13:58:30 +00:00
|
|
|
use App\Filament\Enums\BackupStatus;
|
|
|
|
use App\Helpers;
|
2024-05-07 13:40:06 +00:00
|
|
|
use App\Jobs\ProcessHostingSubscriptionBackup;
|
2024-05-10 16:56:58 +00:00
|
|
|
use App\PhyreConfig;
|
2024-05-02 16:41:20 +00:00
|
|
|
use App\ShellApi;
|
2024-04-26 16:29:28 +00:00
|
|
|
use Illuminate\Database\Eloquent\Builder;
|
2024-04-25 13:58:30 +00:00
|
|
|
use Illuminate\Database\Eloquent\Casts\Attribute;
|
2024-04-25 13:49:19 +00:00
|
|
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
|
|
|
use Illuminate\Database\Eloquent\Model;
|
2024-04-25 13:58:30 +00:00
|
|
|
use Illuminate\Support\Str;
|
2024-04-25 13:49:19 +00:00
|
|
|
|
|
|
|
class HostingSubscriptionBackup extends Model
|
|
|
|
{
|
|
|
|
use HasFactory;
|
2024-04-25 13:58:30 +00:00
|
|
|
|
|
|
|
const STATUS_PENDING = 'pending';
|
|
|
|
const STATUS_PROCESSING = 'processing';
|
|
|
|
const STATUS_COMPLETED = 'completed';
|
|
|
|
const STATUS_FAILED = 'failed';
|
|
|
|
const STATUS_CANCELLED = 'cancelled';
|
|
|
|
|
|
|
|
protected $fillable = [
|
|
|
|
'hosting_subscription_id',
|
|
|
|
'backup_type',
|
|
|
|
'status',
|
|
|
|
'path',
|
|
|
|
'size',
|
|
|
|
'disk',
|
|
|
|
];
|
|
|
|
|
|
|
|
protected $casts = [
|
|
|
|
'status' => BackupStatus::class,
|
|
|
|
];
|
|
|
|
|
2024-05-12 21:10:56 +00:00
|
|
|
protected static function booted(): void
|
|
|
|
{
|
|
|
|
static::addGlobalScope('customer', function (Builder $query) {
|
|
|
|
if (auth()->check() && auth()->guard()->name == 'web_customer') {
|
|
|
|
$query->whereHas('hostingSubscription', function ($query) {
|
|
|
|
$query->where('customer_id', auth()->user()->id);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
2024-04-25 13:58:30 +00:00
|
|
|
public static function boot()
|
|
|
|
{
|
|
|
|
parent::boot();
|
|
|
|
|
|
|
|
static::creating(function ($model) {
|
|
|
|
$model->status = 'pending';
|
|
|
|
$model->checkCronJob();
|
|
|
|
});
|
|
|
|
|
|
|
|
static::created(function ($model) {
|
2024-05-07 13:40:06 +00:00
|
|
|
ProcessHostingSubscriptionBackup::dispatch($model->id);
|
2024-04-25 13:58:30 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
static::deleting(function ($model) {
|
2024-05-07 14:07:09 +00:00
|
|
|
if (is_file($model->file_path)) {
|
|
|
|
shell_exec('rm -rf ' . $model->file_path);
|
|
|
|
}
|
2024-04-25 13:58:30 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
public function checkCronJob()
|
|
|
|
{
|
2024-05-02 08:55:21 +00:00
|
|
|
$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();
|
|
|
|
}
|
|
|
|
|
2024-04-25 13:58:30 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function checkBackup()
|
|
|
|
{
|
2024-04-26 08:04:49 +00:00
|
|
|
$findHostingSubscription = HostingSubscription::select(['id'])
|
|
|
|
->where('id', $this->hosting_subscription_id)
|
|
|
|
->first();
|
2024-05-02 16:41:20 +00:00
|
|
|
|
2024-04-26 08:04:49 +00:00
|
|
|
if (! $findHostingSubscription) {
|
|
|
|
$this->delete();
|
|
|
|
return [
|
|
|
|
'status' => 'failed',
|
|
|
|
'message' => 'Hosting subscription not found'
|
|
|
|
];
|
|
|
|
}
|
|
|
|
|
2024-04-25 13:58:30 +00:00
|
|
|
if ($this->status == BackupStatus::Processing) {
|
|
|
|
|
2024-05-02 16:41:51 +00:00
|
|
|
$backupDoneFile = $this->path.'/backup.done';
|
2024-05-02 16:41:20 +00:00
|
|
|
$backupZipFile = $this->file_path;
|
|
|
|
|
|
|
|
if (file_exists($backupDoneFile) && file_exists($backupZipFile)) {
|
2024-04-26 09:47:53 +00:00
|
|
|
|
2024-05-02 16:41:20 +00:00
|
|
|
$this->size = filesize($this->file_path);
|
2024-04-25 13:58:30 +00:00
|
|
|
$this->status = 'completed';
|
|
|
|
$this->completed = true;
|
|
|
|
$this->completed_at = now();
|
|
|
|
$this->save();
|
2024-04-26 09:47:53 +00:00
|
|
|
|
2024-05-02 16:41:20 +00:00
|
|
|
ShellApi::safeDelete($this->path,[
|
|
|
|
$this->root_path
|
|
|
|
]);
|
|
|
|
ShellApi::safeDelete($this->temp_path,[
|
|
|
|
$this->root_path
|
|
|
|
]);
|
2024-04-26 09:47:53 +00:00
|
|
|
|
2024-04-25 13:58:30 +00:00
|
|
|
return [
|
|
|
|
'status' => 'completed',
|
|
|
|
'message' => 'Backup completed'
|
|
|
|
];
|
|
|
|
}
|
|
|
|
|
|
|
|
$checkProcess = shell_exec('ps -p ' . $this->process_id . ' | grep ' . $this->process_id);
|
2024-05-09 13:46:15 +00:00
|
|
|
|
2024-04-25 13:58:30 +00:00
|
|
|
if (Str::contains($checkProcess, $this->process_id)) {
|
|
|
|
|
2024-05-09 12:16:11 +00:00
|
|
|
$this->size = 0;
|
2024-05-09 13:58:36 +00:00
|
|
|
$this->backup_log = "Backup is started with process id: $this->process_id";
|
2024-04-25 13:58:30 +00:00
|
|
|
$this->save();
|
|
|
|
|
|
|
|
return [
|
|
|
|
'status' => 'processing',
|
|
|
|
'message' => 'Backup is still processing'
|
|
|
|
];
|
|
|
|
} else {
|
|
|
|
$this->status = 'failed';
|
2024-05-09 13:58:36 +00:00
|
|
|
$this->backup_log = "Backup failed. Process not found";
|
2024-04-25 13:58:30 +00:00
|
|
|
$this->save();
|
|
|
|
return [
|
|
|
|
'status' => 'failed',
|
2024-05-09 13:46:15 +00:00
|
|
|
'message' => 'Backup failed. Process not found'
|
2024-04-25 13:58:30 +00:00
|
|
|
];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public function startBackup()
|
|
|
|
{
|
2024-04-26 08:19:12 +00:00
|
|
|
$findHostingSubscription = HostingSubscription::where('id', $this->hosting_subscription_id)
|
2024-04-26 08:04:49 +00:00
|
|
|
->first();
|
|
|
|
if (! $findHostingSubscription) {
|
|
|
|
$this->delete();
|
|
|
|
return [
|
|
|
|
'status' => 'failed',
|
|
|
|
'message' => 'Hosting subscription not found'
|
|
|
|
];
|
|
|
|
}
|
|
|
|
|
2024-05-09 13:58:36 +00:00
|
|
|
if ($this->status !== BackupStatus::Pending) {
|
2024-04-25 13:58:30 +00:00
|
|
|
return [
|
2024-05-09 13:58:36 +00:00
|
|
|
'status' => 'failed',
|
|
|
|
'message' => 'Backup already started'
|
2024-04-25 13:58:30 +00:00
|
|
|
];
|
|
|
|
}
|
2024-05-09 13:58:36 +00:00
|
|
|
|
2024-04-26 08:19:12 +00:00
|
|
|
$findMainDomain = Domain::where('hosting_subscription_id', $findHostingSubscription->id)
|
|
|
|
->where('is_main', 1)
|
|
|
|
->first();
|
|
|
|
if (! $findMainDomain) {
|
|
|
|
$this->delete();
|
|
|
|
return [
|
|
|
|
'status' => 'failed',
|
|
|
|
'message' => 'Main domain not found'
|
|
|
|
];
|
|
|
|
}
|
2024-04-26 08:26:35 +00:00
|
|
|
|
2024-05-02 16:33:47 +00:00
|
|
|
$backupStorageRootPath = '/var/lib/phyre/backups/hosting_subscriptions';
|
|
|
|
$backupPath = $backupStorageRootPath . '/' . $findHostingSubscription->customer_id;
|
|
|
|
|
2024-04-25 13:58:30 +00:00
|
|
|
$backupTempPath = $backupPath.'/temp';
|
2024-04-26 08:26:35 +00:00
|
|
|
shell_exec('mkdir -p ' . $backupTempPath);
|
2024-04-25 13:58:30 +00:00
|
|
|
|
2024-05-02 16:33:47 +00:00
|
|
|
$backupFileName = Str::slug($findHostingSubscription->system_username .'-'. date('Ymd-His')) . '.zip';
|
|
|
|
$backupFilePath = $backupStorageRootPath.'/'.$backupFileName;
|
2024-04-25 13:58:30 +00:00
|
|
|
|
2024-05-02 16:33:47 +00:00
|
|
|
$backupLogFilePath = $backupPath.'/backup.log';
|
2024-04-26 08:26:35 +00:00
|
|
|
|
2024-04-26 11:49:00 +00:00
|
|
|
$backupTempScript = '/tmp/backup-script-'.$this->id.'.sh';
|
|
|
|
$shellFileContent = '';
|
2024-05-02 16:33:47 +00:00
|
|
|
$shellFileContent .= 'echo "Backup up user: '.$findHostingSubscription->system_username .'"'. PHP_EOL;
|
2024-04-26 12:26:18 +00:00
|
|
|
$shellFileContent .= 'echo "Backup filename: '.$backupFileName.'"' . PHP_EOL;
|
2024-04-25 13:58:30 +00:00
|
|
|
|
2024-04-26 11:49:00 +00:00
|
|
|
if ($this->backup_type == 'full') {
|
|
|
|
$shellFileContent .= 'cp -r /home/' . $findHostingSubscription->system_username . ' ' . $backupTempPath . PHP_EOL;
|
2024-05-02 17:15:25 +00:00
|
|
|
$shellFileContent .= 'rsync -azP '. $backupTempPath . '/' . $findHostingSubscription->system_username . '/ ' . $backupTempPath . PHP_EOL;
|
|
|
|
$shellFileContent .= 'rm -rf '. $backupTempPath . '/' . $findHostingSubscription->system_username . '/' . PHP_EOL;
|
2024-04-26 11:49:00 +00:00
|
|
|
}
|
2024-04-25 13:58:30 +00:00
|
|
|
|
2024-04-26 11:49:00 +00:00
|
|
|
if ($this->backup_type == 'full' || $this->backup_type == 'database') {
|
2024-05-02 17:59:16 +00:00
|
|
|
|
|
|
|
// Export Phyre Panel database
|
|
|
|
$mysqlAuthConf = '/root/.phyre-mysql.cnf';
|
|
|
|
$mysqlAuthContent = '[client]' . PHP_EOL;
|
2024-05-10 16:56:58 +00:00
|
|
|
$mysqlAuthContent .= 'user="' . PhyreConfig::get('MYSQL_ROOT_USERNAME') .'"'. PHP_EOL;
|
|
|
|
$mysqlAuthContent .= 'password="' . PhyreConfig::get('MYSQL_ROOT_PASSWORD') . '"' . PHP_EOL;
|
2024-05-02 17:59:16 +00:00
|
|
|
file_put_contents($mysqlAuthConf, $mysqlAuthContent);
|
|
|
|
|
2024-04-26 11:49:00 +00:00
|
|
|
$getDatabases = Database::where('hosting_subscription_id', $findHostingSubscription->id)
|
2024-04-26 12:10:51 +00:00
|
|
|
->where(function ($query) {
|
|
|
|
$query->where('is_remote_database_server', '0')
|
|
|
|
->orWhereNull('is_remote_database_server');
|
|
|
|
})
|
2024-04-26 11:49:00 +00:00
|
|
|
->get();
|
2024-04-26 12:10:51 +00:00
|
|
|
|
2024-04-26 11:49:00 +00:00
|
|
|
if ($getDatabases->count() > 0) {
|
|
|
|
foreach ($getDatabases as $database) {
|
2024-04-26 12:10:51 +00:00
|
|
|
$databaseName = $database->database_name_prefix . $database->database_name;
|
2024-04-26 11:49:00 +00:00
|
|
|
|
2024-04-26 12:26:18 +00:00
|
|
|
$shellFileContent .= 'echo "Backup up database: ' . $databaseName .'" '. PHP_EOL;
|
2024-05-02 16:33:47 +00:00
|
|
|
$shellFileContent .= 'mkdir -p '.$backupTempPath . '/databases' . PHP_EOL;
|
|
|
|
$databaseBackupPath = $backupTempPath . '/databases/' . $databaseName . '.sql';
|
2024-05-02 17:59:16 +00:00
|
|
|
$shellFileContent .= 'mysqldump --defaults-extra-file='.$mysqlAuthConf.' "'.$databaseName.'" > '.$databaseBackupPath . PHP_EOL;
|
2024-04-26 11:49:00 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-04-26 08:26:35 +00:00
|
|
|
|
2024-05-02 16:33:47 +00:00
|
|
|
// With find, we can search for all files,directories (including hidden) in the current directory and zip them
|
2024-05-10 14:13:25 +00:00
|
|
|
$shellFileContent .= 'cd '.$backupTempPath .' && find . -exec zip --symlinks -r '.$backupFilePath.' {} \;'. PHP_EOL;
|
2024-04-25 13:58:30 +00:00
|
|
|
|
2024-04-26 11:49:00 +00:00
|
|
|
$shellFileContent .= 'rm -rf '.$backupTempPath.PHP_EOL;
|
|
|
|
$shellFileContent .= 'echo "Backup complete"' . PHP_EOL;
|
2024-05-02 16:33:47 +00:00
|
|
|
$shellFileContent .= 'touch ' . $backupPath. '/backup.done' . PHP_EOL;
|
2024-04-26 12:26:18 +00:00
|
|
|
$shellFileContent .= 'rm -rf ' . $backupTempScript . PHP_EOL;
|
2024-04-25 13:58:30 +00:00
|
|
|
|
2024-04-26 11:49:00 +00:00
|
|
|
file_put_contents($backupTempScript, $shellFileContent);
|
2024-04-25 13:58:30 +00:00
|
|
|
|
2024-04-26 11:49:00 +00:00
|
|
|
$processId = shell_exec('bash '.$backupTempScript.' >> ' . $backupLogFilePath . ' & echo $!');
|
|
|
|
$processId = intval($processId);
|
2024-04-25 13:58:30 +00:00
|
|
|
|
2024-04-26 11:49:00 +00:00
|
|
|
if ($processId > 0 && is_numeric($processId)) {
|
|
|
|
|
2024-05-02 16:33:47 +00:00
|
|
|
$this->path = $backupPath;
|
|
|
|
$this->root_path = $backupStorageRootPath;
|
|
|
|
$this->temp_path = $backupTempPath;
|
|
|
|
$this->file_path = $backupFilePath;
|
|
|
|
$this->file_name = $backupFileName;
|
|
|
|
|
2024-04-26 11:49:00 +00:00
|
|
|
$this->status = 'processing';
|
|
|
|
$this->queued = true;
|
|
|
|
$this->queued_at = now();
|
|
|
|
$this->process_id = $processId;
|
|
|
|
$this->save();
|
2024-04-26 08:04:49 +00:00
|
|
|
|
2024-04-26 11:49:00 +00:00
|
|
|
return [
|
|
|
|
'status' => 'processing',
|
|
|
|
'message' => 'Backup started'
|
|
|
|
];
|
|
|
|
} else {
|
|
|
|
$this->status = 'failed';
|
|
|
|
$this->save();
|
|
|
|
return [
|
|
|
|
'status' => 'failed',
|
|
|
|
'message' => 'Backup failed to start'
|
|
|
|
];
|
2024-04-25 13:58:30 +00:00
|
|
|
}
|
2024-04-26 11:49:00 +00:00
|
|
|
|
2024-04-25 13:58:30 +00:00
|
|
|
}
|
2024-04-26 16:29:28 +00:00
|
|
|
|
|
|
|
public function hostingSubscription()
|
|
|
|
{
|
|
|
|
return $this->belongsTo(HostingSubscription::class);
|
|
|
|
}
|
2024-04-25 13:49:19 +00:00
|
|
|
}
|