Merge remote-tracking branch 'origin/next' into audit-policies

# Conflicts:
#	app/Http/Controllers/Api/SecurityController.php
#	app/Http/Controllers/Api/ServersController.php
#	app/Livewire/Admin/Index.php
#	app/Livewire/Destination/Show.php
#	app/Livewire/NavbarDeleteTeam.php
#	app/Livewire/Project/Application/Previews.php
#	app/Livewire/Project/DeleteProject.php
#	app/Livewire/Project/Shared/ResourceOperations.php
#	app/Livewire/Server/Resources.php
#	app/Livewire/Server/ValidateAndInstall.php
#	app/Livewire/Storage/Show.php
#	resources/views/livewire/dashboard.blade.php
#	resources/views/livewire/project/application/heading.blade.php
#	resources/views/livewire/project/database/heading.blade.php
#	resources/views/livewire/project/service/heading.blade.php
#	resources/views/livewire/project/shared/environment-variable/show.blade.php
#	resources/views/livewire/project/shared/scheduled-task/show.blade.php
#	tests/Feature/Security/TrustHostsMiddlewareTest.php
This commit is contained in:
Andras Bacsai committed 2026-04-19 15:19:37 +02:00
commit 43f33d94ad
685 files changed
+41904 -5674

No files matched your search

+15 -4
View File
@@ -40,12 +40,16 @@ class FileStorage extends Component
#[Validate(['required', 'boolean'])]
public bool $isBasedOnGit = false;
#[Validate(['required', 'boolean'])]
public bool $isPreviewSuffixEnabled = true;
protected $rules = [
'fileStorage.is_directory' => 'required',
'fileStorage.fs_path' => 'required',
'fileStorage.mount_path' => 'required',
'content' => 'nullable',
'isBasedOnGit' => 'required|boolean',
'isPreviewSuffixEnabled' => 'required|boolean',
];
public function mount()
@@ -71,12 +75,14 @@ class FileStorage extends Component
// Sync to model
$this->fileStorage->content = $this->content;
$this->fileStorage->is_based_on_git = $this->isBasedOnGit;
$this->fileStorage->is_preview_suffix_enabled = $this->isPreviewSuffixEnabled;
$this->fileStorage->save();
} else {
// Sync from model
$this->content = $this->fileStorage->content;
$this->isBasedOnGit = $this->fileStorage->is_based_on_git;
$this->isPreviewSuffixEnabled = $this->fileStorage->is_preview_suffix_enabled ?? true;
}
}
@@ -133,12 +139,12 @@ class FileStorage extends Component
}
}
public function delete($password)
public function delete($password, $selectedActions = [])
{
$this->authorize('update', $this->resource);
if (! verifyPasswordConfirmation($password, $this)) {
return;
return 'The provided password is incorrect.';
}
try {
@@ -157,6 +163,8 @@ class FileStorage extends Component
} finally {
$this->dispatch('refreshStorages');
}
return true;
}
public function submit()
@@ -172,6 +180,7 @@ class FileStorage extends Component
// Sync component properties to model
$this->fileStorage->content = $this->content;
$this->fileStorage->is_based_on_git = $this->isBasedOnGit;
$this->fileStorage->is_preview_suffix_enabled = $this->isPreviewSuffixEnabled;
$this->fileStorage->save();
$this->fileStorage->saveStorageOnServer();
$this->dispatch('success', 'File updated.');
@@ -184,9 +193,11 @@ class FileStorage extends Component
}
}
public function instantSave()
public function instantSave(): void
{
$this->submit();
$this->authorize('update', $this->resource);
$this->syncData(true);
$this->dispatch('success', 'File updated.');
}
public function render()
+9 -9
View File
@@ -51,9 +51,9 @@ class Index extends Component
public bool $excludeFromStatus = false;
public ?int $publicPort = null;
public mixed $publicPort = null;
public ?int $publicPortTimeout = 3600;
public mixed $publicPortTimeout = 3600;
public bool $isPublic = false;
@@ -91,7 +91,7 @@ class Index extends Component
'description' => 'nullable',
'image' => 'required',
'excludeFromStatus' => 'required|boolean',
'publicPort' => 'nullable|integer',
'publicPort' => 'nullable|integer|min:1|max:65535',
'publicPortTimeout' => 'nullable|integer|min:1',
'isPublic' => 'required|boolean',
'isLogDrainEnabled' => 'required|boolean',
@@ -160,8 +160,8 @@ class Index extends Component
$this->serviceDatabase->description = $this->description;
$this->serviceDatabase->image = $this->image;
$this->serviceDatabase->exclude_from_status = $this->excludeFromStatus;
$this->serviceDatabase->public_port = $this->publicPort;
$this->serviceDatabase->public_port_timeout = $this->publicPortTimeout;
$this->serviceDatabase->public_port = $this->publicPort ?: null;
$this->serviceDatabase->public_port_timeout = $this->publicPortTimeout ?: null;
$this->serviceDatabase->is_public = $this->isPublic;
$this->serviceDatabase->is_log_drain_enabled = $this->isLogDrainEnabled;
} else {
@@ -194,13 +194,13 @@ class Index extends Component
}
}
public function deleteDatabase($password)
public function deleteDatabase($password, $selectedActions = [])
{
try {
$this->authorize('delete', $this->serviceDatabase);
if (! verifyPasswordConfirmation($password, $this)) {
return;
return 'The provided password is incorrect.';
}
$this->serviceDatabase->delete();
@@ -398,13 +398,13 @@ class Index extends Component
}
}
public function deleteApplication($password)
public function deleteApplication($password, $selectedActions = [])
{
try {
$this->authorize('delete', $this->serviceApplication);
if (! verifyPasswordConfirmation($password, $this)) {
return;
return 'The provided password is incorrect.';
}
$this->serviceApplication->delete();
+12 -6
View File
@@ -2,7 +2,10 @@
namespace App\Livewire\Project\Service;
use App\Models\Application;
use App\Models\LocalFileVolume;
use App\Models\LocalPersistentVolume;
use App\Support\ValidationPatterns;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Livewire\Component;
@@ -49,7 +52,7 @@ class Storage extends Component
$this->file_storage_directory_source = application_configuration_dir()."/{$this->resource->uuid}";
}
if ($this->resource->getMorphClass() === \App\Models\Application::class) {
if ($this->resource->getMorphClass() === Application::class) {
if ($this->resource->destination->server->isSwarm()) {
$this->isSwarm = true;
}
@@ -101,10 +104,10 @@ class Storage extends Component
$this->authorize('update', $this->resource);
$this->validate([
'name' => 'required|string',
'name' => ValidationPatterns::volumeNameRules(),
'mount_path' => 'required|string',
'host_path' => $this->isSwarm ? 'required|string' : 'string|nullable',
]);
], ValidationPatterns::volumeNameMessages());
$name = $this->resource->uuid.'-'.$this->name;
@@ -138,7 +141,10 @@ class Storage extends Component
$this->file_storage_path = trim($this->file_storage_path);
$this->file_storage_path = str($this->file_storage_path)->start('/')->value();
if ($this->resource->getMorphClass() === \App\Models\Application::class) {
// Validate path to prevent command injection
validateShellSafePath($this->file_storage_path, 'file storage path');
if ($this->resource->getMorphClass() === Application::class) {
$fs_path = application_configuration_dir().'/'.$this->resource->uuid.$this->file_storage_path;
} elseif (str($this->resource->getMorphClass())->contains('Standalone')) {
$fs_path = database_configuration_dir().'/'.$this->resource->uuid.$this->file_storage_path;
@@ -146,7 +152,7 @@ class Storage extends Component
throw new \Exception('No valid resource type for file mount storage type!');
}
\App\Models\LocalFileVolume::create([
LocalFileVolume::create([
'fs_path' => $fs_path,
'mount_path' => $this->file_storage_path,
'content' => $this->file_storage_content,
@@ -183,7 +189,7 @@ class Storage extends Component
validateShellSafePath($this->file_storage_directory_source, 'storage source path');
validateShellSafePath($this->file_storage_directory_destination, 'storage destination path');
\App\Models\LocalFileVolume::create([
LocalFileVolume::create([
'fs_path' => $this->file_storage_directory_source,
'mount_path' => $this->file_storage_directory_destination,
'is_directory' => true,