fix(hetzner): require at least one public IP protocol

This commit is contained in:
Andras Bacsai 2026-07-07 14:41:17 +02:00
parent c9ffa0db96
commit a8000ac2ad
4 changed files with 59 additions and 1 deletions

View file

@ -824,6 +824,16 @@ public function createServer(Request $request)
$request->offsetSet('instant_validate', false);
}
if (! $request->boolean('enable_ipv4') && ! $request->boolean('enable_ipv6')) {
return response()->json([
'message' => 'Validation failed.',
'errors' => [
'enable_ipv4' => ['Enable at least one public IP protocol.'],
'enable_ipv6' => ['Enable at least one public IP protocol.'],
],
], 422);
}
// Validate cloud provider token
$tokenUuid = $this->getCloudProviderTokenUuid($request);
$token = CloudProviderToken::whereTeamId($teamId)

View file

@ -620,6 +620,13 @@ public function submit()
{
$this->validate();
if (! $this->enable_ipv4 && ! $this->enable_ipv6) {
$this->addError('enable_ipv4', 'Enable at least one public IP protocol.');
$this->addError('enable_ipv6', 'Enable at least one public IP protocol.');
return null;
}
try {
$this->authorize('create', Server::class);

View file

@ -669,6 +669,28 @@
$response->assertJsonFragment(['ip' => '2001:db8::1']);
});
test('rejects server creation when both public IP protocols are disabled', function () {
Http::fake();
$response = $this->withHeaders([
'Authorization' => 'Bearer '.$this->bearerToken,
'Content-Type' => 'application/json',
])->postJson('/api/v1/servers/hetzner', [
'cloud_provider_token_id' => $this->hetznerToken->uuid,
'location' => 'nbg1',
'server_type' => 'cx11',
'image' => 15512617,
'name' => 'test-server',
'private_key_uuid' => $this->privateKey->uuid,
'enable_ipv4' => false,
'enable_ipv6' => false,
]);
$response->assertStatus(422);
$response->assertJsonValidationErrors(['enable_ipv4', 'enable_ipv6']);
Http::assertNothingSent();
});
test('passes selected firewalls and networks to Hetzner server creation', function () {
Http::fake([
'https://api.hetzner.cloud/v1/ssh_keys' => Http::response([

View file

@ -240,7 +240,7 @@
'token' => 'test-hetzner-api-token',
]);
PrivateKey::factory()->create([
$this->privateKey = PrivateKey::factory()->create([
'team_id' => $this->team->id,
]);
});
@ -298,4 +298,23 @@
expect($component->get('hetznerFirewalls'))->toHaveCount(1)
->and($component->get('hetznerNetworks'))->toHaveCount(1);
});
test('rejects submitting without a public IP protocol before calling Hetzner', function () {
Http::fake();
Livewire::test(ByHetzner::class)
->set('current_step', 2)
->set('selected_token_id', $this->hetznerToken->id)
->set('server_name', 'test-server')
->set('selected_location', 'nbg1')
->set('selected_server_type', 'cx11')
->set('selected_image', 15512617)
->set('private_key_id', $this->privateKey->id)
->set('enable_ipv4', false)
->set('enable_ipv6', false)
->call('submit')
->assertHasErrors(['enable_ipv4', 'enable_ipv6']);
Http::assertNothingSent();
});
});