feat: add token validation functionality for Hetzner and DigitalOcean providers
This commit is contained in:
parent
c95e297f39
commit
2a8fbb3f6e
3 changed files with 82 additions and 18 deletions
|
|
@ -30,6 +30,60 @@ public function loadTokens()
|
||||||
$this->tokens = CloudProviderToken::ownedByCurrentTeam()->get();
|
$this->tokens = CloudProviderToken::ownedByCurrentTeam()->get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function validateToken(int $tokenId)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$token = CloudProviderToken::ownedByCurrentTeam()->findOrFail($tokenId);
|
||||||
|
$this->authorize('view', $token);
|
||||||
|
|
||||||
|
if ($token->provider === 'hetzner') {
|
||||||
|
$isValid = $this->validateHetznerToken($token->token);
|
||||||
|
if ($isValid) {
|
||||||
|
$this->dispatch('success', 'Hetzner token is valid.');
|
||||||
|
} else {
|
||||||
|
$this->dispatch('error', 'Hetzner token validation failed. Please check the token.');
|
||||||
|
}
|
||||||
|
} elseif ($token->provider === 'digitalocean') {
|
||||||
|
$isValid = $this->validateDigitalOceanToken($token->token);
|
||||||
|
if ($isValid) {
|
||||||
|
$this->dispatch('success', 'DigitalOcean token is valid.');
|
||||||
|
} else {
|
||||||
|
$this->dispatch('error', 'DigitalOcean token validation failed. Please check the token.');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$this->dispatch('error', 'Unknown provider.');
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return handleError($e, $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function validateHetznerToken(string $token): bool
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$response = \Illuminate\Support\Facades\Http::withToken($token)
|
||||||
|
->timeout(10)
|
||||||
|
->get('https://api.hetzner.cloud/v1/servers?per_page=1');
|
||||||
|
|
||||||
|
return $response->successful();
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function validateDigitalOceanToken(string $token): bool
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$response = \Illuminate\Support\Facades\Http::withToken($token)
|
||||||
|
->timeout(10)
|
||||||
|
->get('https://api.digitalocean.com/v2/account');
|
||||||
|
|
||||||
|
return $response->successful();
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function deleteToken(int $tokenId)
|
public function deleteToken(int $tokenId)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
|
|
@ -14,13 +14,14 @@
|
||||||
<x-forms.input required id="name" label="Token Name"
|
<x-forms.input required id="name" label="Token Name"
|
||||||
placeholder="e.g., Production Hetzner. tip: add Hetzner project name to identify easier" />
|
placeholder="e.g., Production Hetzner. tip: add Hetzner project name to identify easier" />
|
||||||
|
|
||||||
<x-forms.input required type="password" id="token" label="API Token" placeholder="Enter your API token" />
|
<x-forms.input required type="password" id="token" label="API Token"
|
||||||
|
placeholder="Enter your API token" />
|
||||||
|
|
||||||
@if (auth()->user()->currentTeam()->cloudProviderTokens->where('provider', $provider)->isEmpty())
|
@if (auth()->user()->currentTeam()->cloudProviderTokens->where('provider', $provider)->isEmpty())
|
||||||
<div class="text-sm text-neutral-500 dark:text-neutral-400">
|
<div class="text-sm text-neutral-500 dark:text-neutral-400">
|
||||||
Create an API token in the <a
|
Create an API token in the <a
|
||||||
href='{{ $provider === 'hetzner' ? 'https://console.hetzner.com/projects' : '#' }}' target='_blank'
|
href='{{ $provider === 'hetzner' ? 'https://console.hetzner.com/projects' : '#' }}'
|
||||||
class='underline dark:text-white'>{{ ucfirst($provider) }} Console</a> → choose
|
target='_blank' class='underline dark:text-white'>{{ ucfirst($provider) }} Console</a> → choose
|
||||||
Project → Security → API Tokens.
|
Project → Security → API Tokens.
|
||||||
@if ($provider === 'hetzner')
|
@if ($provider === 'hetzner')
|
||||||
<br><br>
|
<br><br>
|
||||||
|
|
@ -28,12 +29,12 @@ class='underline dark:text-white'>{{ ucfirst($provider) }} Console</a> → choos
|
||||||
class='underline dark:text-white'>Sign up here</a>
|
class='underline dark:text-white'>Sign up here</a>
|
||||||
<br>
|
<br>
|
||||||
<span class="text-xs">(Coolify's affiliate link, only new accounts - supports us (€10)
|
<span class="text-xs">(Coolify's affiliate link, only new accounts - supports us (€10)
|
||||||
and gives you €20)</span>
|
and gives you €20)</span>
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
<x-forms.button type="submit" wire:target="addToken">Validate & Add Token</x-forms.button>
|
<x-forms.button type="submit">Validate & Add Token</x-forms.button>
|
||||||
@else
|
@else
|
||||||
{{-- Full page layout: horizontal, spacious --}}
|
{{-- Full page layout: horizontal, spacious --}}
|
||||||
<div class="flex gap-2 items-end flex-wrap">
|
<div class="flex gap-2 items-end flex-wrap">
|
||||||
|
|
@ -49,7 +50,8 @@ class='underline dark:text-white'>Sign up here</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-1 min-w-64">
|
<div class="flex-1 min-w-64">
|
||||||
<x-forms.input required type="password" id="token" label="API Token" placeholder="Enter your API token" />
|
<x-forms.input required type="password" id="token" label="API Token"
|
||||||
|
placeholder="Enter your API token" />
|
||||||
@if (auth()->user()->currentTeam()->cloudProviderTokens->where('provider', $provider)->isEmpty())
|
@if (auth()->user()->currentTeam()->cloudProviderTokens->where('provider', $provider)->isEmpty())
|
||||||
<div class="text-sm text-neutral-500 dark:text-neutral-400 mt-2">
|
<div class="text-sm text-neutral-500 dark:text-neutral-400 mt-2">
|
||||||
Create an API token in the <a href='https://console.hetzner.com/projects' target='_blank'
|
Create an API token in the <a href='https://console.hetzner.com/projects' target='_blank'
|
||||||
|
|
@ -60,11 +62,11 @@ class='underline dark:text-white'>Hetzner Console</a> → choose Project → Sec
|
||||||
class='underline dark:text-white'>Sign up here</a>
|
class='underline dark:text-white'>Sign up here</a>
|
||||||
<br>
|
<br>
|
||||||
<span class="text-xs">(Coolify's affiliate link, only new accounts - supports us (€10)
|
<span class="text-xs">(Coolify's affiliate link, only new accounts - supports us (€10)
|
||||||
and gives you €20)</span>
|
and gives you €20)</span>
|
||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
<x-forms.button type="submit" wire:target="addToken">Validate & Add Token</x-forms.button>
|
<x-forms.button type="submit">Validate & Add Token</x-forms.button>
|
||||||
@endif
|
@endif
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -20,16 +20,24 @@ class="flex flex-col gap-1 p-2 border dark:border-coolgray-200 hover:no-underlin
|
||||||
</div>
|
</div>
|
||||||
<div class="text-sm">Created: {{ $savedToken->created_at->diffForHumans() }}</div>
|
<div class="text-sm">Created: {{ $savedToken->created_at->diffForHumans() }}</div>
|
||||||
|
|
||||||
@can('delete', $savedToken)
|
<div class="flex gap-2 pt-2">
|
||||||
<x-modal-confirmation title="Confirm Token Deletion?" isErrorButton buttonTitle="Delete Token"
|
@can('view', $savedToken)
|
||||||
submitAction="deleteToken({{ $savedToken->id }})" :actions="[
|
<x-forms.button wire:click="validateToken({{ $savedToken->id }})" type="button">
|
||||||
'This cloud provider token will be permanently deleted.',
|
Validate Token
|
||||||
'Any servers using this token will need to be reconfigured.',
|
</x-forms.button>
|
||||||
]"
|
@endcan
|
||||||
confirmationText="{{ $savedToken->name }}"
|
|
||||||
confirmationLabel="Please confirm the deletion by entering the token name below"
|
@can('delete', $savedToken)
|
||||||
shortConfirmationLabel="Token Name" :confirmWithPassword="false" step2ButtonText="Delete Token" />
|
<x-modal-confirmation title="Confirm Token Deletion?" isErrorButton buttonTitle="Delete Token"
|
||||||
@endcan
|
submitAction="deleteToken({{ $savedToken->id }})" :actions="[
|
||||||
|
'This cloud provider token will be permanently deleted.',
|
||||||
|
'Any servers using this token will need to be reconfigured.',
|
||||||
|
]"
|
||||||
|
confirmationText="{{ $savedToken->name }}"
|
||||||
|
confirmationLabel="Please confirm the deletion by entering the token name below"
|
||||||
|
shortConfirmationLabel="Token Name" :confirmWithPassword="false" step2ButtonText="Delete Token" />
|
||||||
|
@endcan
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@empty
|
@empty
|
||||||
<div>
|
<div>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue