fix(api): secure proxy config, clone volumes, and rollback tags (#11003)

This commit is contained in:
Andras Bacsai authored and GitHub committed 2026-07-22 14:52:11 +02:00
1 parent 16972f6483
commit e560bdf9e0
43 files changed
+16229 -29

No files matched your search

@@ -57,6 +57,14 @@ function recommendedApplicationSettingsPayload(): array
'is_gzip_enabled' => false,
'is_stripprefix_enabled' => false,
'is_raw_compose_deployment_enabled' => true,
'is_log_drain_enabled' => true,
'is_gpu_enabled' => true,
'gpu_driver' => 'nvidia',
'gpu_count' => '1',
'gpu_device_ids' => '0',
'gpu_options' => null,
'is_consistent_container_name_enabled' => true,
'custom_internal_name' => 'my-app-internal',
];
}
@@ -181,3 +189,122 @@ test('raw compose deployment can only be enabled for Docker Compose applications
->assertUnprocessable()
->assertJsonValidationErrors('is_raw_compose_deployment_enabled');
});
function advancedApplicationSettingsPayload(): array
{
return [
'is_log_drain_enabled' => true,
'is_gpu_enabled' => true,
'gpu_driver' => 'nvidia',
'gpu_count' => '1',
'gpu_device_ids' => '0',
'gpu_options' => 'capabilities=compute,utility',
'is_consistent_container_name_enabled' => true,
'custom_internal_name' => 'my-app-container',
];
}
test('PATCH /api/v1/applications/{uuid} updates advanced application settings', function () {
$this->withHeaders(applicationSettingsApiHeaders($this->bearerToken))
->patchJson("/api/v1/applications/{$this->application->uuid}", advancedApplicationSettingsPayload())
->assertOk();
$settings = $this->application->fresh()->settings;
foreach (advancedApplicationSettingsPayload() as $field => $value) {
expect($settings->{$field})->toBe($value);
}
});
test('PATCH /api/v1/applications/{uuid} updates preview_url_template and max_restart_count', function () {
$this->withHeaders(applicationSettingsApiHeaders($this->bearerToken))
->patchJson("/api/v1/applications/{$this->application->uuid}", [
'preview_url_template' => '{{pr_id}}.preview.example.com',
'max_restart_count' => 5,
])
->assertOk();
$application = $this->application->fresh();
expect($application->preview_url_template)->toBe('{{pr_id}}.preview.example.com')
->and($application->max_restart_count)->toBe(5);
});
test('GET /api/v1/applications/{uuid} includes advanced settings', function () {
$this->application->settings->update(advancedApplicationSettingsPayload());
$this->application->update([
'preview_url_template' => '{{pr_id}}.preview.example.com',
'max_restart_count' => 3,
]);
$this->withHeaders(applicationSettingsApiHeaders($this->bearerToken))
->getJson("/api/v1/applications/{$this->application->uuid}")
->assertOk()
->assertJsonPath('settings.is_log_drain_enabled', true)
->assertJsonPath('settings.is_gpu_enabled', true)
->assertJsonPath('settings.gpu_driver', 'nvidia')
->assertJsonPath('settings.custom_internal_name', 'my-app-container')
->assertJsonPath('settings.is_consistent_container_name_enabled', true)
->assertJsonPath('preview_url_template', '{{pr_id}}.preview.example.com')
->assertJsonPath('max_restart_count', 3);
});
test('application creation accepts advanced application settings', function () {
Queue::fake();
$response = $this->withHeaders(applicationSettingsApiHeaders($this->bearerToken))
->postJson('/api/v1/applications/public', array_merge([
'project_uuid' => $this->project->uuid,
'environment_uuid' => $this->environment->uuid,
'server_uuid' => $this->server->uuid,
'git_repository' => 'https://gitlab.com/coolify/advanced-settings-test',
'git_branch' => 'main',
'build_pack' => 'nixpacks',
'ports_exposes' => '3000',
'autogenerate_domain' => false,
'preview_url_template' => '{{pr_id}}.create.example.com',
'max_restart_count' => 7,
], advancedApplicationSettingsPayload()))
->assertCreated();
$application = Application::where('uuid', $response->json('uuid'))->firstOrFail();
$settings = $application->settings;
foreach (advancedApplicationSettingsPayload() as $field => $value) {
expect($settings->{$field})->toBe($value);
}
expect($application->preview_url_template)->toBe('{{pr_id}}.create.example.com')
->and($application->max_restart_count)->toBe(7);
});
test('rejects invalid max_restart_count', function () {
$this->withHeaders(applicationSettingsApiHeaders($this->bearerToken))
->patchJson("/api/v1/applications/{$this->application->uuid}", [
'max_restart_count' => -1,
])
->assertUnprocessable()
->assertJsonValidationErrors('max_restart_count');
});
test('rejects invalid gpu boolean settings', function () {
$this->withHeaders(applicationSettingsApiHeaders($this->bearerToken))
->patchJson("/api/v1/applications/{$this->application->uuid}", [
'is_gpu_enabled' => 'not-a-boolean',
])
->assertUnprocessable()
->assertJsonValidationErrors('is_gpu_enabled');
});
test('rejects swarm fields on application update', function (string $field, mixed $value) {
$this->withHeaders(applicationSettingsApiHeaders($this->bearerToken))
->patchJson("/api/v1/applications/{$this->application->uuid}", [
$field => $value,
])
->assertUnprocessable()
->assertJsonValidationErrors($field);
})->with([
'swarm_replicas' => ['swarm_replicas', 3],
'swarm_placement_constraints' => ['swarm_placement_constraints', 'node.role==worker'],
'is_swarm_only_worker_nodes' => ['is_swarm_only_worker_nodes', true],
]);