2025-10-09 10:53:57 +00:00
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
namespace App\Livewire\Server\CloudProviderToken;
|
|
|
|
|
|
|
|
|
|
use App\Models\CloudProviderToken;
|
|
|
|
|
use App\Models\Server;
|
|
|
|
|
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
2026-06-03 18:17:05 +00:00
|
|
|
use Illuminate\Support\Facades\Http;
|
2025-10-09 10:53:57 +00:00
|
|
|
use Livewire\Component;
|
|
|
|
|
|
|
|
|
|
class Show extends Component
|
|
|
|
|
{
|
|
|
|
|
use AuthorizesRequests;
|
|
|
|
|
|
|
|
|
|
public Server $server;
|
|
|
|
|
|
|
|
|
|
public $cloudProviderTokens = [];
|
|
|
|
|
|
|
|
|
|
public $parameters = [];
|
|
|
|
|
|
2026-06-03 18:17:05 +00:00
|
|
|
public string $provider = 'hetzner';
|
|
|
|
|
|
|
|
|
|
public string $providerName = 'Hetzner';
|
|
|
|
|
|
2025-10-09 10:53:57 +00:00
|
|
|
public function mount(string $server_uuid)
|
|
|
|
|
{
|
|
|
|
|
try {
|
|
|
|
|
$this->server = Server::ownedByCurrentTeam()->whereUuid($server_uuid)->firstOrFail();
|
|
|
|
|
$this->loadTokens();
|
|
|
|
|
} catch (\Throwable $e) {
|
|
|
|
|
return handleError($e, $this);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function getListeners()
|
|
|
|
|
{
|
|
|
|
|
return [
|
2026-07-08 08:42:58 +00:00
|
|
|
'tokenAdded.hetzner' => 'handleTokenAdded',
|
2025-10-09 10:53:57 +00:00
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function loadTokens()
|
|
|
|
|
{
|
2026-06-03 18:17:05 +00:00
|
|
|
$this->provider = $this->server->vultr_instance_id ? 'vultr' : 'hetzner';
|
|
|
|
|
$this->providerName = $this->provider === 'vultr' ? 'Vultr' : 'Hetzner';
|
|
|
|
|
|
2025-10-09 10:53:57 +00:00
|
|
|
$this->cloudProviderTokens = CloudProviderToken::ownedByCurrentTeam()
|
2026-06-03 18:17:05 +00:00
|
|
|
->where('provider', $this->provider)
|
2025-10-09 10:53:57 +00:00
|
|
|
->get();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function handleTokenAdded($tokenId)
|
|
|
|
|
{
|
|
|
|
|
$this->loadTokens();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function setCloudProviderToken($tokenId)
|
|
|
|
|
{
|
|
|
|
|
$ownedToken = CloudProviderToken::ownedByCurrentTeam()->find($tokenId);
|
|
|
|
|
if (is_null($ownedToken)) {
|
|
|
|
|
$this->dispatch('error', 'You are not allowed to use this token.');
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
try {
|
|
|
|
|
$this->authorize('update', $this->server);
|
|
|
|
|
|
|
|
|
|
// Validate the token works and can access this specific server
|
|
|
|
|
$validationResult = $this->validateTokenForServer($ownedToken);
|
|
|
|
|
if (! $validationResult['valid']) {
|
|
|
|
|
$this->dispatch('error', $validationResult['error']);
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$this->server->cloudProviderToken()->associate($ownedToken);
|
|
|
|
|
$this->server->save();
|
2026-06-04 09:03:06 +00:00
|
|
|
|
|
|
|
|
auditLog('ui.server.cloud_token_assigned', [
|
|
|
|
|
'team_id' => currentTeam()->id,
|
|
|
|
|
'server_uuid' => $this->server->uuid,
|
|
|
|
|
'server_name' => $this->server->name,
|
|
|
|
|
'cloud_token_uuid' => $ownedToken->uuid,
|
|
|
|
|
'cloud_token_name' => $ownedToken->name,
|
|
|
|
|
'provider' => $ownedToken->provider,
|
|
|
|
|
]);
|
|
|
|
|
|
2026-06-03 18:17:05 +00:00
|
|
|
$this->dispatch('success', "{$this->providerName} token updated successfully.");
|
2025-10-09 10:53:57 +00:00
|
|
|
$this->dispatch('refreshServerShow');
|
|
|
|
|
} catch (\Exception $e) {
|
|
|
|
|
$this->server->refresh();
|
|
|
|
|
$this->dispatch('error', $e->getMessage());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private function validateTokenForServer(CloudProviderToken $token): array
|
|
|
|
|
{
|
|
|
|
|
try {
|
2026-06-03 18:17:05 +00:00
|
|
|
$endpoint = $token->provider === 'vultr'
|
|
|
|
|
? 'https://api.vultr.com/v2/account'
|
|
|
|
|
: 'https://api.hetzner.cloud/v1/servers';
|
|
|
|
|
|
|
|
|
|
$response = Http::withHeaders([
|
2025-10-09 10:53:57 +00:00
|
|
|
'Authorization' => 'Bearer '.$token->token,
|
2026-06-03 18:17:05 +00:00
|
|
|
])->timeout(10)->get($endpoint);
|
2025-10-09 10:53:57 +00:00
|
|
|
|
|
|
|
|
if (! $response->successful()) {
|
|
|
|
|
return [
|
|
|
|
|
'valid' => false,
|
|
|
|
|
'error' => 'This token is invalid or has insufficient permissions.',
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($this->server->hetzner_server_id) {
|
2026-06-03 18:17:05 +00:00
|
|
|
$serverResponse = Http::withHeaders([
|
2025-10-09 10:53:57 +00:00
|
|
|
'Authorization' => 'Bearer '.$token->token,
|
|
|
|
|
])->timeout(10)->get("https://api.hetzner.cloud/v1/servers/{$this->server->hetzner_server_id}");
|
|
|
|
|
|
|
|
|
|
if (! $serverResponse->successful()) {
|
|
|
|
|
return [
|
|
|
|
|
'valid' => false,
|
|
|
|
|
'error' => 'This token cannot access this server. It may belong to a different Hetzner project.',
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-03 18:17:05 +00:00
|
|
|
if ($this->server->vultr_instance_id) {
|
|
|
|
|
$serverResponse = Http::withHeaders([
|
|
|
|
|
'Authorization' => 'Bearer '.$token->token,
|
|
|
|
|
])->timeout(10)->get("https://api.vultr.com/v2/instances/{$this->server->vultr_instance_id}");
|
|
|
|
|
|
|
|
|
|
if (! $serverResponse->successful()) {
|
|
|
|
|
return [
|
|
|
|
|
'valid' => false,
|
|
|
|
|
'error' => 'This token cannot access this instance. It may belong to a different Vultr account.',
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-09 10:53:57 +00:00
|
|
|
return ['valid' => true];
|
|
|
|
|
} catch (\Throwable $e) {
|
|
|
|
|
return [
|
|
|
|
|
'valid' => false,
|
|
|
|
|
'error' => 'Failed to validate token: '.$e->getMessage(),
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function validateToken()
|
|
|
|
|
{
|
|
|
|
|
try {
|
|
|
|
|
$token = $this->server->cloudProviderToken;
|
|
|
|
|
if (! $token) {
|
2026-06-03 18:17:05 +00:00
|
|
|
$this->dispatch('error', "No {$this->providerName} token is associated with this server.");
|
2025-10-09 10:53:57 +00:00
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-03 18:17:05 +00:00
|
|
|
$endpoint = $token->provider === 'vultr'
|
|
|
|
|
? 'https://api.vultr.com/v2/account'
|
|
|
|
|
: 'https://api.hetzner.cloud/v1/servers';
|
|
|
|
|
|
|
|
|
|
$response = Http::withHeaders([
|
2025-10-09 10:53:57 +00:00
|
|
|
'Authorization' => 'Bearer '.$token->token,
|
2026-06-03 18:17:05 +00:00
|
|
|
])->timeout(10)->get($endpoint);
|
2025-10-09 10:53:57 +00:00
|
|
|
|
|
|
|
|
if ($response->successful()) {
|
2026-06-03 18:17:05 +00:00
|
|
|
$this->dispatch('success', "{$this->providerName} token is valid and working.");
|
2025-10-09 10:53:57 +00:00
|
|
|
} else {
|
2026-06-03 18:17:05 +00:00
|
|
|
$this->dispatch('error', "{$this->providerName} token is invalid or has insufficient permissions.");
|
2025-10-09 10:53:57 +00:00
|
|
|
}
|
2026-06-04 09:03:06 +00:00
|
|
|
|
|
|
|
|
auditLog('ui.server.cloud_token_validated', [
|
|
|
|
|
'team_id' => currentTeam()->id,
|
|
|
|
|
'server_uuid' => $this->server->uuid,
|
|
|
|
|
'server_name' => $this->server->name,
|
|
|
|
|
'cloud_token_uuid' => $token->uuid,
|
|
|
|
|
'cloud_token_name' => $token->name,
|
|
|
|
|
'provider' => $token->provider,
|
|
|
|
|
'valid' => $response->successful(),
|
|
|
|
|
]);
|
2025-10-09 10:53:57 +00:00
|
|
|
} catch (\Throwable $e) {
|
|
|
|
|
return handleError($e, $this);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function render()
|
|
|
|
|
{
|
|
|
|
|
return view('livewire.server.cloud-provider-token.show');
|
|
|
|
|
}
|
|
|
|
|
}
|