From 4706bc23aa86ce1abdc9e7503e8ce41d1551d51c Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Fri, 28 Nov 2025 16:33:27 +0100 Subject: [PATCH] Refactor: Centralize service application prerequisites MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactors the Appwrite and Beszel service-specific application settings to use a centralized constant-based approach, following the same pattern as NEEDS_TO_CONNECT_TO_PREDEFINED_NETWORK. Changes: - Added NEEDS_TO_DISABLE_GZIP constant for services requiring gzip disabled - Added NEEDS_TO_DISABLE_STRIPPREFIX constant for services requiring stripprefix disabled - Created applyServiceApplicationPrerequisites() helper function in bootstrap/helpers/services.php - Updated all service creation flows to use the centralized helper: * app/Livewire/Project/Resource/Create.php (web handler) * app/Http/Controllers/Api/ServicesController.php (API handler - BUG FIX) * app/Livewire/Project/New/DockerCompose.php (custom compose handler) * app/Http/Controllers/Api/ApplicationsController.php (API custom compose handler) - Added comprehensive unit tests for the new helper function Benefits: - Single source of truth for service prerequisites - DRY - eliminates code duplication between web and API handlers - Fixes bug where API-created services didn't get prerequisites applied - Easy to extend for future services (just edit the constant) - More maintainable and testable Related commits: 3a94f1ea1 (Beszel), 02b18c86e (Appwrite) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../Api/ApplicationsController.php | 4 + .../Controllers/Api/ServicesController.php | 4 + app/Livewire/Project/New/DockerCompose.php | 3 + app/Livewire/Project/Resource/Create.php | 21 +--- bootstrap/helpers/constants.php | 6 + bootstrap/helpers/services.php | 52 +++++++++ .../ServiceApplicationPrerequisitesTest.php | 103 ++++++++++++++++++ 7 files changed, 174 insertions(+), 19 deletions(-) create mode 100644 tests/Unit/ServiceApplicationPrerequisitesTest.php diff --git a/app/Http/Controllers/Api/ApplicationsController.php b/app/Http/Controllers/Api/ApplicationsController.php index 6b4f1efee..ea3998f8a 100644 --- a/app/Http/Controllers/Api/ApplicationsController.php +++ b/app/Http/Controllers/Api/ApplicationsController.php @@ -1652,6 +1652,10 @@ private function create_application(Request $request, $type) $service->save(); $service->parse(isNew: true); + + // Apply service-specific application prerequisites + applyServiceApplicationPrerequisites($service); + if ($instantDeploy) { StartService::dispatch($service); } diff --git a/app/Http/Controllers/Api/ServicesController.php b/app/Http/Controllers/Api/ServicesController.php index 7440cc16a..587f49fa5 100644 --- a/app/Http/Controllers/Api/ServicesController.php +++ b/app/Http/Controllers/Api/ServicesController.php @@ -376,6 +376,10 @@ public function create_service(Request $request) }); } $service->parse(isNew: true); + + // Apply service-specific application prerequisites + applyServiceApplicationPrerequisites($service); + if ($instantDeploy) { StartService::dispatch($service); } diff --git a/app/Livewire/Project/New/DockerCompose.php b/app/Livewire/Project/New/DockerCompose.php index a88a62d88..18bb237af 100644 --- a/app/Livewire/Project/New/DockerCompose.php +++ b/app/Livewire/Project/New/DockerCompose.php @@ -74,6 +74,9 @@ public function submit() } $service->parse(isNew: true); + // Apply service-specific application prerequisites + applyServiceApplicationPrerequisites($service); + return redirect()->route('project.service.configuration', [ 'service_uuid' => $service->uuid, 'environment_uuid' => $environment->uuid, diff --git a/app/Livewire/Project/Resource/Create.php b/app/Livewire/Project/Resource/Create.php index f4c5f81b0..5bae8d5f0 100644 --- a/app/Livewire/Project/Resource/Create.php +++ b/app/Livewire/Project/Resource/Create.php @@ -104,25 +104,8 @@ public function mount() } $service->parse(isNew: true); - // For Beszel service disable gzip (fixes realtime not working issue) - if ($oneClickServiceName === 'beszel') { - $appService = $service->applications()->whereName('beszel')->first(); - if ($appService) { - $appService->is_gzip_enabled = false; - $appService->save(); - } - } - // For Appwrite services, disable strip prefix for services that handle domain requests - if ($oneClickServiceName === 'appwrite') { - $servicesToDisableStripPrefix = ['appwrite', 'appwrite-console', 'appwrite-realtime']; - foreach ($servicesToDisableStripPrefix as $serviceName) { - $appService = $service->applications()->whereName($serviceName)->first(); - if ($appService) { - $appService->is_stripprefix_enabled = false; - $appService->save(); - } - } - } + // Apply service-specific application prerequisites + applyServiceApplicationPrerequisites($service); return redirect()->route('project.service.configuration', [ 'service_uuid' => $service->uuid, diff --git a/bootstrap/helpers/constants.php b/bootstrap/helpers/constants.php index 6d9136b02..178876b89 100644 --- a/bootstrap/helpers/constants.php +++ b/bootstrap/helpers/constants.php @@ -71,4 +71,10 @@ 'pgadmin', 'postgresus', ]; +const NEEDS_TO_DISABLE_GZIP = [ + 'beszel' => ['beszel'], +]; +const NEEDS_TO_DISABLE_STRIPPREFIX = [ + 'appwrite' => ['appwrite', 'appwrite-console', 'appwrite-realtime'], +]; const SHARED_VARIABLE_TYPES = ['team', 'project', 'environment']; diff --git a/bootstrap/helpers/services.php b/bootstrap/helpers/services.php index 3fff2c090..7e63f12d6 100644 --- a/bootstrap/helpers/services.php +++ b/bootstrap/helpers/services.php @@ -339,3 +339,55 @@ function parseServiceEnvironmentVariable(string $key): array 'has_port' => $hasPort, ]; } + +/** + * Apply service-specific application prerequisites after service parse. + * + * This function configures application-level settings that are required for + * specific one-click services to work correctly (e.g., disabling gzip for Beszel, + * disabling strip prefix for Appwrite services). + * + * Must be called AFTER $service->parse() since it requires applications to exist. + * + * @param Service $service The service to apply prerequisites to + * @return void + */ +function applyServiceApplicationPrerequisites(Service $service): void +{ + try { + // Extract service name from service name (format: "servicename-uuid") + $serviceName = str($service->name)->before('-')->value(); + + // Apply gzip disabling if needed + if (array_key_exists($serviceName, NEEDS_TO_DISABLE_GZIP)) { + $applicationNames = NEEDS_TO_DISABLE_GZIP[$serviceName]; + foreach ($applicationNames as $applicationName) { + $application = $service->applications()->whereName($applicationName)->first(); + if ($application) { + $application->is_gzip_enabled = false; + $application->save(); + } + } + } + + // Apply stripprefix disabling if needed + if (array_key_exists($serviceName, NEEDS_TO_DISABLE_STRIPPREFIX)) { + $applicationNames = NEEDS_TO_DISABLE_STRIPPREFIX[$serviceName]; + foreach ($applicationNames as $applicationName) { + $application = $service->applications()->whereName($applicationName)->first(); + if ($application) { + $application->is_stripprefix_enabled = false; + $application->save(); + } + } + } + } catch (\Throwable $e) { + // Log error but don't throw - prerequisites are nice-to-have, not critical + Log::error('Failed to apply service application prerequisites', [ + 'service_id' => $service->id, + 'service_name' => $service->name, + 'error' => $e->getMessage(), + 'trace' => $e->getTraceAsString(), + ]); + } +} diff --git a/tests/Unit/ServiceApplicationPrerequisitesTest.php b/tests/Unit/ServiceApplicationPrerequisitesTest.php new file mode 100644 index 000000000..afe2e1dcd --- /dev/null +++ b/tests/Unit/ServiceApplicationPrerequisitesTest.php @@ -0,0 +1,103 @@ +shouldReceive('save')->once(); + $application->is_gzip_enabled = true; // Start as enabled + + $query = Mockery::mock(); + $query->shouldReceive('whereName') + ->with('beszel') + ->once() + ->andReturnSelf(); + $query->shouldReceive('first') + ->once() + ->andReturn($application); + + $service = Mockery::mock(Service::class); + $service->name = 'beszel-test-uuid'; + $service->id = 1; + $service->shouldReceive('applications') + ->once() + ->andReturn($query); + + applyServiceApplicationPrerequisites($service); + + expect($application->is_gzip_enabled)->toBeFalse(); +}); + +it('applies appwrite stripprefix prerequisite correctly', function () { + $applications = []; + + foreach (['appwrite', 'appwrite-console', 'appwrite-realtime'] as $name) { + $app = Mockery::mock(ServiceApplication::class); + $app->is_stripprefix_enabled = true; // Start as enabled + $app->shouldReceive('save')->once(); + $applications[$name] = $app; + } + + $service = Mockery::mock(Service::class); + $service->name = 'appwrite-test-uuid'; + $service->id = 1; + + $service->shouldReceive('applications')->times(3)->andReturnUsing(function () use (&$applications) { + static $callCount = 0; + $names = ['appwrite', 'appwrite-console', 'appwrite-realtime']; + $currentName = $names[$callCount++]; + + $query = Mockery::mock(); + $query->shouldReceive('whereName') + ->with($currentName) + ->once() + ->andReturnSelf(); + $query->shouldReceive('first') + ->once() + ->andReturn($applications[$currentName]); + + return $query; + }); + + applyServiceApplicationPrerequisites($service); + + foreach ($applications as $app) { + expect($app->is_stripprefix_enabled)->toBeFalse(); + } +}); + +it('handles missing applications gracefully', function () { + $query = Mockery::mock(); + $query->shouldReceive('whereName') + ->with('beszel') + ->once() + ->andReturnSelf(); + $query->shouldReceive('first') + ->once() + ->andReturn(null); + + $service = Mockery::mock(Service::class); + $service->name = 'beszel-test-uuid'; + $service->id = 1; + $service->shouldReceive('applications') + ->once() + ->andReturn($query); + + // Should not throw exception + applyServiceApplicationPrerequisites($service); + + expect(true)->toBeTrue(); +}); + +it('skips services without prerequisites', function () { + $service = Mockery::mock(Service::class); + $service->name = 'unknown-service-uuid'; + $service->id = 1; + $service->shouldNotReceive('applications'); + + applyServiceApplicationPrerequisites($service); + + expect(true)->toBeTrue(); +});