mirror of
https://github.com/PhyreApps/PhyrePanel.git
synced 2024-11-25 00:50:32 +00:00
Compare commits
5 commits
1b4c35819e
...
aa48915b05
Author | SHA1 | Date | |
---|---|---|---|
|
aa48915b05 | ||
|
71ba6c2946 | ||
|
e68b198d74 | ||
|
bae4213184 | ||
|
39a4a76183 |
5 changed files with 213 additions and 15 deletions
|
@ -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,9 +56,13 @@ class RunBackup extends Command
|
|||
->get();
|
||||
|
||||
if ($getPendingBackups->count() > 0) {
|
||||
foreach ($getPendingBackups as $pendingBackup) {
|
||||
$pendingBackup->startBackup();
|
||||
$this->info('Backup started.. ');
|
||||
if ($getPendingBackups->count() > 1) {
|
||||
$this->info('Multiple backups are pending..');
|
||||
} else {
|
||||
foreach ($getPendingBackups as $pendingBackup) {
|
||||
$pendingBackup->startBackup();
|
||||
$this->info('Backup started.. ');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
65
web/app/Console/Commands/RunUploadBackupsToRemoteServers.php
Normal file
65
web/app/Console/Commands/RunUploadBackupsToRemoteServers.php
Normal 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.');
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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.'
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
44
web/tests/Unit/DockerTest.php
Normal file
44
web/tests/Unit/DockerTest.php
Normal 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');
|
||||
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in a new issue