diff --git a/app/Livewire/SharedVariables/Environment/Show.php b/app/Livewire/SharedVariables/Environment/Show.php index 9405b452a..bfbdf9212 100644 --- a/app/Livewire/SharedVariables/Environment/Show.php +++ b/app/Livewire/SharedVariables/Environment/Show.php @@ -51,11 +51,14 @@ public function saveKey($data) } } - public function mount() + public function mount(?string $project_uuid = null, ?string $environment_uuid = null) { $this->parameters = get_route_parameters(); - $this->project = Project::ownedByCurrentTeam()->where('uuid', request()->route('project_uuid'))->firstOrFail(); - $this->environment = $this->project->environments()->where('uuid', request()->route('environment_uuid'))->firstOrFail(); + $projectUuid = $project_uuid ?? request()->route('project_uuid'); + $environmentUuid = $environment_uuid ?? request()->route('environment_uuid'); + + $this->project = Project::ownedByCurrentTeam()->where('uuid', $projectUuid)->firstOrFail(); + $this->environment = $this->project->environments()->where('uuid', $environmentUuid)->firstOrFail(); $this->getDevView(); } diff --git a/app/Livewire/SharedVariables/Project/Show.php b/app/Livewire/SharedVariables/Project/Show.php index 7753a4027..c9f0dcd8e 100644 --- a/app/Livewire/SharedVariables/Project/Show.php +++ b/app/Livewire/SharedVariables/Project/Show.php @@ -44,9 +44,9 @@ public function saveKey($data) } } - public function mount() + public function mount(?string $project_uuid = null) { - $projectUuid = request()->route('project_uuid'); + $projectUuid = $project_uuid ?? request()->route('project_uuid'); $teamId = currentTeam()->id; $project = Project::where('team_id', $teamId)->where('uuid', $projectUuid)->first(); if (! $project) { diff --git a/app/Livewire/SharedVariables/Server/Show.php b/app/Livewire/SharedVariables/Server/Show.php index 6078ef36f..a0498b2b7 100644 --- a/app/Livewire/SharedVariables/Server/Show.php +++ b/app/Livewire/SharedVariables/Server/Show.php @@ -37,6 +37,7 @@ public function saveKey($data) 'value' => $data['value'], 'is_multiline' => $data['is_multiline'], 'is_literal' => $data['is_literal'], + 'comment' => $data['comment'] ?? null, 'type' => 'server', 'team_id' => currentTeam()->id, ]); @@ -47,9 +48,9 @@ public function saveKey($data) } } - public function mount() + public function mount(?string $server_uuid = null) { - $serverUuid = request()->route('server_uuid'); + $serverUuid = $server_uuid ?? request()->route('server_uuid'); $teamId = currentTeam()->id; $server = Server::where('team_id', $teamId)->where('uuid', $serverUuid)->first(); if (! $server) { @@ -140,7 +141,10 @@ private function deleteRemovedVariables($variables) private function updateOrCreateVariables($variables) { $count = 0; - foreach ($variables as $key => $value) { + foreach ($variables as $key => $data) { + $value = is_array($data) ? ($data['value'] ?? '') : $data; + $comment = is_array($data) ? ($data['comment'] ?? null) : null; + // Skip predefined variables if (in_array($key, ['COOLIFY_SERVER_UUID', 'COOLIFY_SERVER_NAME'])) { continue; @@ -149,8 +153,9 @@ private function updateOrCreateVariables($variables) if ($found) { if (! $found->is_shown_once && ! $found->is_multiline) { - if ($found->value !== $value) { + if ($found->value !== $value || $found->comment !== $comment) { $found->value = $value; + $found->comment = $comment; $found->save(); $count++; } @@ -159,6 +164,7 @@ private function updateOrCreateVariables($variables) $this->server->environment_variables()->create([ 'key' => $key, 'value' => $value, + 'comment' => $comment, 'is_multiline' => false, 'is_literal' => false, 'type' => 'server', diff --git a/database/migrations/2025_12_24_095507_add_server_to_shared_environment_variables_table.php b/database/migrations/2025_12_24_095507_add_server_to_shared_environment_variables_table.php index a6a6fe872..66d585069 100644 --- a/database/migrations/2025_12_24_095507_add_server_to_shared_environment_variables_table.php +++ b/database/migrations/2025_12_24_095507_add_server_to_shared_environment_variables_table.php @@ -13,8 +13,10 @@ public function up(): void { DB::transaction(function () { - DB::statement('ALTER TABLE shared_environment_variables DROP CONSTRAINT IF EXISTS shared_environment_variables_type_check'); - DB::statement("ALTER TABLE shared_environment_variables ADD CONSTRAINT shared_environment_variables_type_check CHECK (type IN ('team', 'project', 'environment', 'server'))"); + if (DB::getDriverName() !== 'sqlite') { + DB::statement('ALTER TABLE shared_environment_variables DROP CONSTRAINT IF EXISTS shared_environment_variables_type_check'); + DB::statement("ALTER TABLE shared_environment_variables ADD CONSTRAINT shared_environment_variables_type_check CHECK (type IN ('team', 'project', 'environment', 'server'))"); + } Schema::table('shared_environment_variables', function (Blueprint $table) { $table->foreignId('server_id')->nullable()->constrained()->onDelete('cascade'); // NULL != NULL in PostgreSQL unique indexes, so this only enforces uniqueness @@ -36,8 +38,10 @@ public function down(): void $table->dropForeign(['server_id']); $table->dropColumn('server_id'); }); - DB::statement('ALTER TABLE shared_environment_variables DROP CONSTRAINT IF EXISTS shared_environment_variables_type_check'); - DB::statement("ALTER TABLE shared_environment_variables ADD CONSTRAINT shared_environment_variables_type_check CHECK (type IN ('team', 'project', 'environment'))"); + if (DB::getDriverName() !== 'sqlite') { + DB::statement('ALTER TABLE shared_environment_variables DROP CONSTRAINT IF EXISTS shared_environment_variables_type_check'); + DB::statement("ALTER TABLE shared_environment_variables ADD CONSTRAINT shared_environment_variables_type_check CHECK (type IN ('team', 'project', 'environment'))"); + } }); } }; diff --git a/tests/Feature/SharedVariableDevViewTest.php b/tests/Feature/SharedVariableDevViewTest.php index 779be26a9..34767cf06 100644 --- a/tests/Feature/SharedVariableDevViewTest.php +++ b/tests/Feature/SharedVariableDevViewTest.php @@ -1,7 +1,11 @@ environment = Environment::factory()->create([ 'project_id' => $this->project->id, ]); + InstanceSettings::unguarded(function () { + InstanceSettings::updateOrCreate([ + 'id' => 0, + ], [ + 'is_registration_enabled' => true, + 'is_api_enabled' => true, + 'smtp_enabled' => true, + 'smtp_host' => 'localhost', + 'smtp_port' => 1025, + 'smtp_from_address' => 'hi@example.com', + 'smtp_from_name' => 'Coolify', + ]); + }); $this->actingAs($this->user); session(['currentTeam' => $this->team]); }); +afterEach(function () { + request()->setRouteResolver(function () { + return null; + }); +}); + test('environment shared variable dev view saves without openssl_encrypt error', function () { - Livewire::test(\App\Livewire\SharedVariables\Environment\Show::class) + Livewire::test(Show::class, [ + 'project_uuid' => $this->project->uuid, + 'environment_uuid' => $this->environment->uuid, + ]) ->set('variables', "MY_VAR=my_value\nANOTHER_VAR=another_value") ->call('submit') ->assertHasNoErrors(); @@ -38,7 +64,9 @@ }); test('project shared variable dev view saves without openssl_encrypt error', function () { - Livewire::test(\App\Livewire\SharedVariables\Project\Show::class) + Livewire::test(App\Livewire\SharedVariables\Project\Show::class, [ + 'project_uuid' => $this->project->uuid, + ]) ->set('variables', 'PROJ_VAR=proj_value') ->call('submit') ->assertHasNoErrors(); @@ -49,7 +77,7 @@ }); test('team shared variable dev view saves without openssl_encrypt error', function () { - Livewire::test(\App\Livewire\SharedVariables\Team\Index::class) + Livewire::test(Index::class) ->set('variables', 'TEAM_VAR=team_value') ->call('submit') ->assertHasNoErrors(); @@ -69,7 +97,10 @@ 'team_id' => $this->team->id, ]); - Livewire::test(\App\Livewire\SharedVariables\Environment\Show::class) + Livewire::test(Show::class, [ + 'project_uuid' => $this->project->uuid, + 'environment_uuid' => $this->environment->uuid, + ]) ->set('variables', 'EXISTING_VAR=new_value') ->call('submit') ->assertHasNoErrors(); @@ -77,3 +108,63 @@ $var = $this->environment->environment_variables()->where('key', 'EXISTING_VAR')->first(); expect($var->value)->toBe('new_value'); }); + +test('server shared variable dev view saves without openssl_encrypt error', function () { + $this->server = Server::factory()->create(['team_id' => $this->team->id]); + + Livewire::test(App\Livewire\SharedVariables\Server\Show::class, [ + 'server_uuid' => $this->server->uuid, + ]) + ->set('variables', "SERVER_VAR=server_value\nSECOND_SERVER_VAR=second_value") + ->call('submit') + ->assertHasNoErrors(); + + $vars = $this->server->environment_variables()->pluck('value', 'key')->toArray(); + + expect($vars)->toHaveKey('SERVER_VAR') + ->and($vars['SERVER_VAR'])->toBe('server_value') + ->and($vars)->toHaveKey('SECOND_SERVER_VAR') + ->and($vars['SECOND_SERVER_VAR'])->toBe('second_value'); +}); + +test('server shared variable dev view preserves inline comments', function () { + $this->server = Server::factory()->create(['team_id' => $this->team->id]); + + Livewire::test(App\Livewire\SharedVariables\Server\Show::class, [ + 'server_uuid' => $this->server->uuid, + ]) + ->set('variables', 'COMMENTED_SERVER_VAR=value # note from dev view') + ->call('submit') + ->assertHasNoErrors(); + + $var = $this->server->environment_variables()->where('key', 'COMMENTED_SERVER_VAR')->first(); + + expect($var)->not->toBeNull() + ->and($var->value)->toBe('value') + ->and($var->comment)->toBe('note from dev view'); +}); + +test('server shared variable dev view updates existing variable', function () { + $this->server = Server::factory()->create(['team_id' => $this->team->id]); + + SharedEnvironmentVariable::create([ + 'key' => 'EXISTING_SERVER_VAR', + 'value' => 'old_value', + 'comment' => 'old comment', + 'type' => 'server', + 'server_id' => $this->server->id, + 'team_id' => $this->team->id, + ]); + + Livewire::test(App\Livewire\SharedVariables\Server\Show::class, [ + 'server_uuid' => $this->server->uuid, + ]) + ->set('variables', 'EXISTING_SERVER_VAR=new_value # updated comment') + ->call('submit') + ->assertHasNoErrors(); + + $var = $this->server->environment_variables()->where('key', 'EXISTING_SERVER_VAR')->first(); + + expect($var->value)->toBe('new_value') + ->and($var->comment)->toBe('updated comment'); +});