PhyrePanel-mirror/web/app/Models/Backup.php

238 lines
7.8 KiB
PHP
Raw Normal View History

2024-04-22 11:14:05 +00:00
<?php
namespace App\Models;
2024-04-25 09:30:01 +00:00
use App\Filament\Enums\BackupStatus;
2024-04-25 12:11:44 +00:00
use App\Helpers;
2024-04-29 15:12:08 +00:00
use Dotenv\Dotenv;
2024-04-22 11:14:05 +00:00
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
2024-04-24 17:51:36 +00:00
use Illuminate\Database\Eloquent\Casts\Attribute;
2024-04-29 15:12:08 +00:00
use Illuminate\Support\Env;
2024-04-25 10:46:57 +00:00
use Illuminate\Support\Number;
2024-04-24 17:51:36 +00:00
use Illuminate\Support\Str;
2024-04-22 11:14:05 +00:00
class Backup extends Model
{
use HasFactory;
2024-04-24 17:51:36 +00:00
2024-04-25 09:30:01 +00:00
const STATUS_PENDING = 'pending';
const STATUS_PROCESSING = 'processing';
const STATUS_COMPLETED = 'completed';
const STATUS_FAILED = 'failed';
const STATUS_CANCELLED = 'cancelled';
2024-04-24 17:51:36 +00:00
protected $fillable = [
'backup_type',
'status',
'path',
'size',
'disk',
];
2024-04-25 09:30:01 +00:00
protected $casts = [
'status' => BackupStatus::class,
];
2024-04-24 18:03:08 +00:00
public static function boot()
{
parent::boot();
static::creating(function ($model) {
$model->status = 'pending';
2024-04-24 19:19:31 +00:00
$model->checkCronJob();
2024-04-24 18:03:08 +00:00
});
2024-04-25 12:11:44 +00:00
static::created(function ($model) {
$model->startBackup();
});
2024-04-25 08:46:24 +00:00
static::deleting(function ($model) {
if (is_dir($model->path)) {
shell_exec('rm -rf ' . $model->path);
}
});
2024-04-24 18:03:08 +00:00
}
2024-04-25 12:38:39 +00:00
public function checkCronJob()
2024-04-24 19:19:31 +00:00
{
2024-04-24 20:18:50 +00:00
$cronJobCommand = 'phyre-php /usr/local/phyre/web/artisan phyre:run-backup';
$findCronJob = CronJob::where('command', $cronJobCommand)->first();
2024-04-24 19:19:31 +00:00
if (! $findCronJob) {
$cronJob = new CronJob();
$cronJob->schedule = '*/5 * * * *';
2024-04-24 20:18:50 +00:00
$cronJob->command = $cronJobCommand;
2024-04-24 19:19:31 +00:00
$cronJob->user = 'root';
$cronJob->save();
2024-04-25 12:38:39 +00:00
return false;
2024-04-24 19:19:31 +00:00
}
2024-04-25 12:38:39 +00:00
return true;
2024-04-24 19:19:31 +00:00
}
2024-04-24 18:03:08 +00:00
public function checkBackup()
{
2024-04-25 10:36:22 +00:00
if ($this->status == BackupStatus::Processing) {
2024-04-24 18:03:08 +00:00
$backupDoneFile = $this->path.'/backup.done';
if (file_exists($backupDoneFile)) {
2024-04-29 15:12:08 +00:00
$tempValidatePath = $this->path.'/temp-validate';
if (! is_dir($tempValidatePath)) {
mkdir($tempValidatePath);
}
shell_exec('tar -xzf '.$this->filepath.' -C '.$tempValidatePath);
$validateDatabaseFile = $tempValidatePath.'/database.sql';
$validateEnvFile = $tempValidatePath.'/env.json';
$errorsBag = [];
if (! file_exists($validateDatabaseFile)) {
$errorsBag[] = 'Database file not found';
}
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',
'errors' => $errorsBag
];
}
2024-04-25 12:11:44 +00:00
$this->size = Helpers::checkPathSize($this->path);
2024-04-24 18:03:08 +00:00
$this->status = 'completed';
$this->completed = true;
$this->completed_at = now();
$this->save();
2024-04-29 15:12:08 +00:00
2024-04-25 10:36:22 +00:00
return [
'status' => 'completed',
'message' => 'Backup completed'
];
}
$checkProcess = shell_exec('ps -p ' . $this->process_id . ' | grep ' . $this->process_id);
if (Str::contains($checkProcess, $this->process_id)) {
2024-04-25 10:46:57 +00:00
2024-04-25 12:11:44 +00:00
$this->size = Helpers::checkPathSize($this->path);
2024-04-25 10:51:02 +00:00
$this->save();
2024-04-25 10:46:57 +00:00
2024-04-25 10:36:22 +00:00
return [
'status' => 'processing',
'message' => 'Backup is still processing'
];
} else {
$this->status = 'failed';
$this->save();
return [
'status' => 'failed',
'message' => 'Backup failed'
];
2024-04-24 18:03:08 +00:00
}
}
}
2024-04-24 17:51:36 +00:00
public function startBackup()
{
2024-04-25 10:36:22 +00:00
if ($this->status == BackupStatus::Processing) {
2024-04-24 17:51:36 +00:00
return [
2024-04-25 09:30:01 +00:00
'status' => 'processing',
'message' => 'Backup is already processing'
2024-04-24 17:51:36 +00:00
];
}
$storagePath = storage_path('backups');
if (! is_dir($storagePath)) {
mkdir($storagePath);
}
2024-04-25 13:23:15 +00:00
$backupPath = $storagePath.'/'.$this->backup_type.'/'.$this->id;
if (!is_dir(dirname($backupPath))) {
mkdir(dirname($backupPath));
}
2024-04-24 17:51:36 +00:00
if (! is_dir($backupPath)) {
mkdir($backupPath);
}
$backupTempPath = $backupPath.'/temp';
if (! is_dir($backupTempPath)) {
mkdir($backupTempPath);
}
2024-04-25 13:23:15 +00:00
2024-04-25 18:57:47 +00:00
if ($this->backup_type == 'full') {
2024-04-25 13:23:15 +00:00
// Export Phyre Panel database
$databaseBackupPath = $backupTempPath.'/database.sql';
// Export Phyre Panel files
$backupFilePath = $backupPath.'/phyre-panel-'.date('Ymd-His').'.tar.gz';
$backupLogFileName = 'backup.log';
$backupLogFilePath = $backupPath.'/'.$backupLogFileName;
$backupTempScript = '/tmp/backup-script-'.$this->id.'.sh';
$shellFileContent = '';
2024-04-29 15:12:08 +00:00
$shellFileContent .= 'echo "Backup Phyre Panel files"'. PHP_EOL;
// Export Phyre Panel database
2024-04-26 12:10:51 +00:00
$shellFileContent .= 'mysqldump -u "'.env('MYSQl_ROOT_USERNAME').'" -p"'.env('MYSQL_ROOT_PASSWORD').'" "'.env('DB_DATABASE').'" > '.$databaseBackupPath . PHP_EOL;
2024-04-29 15:12:08 +00:00
// Export Phyre Panel ENV
$getEnv = Dotenv::createArrayBacked(base_path())->load();
file_put_contents($backupTempPath.'/env.json', json_encode($getEnv, JSON_PRETTY_PRINT));
$shellFileContent .= 'cd '.$backupTempPath .' && tar -pczf '.$backupFilePath.' ./* '. PHP_EOL;
2024-04-25 13:23:15 +00:00
$shellFileContent .= 'rm -rf '.$backupTempPath.PHP_EOL;
$shellFileContent .= 'echo "Backup complete"' . PHP_EOL;
$shellFileContent .= 'touch ' . $backupPath. '/backup.done' . PHP_EOL;
$shellFileContent .= 'rm -rf ' . $backupTempScript;
file_put_contents($backupTempScript, $shellFileContent);
$processId = shell_exec('bash '.$backupTempScript.' >> ' . $backupLogFilePath . ' & echo $!');
$processId = intval($processId);
if ($processId > 0 && is_numeric($processId)) {
$this->path = $backupPath;
$this->filepath = $backupFilePath;
$this->status = 'processing';
$this->queued = true;
$this->queued_at = now();
$this->process_id = $processId;
$this->save();
return [
'status' => 'processing',
'message' => 'System backup started'
];
} else {
$this->status = 'failed';
$this->save();
return [
'status' => 'failed',
'message' => 'System backup failed to start'
];
}
}
2024-04-24 17:51:36 +00:00
}
2024-04-22 11:14:05 +00:00
}