Compare commits

...

5 commits

Author SHA1 Message Date
Bozhidar
aa48915b05 Update DockerTest.php 2024-04-29 20:06:33 +03:00
Bozhidar
71ba6c2946 update 2024-04-29 19:34:15 +03:00
Bozhidar
e68b198d74 update 2024-04-29 19:27:27 +03:00
Bozhidar
bae4213184 Update RunBackup.php 2024-04-29 18:13:59 +03:00
Bozhidar
39a4a76183 add env backup 2024-04-29 18:12:08 +03:00
5 changed files with 213 additions and 15 deletions

View file

@ -37,6 +37,10 @@ class RunBackup extends Command
}
$findBackupsToday = Backup::where('created_at', '>=', Carbon::now()->subHours(24))
->where(function ($query) {
$query->where('status', 'completed')
->orWhere('status', 'processing');
})
->first();
if (! $findBackupsToday) {
@ -52,11 +56,15 @@ class RunBackup extends Command
->get();
if ($getPendingBackups->count() > 0) {
if ($getPendingBackups->count() > 1) {
$this->info('Multiple backups are pending..');
} else {
foreach ($getPendingBackups as $pendingBackup) {
$pendingBackup->startBackup();
$this->info('Backup started.. ');
}
}
}
// Check for processing backups
$getRunningBackups = Backup::where('status', 'processing')->get();

View file

@ -0,0 +1,65 @@
<?php
namespace App\Console\Commands;
use App\Models\Backup;
use App\Models\HostingSubscriptionBackup;
use App\Models\RemoteBackupServer;
use Illuminate\Console\Command;
class RunUploadBackupsToRemoteServers extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'phyre:run-upload-backups-to-remote-servers';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Command description';
/**
* Execute the console command.
*/
public function handle()
{
$getRemoteBackupServers = RemoteBackupServer::all();
if ($getRemoteBackupServers->count() > 0) {
foreach ($getRemoteBackupServers as $remoteBackupServer) {
$remoteBackupServer->healthCheck();
if ($remoteBackupServer->status == 'offline') {
$this->info('Skipping ' . $remoteBackupServer->name . ' because it is offline.');
continue;
}
$this->info('Uploading backups to ' . $remoteBackupServer->name . '...');
$findBackups = Backup::where('status', 'completed')->get();
if ($findBackups->count() > 0) {
foreach ($findBackups as $backup) {
$uploadStatus = $remoteBackupServer->uploadFile($backup->filepath, 'phyre-system-backups');
}
} else {
$this->info('No backups found to upload.');
}
$findHostingSubscriptionBackups = HostingSubscriptionBackup::where('status', 'completed')->get();
if ($findHostingSubscriptionBackups->count() > 0) {
foreach ($findHostingSubscriptionBackups as $hostingSubscriptionBackup) {
$uploadStatus = $remoteBackupServer->uploadFile($hostingSubscriptionBackup->filepath, 'phyre-hosting-subscription-backups');
}
} else {
$this->info('No hosting subscription backups found to upload.');
}
}
} else {
$this->info('No remote backup servers found.');
}
}
}

View file

@ -4,9 +4,11 @@ namespace App\Models;
use App\Filament\Enums\BackupStatus;
use App\Helpers;
use Dotenv\Dotenv;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Support\Env;
use Illuminate\Support\Number;
use Illuminate\Support\Str;
@ -73,11 +75,53 @@ class Backup extends Model
$backupDoneFile = $this->path.'/backup.done';
if (file_exists($backupDoneFile)) {
$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
];
}
$this->size = Helpers::checkPathSize($this->path);
$this->status = 'completed';
$this->completed = true;
$this->completed_at = now();
$this->save();
return [
'status' => 'completed',
'message' => 'Backup completed'
@ -143,9 +187,17 @@ class Backup extends Model
$backupTempScript = '/tmp/backup-script-'.$this->id.'.sh';
$shellFileContent = '';
$shellFileContent .= 'echo "Backup up Phyre Panel files"'. PHP_EOL;
$shellFileContent .= 'echo "Backup Phyre Panel files"'. PHP_EOL;
// Export Phyre Panel database
$shellFileContent .= 'mysqldump -u "'.env('MYSQl_ROOT_USERNAME').'" -p"'.env('MYSQL_ROOT_PASSWORD').'" "'.env('DB_DATABASE').'" > '.$databaseBackupPath . PHP_EOL;
$shellFileContent .= 'cd '.$backupTempPath .' && tar -czvf '.$backupFilePath.' ./* '. PHP_EOL;
// 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;
$shellFileContent .= 'rm -rf '.$backupTempPath.PHP_EOL;
$shellFileContent .= 'echo "Backup complete"' . PHP_EOL;

View file

@ -5,6 +5,7 @@ namespace App\Models;
use Doctrine\DBAL\DriverManager;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Symfony\Component\Process\Process;
class RemoteBackupServer extends Model
{
@ -30,9 +31,6 @@ class RemoteBackupServer extends Model
$model->healthCheck();
});
static::updated(function ($model) {
$model->healthCheck();
});
}
public function healthCheck()
@ -48,19 +46,19 @@ class RemoteBackupServer extends Model
$path = trim($this->path);
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, 'ftp://'.$hostname.':'.$port.'/'.$path.'/');
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl, CURLOPT_URL, 'ftp://'.$hostname.':'.$port.'/');
curl_setopt($curl, CURLOPT_TIMEOUT, 30);
curl_setopt($curl, CURLOPT_USERPWD, $username.':'.$password);
curl_setopt($curl, CURLOPT_FTPLISTONLY, 1);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($curl, CURLOPT_USERPWD, "$username:$password");
$curlResponse = curl_exec($curl);
curl_close($curl);
if ($curlResponse) {
$this->status = 'online';
if ($curlResponse === false) {
$this->status = 'offline';
$this->save();
return;
} else {
$this->status = 'offline';
$this->status = 'online';
$this->save();
return;
}
@ -74,4 +72,35 @@ class RemoteBackupServer extends Model
}
public function uploadFile($filepath, $directory)
{
$username = trim($this->username);
$password = trim($this->password);
$hostname = trim($this->hostname);
$port = trim($this->port);
if ($this->type == 'ftp') {
$directory = trim($directory);
$uploadCurlCommand = "curl -T $filepath ftp://$hostname:$port/$directory/ -u '$username:$password' --ftp-create-dirs";
$uploadCurlCommand = trim($uploadCurlCommand);
$uploadCurlProcess = Process::fromShellCommandline($uploadCurlCommand);
$uploadCurlProcess->run();
if (!$uploadCurlProcess->isSuccessful()) {
return [
'status' => 'error',
'message' => 'Failed to upload backup to remote server.'
];
}
return [
'status' => 'success',
'message' => 'Backup uploaded successfully.'
];
}
}
}

View file

@ -0,0 +1,44 @@
<?php
namespace tests\Unit;
use App\Models\User;
use Livewire\Livewire;
use Modules\Docker\Filament\Clusters\Docker\Pages\DockerCatalog;
use Modules\Docker\PostInstall;
use Tests\TestCase;
class DockerTest extends TestCase
{
public function testDocker()
{
$docker = new PostInstall();
$docker->setLogFile('/tmp/phyrepanel-docker-install.log');
$docker->run();
$dockerIsInstalled = false;
for ($i = 0; $i < 50; $i++) {
$logFile = file_get_contents('/tmp/phyrepanel-docker-install.log');
if (strpos($logFile, 'Done!') !== false) {
$dockerIsInstalled = true;
break;
}
sleep(1);
}
$this->assertTrue($dockerIsInstalled);
$this->actingAs(User::factory()->create());
$livewireCatalogIndex = Livewire::test(DockerCatalog::class)
->set('keyword', 'nginx')
->assertSee('nginx');
$viewData = $livewireCatalogIndex->viewData('dockerImages');
$this->assertNotEmpty($viewData);
$livewireCatalogIndex->set('keyword', 'non-existing-image')
->assertDontSee('non-existing-image');
}
}