mirror of
https://github.com/tiennm99/coolify.git
synced 2026-10-03 05:19:37 +00:00
improved hetzner features
This commit is contained in:
1 parent
c9e6418542
commit
704ddf2968
17 files changed
+514
-199
No files matched your search
@@ -0,0 +1,144 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\Server\CloudProviderToken;
|
||||
|
||||
use App\Models\CloudProviderToken;
|
||||
use App\Models\Server;
|
||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||
use Livewire\Component;
|
||||
|
||||
class Show extends Component
|
||||
{
|
||||
use AuthorizesRequests;
|
||||
|
||||
public Server $server;
|
||||
|
||||
public $cloudProviderTokens = [];
|
||||
|
||||
public $parameters = [];
|
||||
|
||||
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 [
|
||||
'tokenAdded' => 'handleTokenAdded',
|
||||
];
|
||||
}
|
||||
|
||||
public function loadTokens()
|
||||
{
|
||||
$this->cloudProviderTokens = CloudProviderToken::ownedByCurrentTeam()
|
||||
->where('provider', 'hetzner')
|
||||
->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();
|
||||
$this->dispatch('success', 'Hetzner token updated successfully.');
|
||||
$this->dispatch('refreshServerShow');
|
||||
} catch (\Exception $e) {
|
||||
$this->server->refresh();
|
||||
$this->dispatch('error', $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private function validateTokenForServer(CloudProviderToken $token): array
|
||||
{
|
||||
try {
|
||||
// First, validate the token itself
|
||||
$response = \Illuminate\Support\Facades\Http::withHeaders([
|
||||
'Authorization' => 'Bearer '.$token->token,
|
||||
])->timeout(10)->get('https://api.hetzner.cloud/v1/servers');
|
||||
|
||||
if (! $response->successful()) {
|
||||
return [
|
||||
'valid' => false,
|
||||
'error' => 'This token is invalid or has insufficient permissions.',
|
||||
];
|
||||
}
|
||||
|
||||
// Check if this token can access the specific Hetzner server
|
||||
if ($this->server->hetzner_server_id) {
|
||||
$serverResponse = \Illuminate\Support\Facades\Http::withHeaders([
|
||||
'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.',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
$this->dispatch('error', 'No Hetzner token is associated with this server.');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$response = \Illuminate\Support\Facades\Http::withHeaders([
|
||||
'Authorization' => 'Bearer '.$token->token,
|
||||
])->timeout(10)->get('https://api.hetzner.cloud/v1/servers');
|
||||
|
||||
if ($response->successful()) {
|
||||
$this->dispatch('success', 'Hetzner token is valid and working.');
|
||||
} else {
|
||||
$this->dispatch('error', 'Hetzner token is invalid or has insufficient permissions.');
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.server.cloud-provider-token.show');
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Livewire\Server;
|
||||
|
||||
use App\Models\CloudProviderToken;
|
||||
use App\Models\PrivateKey;
|
||||
use App\Models\Team;
|
||||
use Livewire\Component;
|
||||
@@ -12,6 +13,8 @@ class Create extends Component
|
||||
|
||||
public bool $limit_reached = false;
|
||||
|
||||
public bool $has_hetzner_tokens = false;
|
||||
|
||||
public function mount()
|
||||
{
|
||||
$this->private_keys = PrivateKey::ownedByCurrentTeam()->get();
|
||||
@@ -21,6 +24,11 @@ class Create extends Component
|
||||
return;
|
||||
}
|
||||
$this->limit_reached = Team::serverLimitReached();
|
||||
|
||||
// Check if user has Hetzner tokens
|
||||
$this->has_hetzner_tokens = CloudProviderToken::ownedByCurrentTeam()
|
||||
->where('provider', 'hetzner')
|
||||
->exists();
|
||||
}
|
||||
|
||||
public function render()
|
||||
|
||||
@@ -34,12 +34,6 @@ class ByHetzner extends Component
|
||||
// Step 1: Token selection
|
||||
public ?int $selected_token_id = null;
|
||||
|
||||
public string $hetzner_token = '';
|
||||
|
||||
public bool $save_token = false;
|
||||
|
||||
public ?string $token_name = null;
|
||||
|
||||
// Step 2: Server configuration
|
||||
public array $locations = [];
|
||||
|
||||
@@ -64,31 +58,50 @@ class ByHetzner extends Component
|
||||
public function mount()
|
||||
{
|
||||
$this->authorize('viewAny', CloudProviderToken::class);
|
||||
$this->available_tokens = CloudProviderToken::ownedByCurrentTeam()
|
||||
->where('provider', 'hetzner')
|
||||
->get();
|
||||
$this->loadTokens();
|
||||
$this->server_name = generate_random_name();
|
||||
if ($this->private_keys->count() > 0) {
|
||||
$this->private_key_id = $this->private_keys->first()->id;
|
||||
}
|
||||
}
|
||||
|
||||
public function getListeners()
|
||||
{
|
||||
return [
|
||||
'tokenAdded' => 'handleTokenAdded',
|
||||
'modalClosed' => 'resetSelection',
|
||||
];
|
||||
}
|
||||
|
||||
public function resetSelection()
|
||||
{
|
||||
$this->selected_token_id = null;
|
||||
$this->current_step = 1;
|
||||
}
|
||||
|
||||
public function loadTokens()
|
||||
{
|
||||
$this->available_tokens = CloudProviderToken::ownedByCurrentTeam()
|
||||
->where('provider', 'hetzner')
|
||||
->get();
|
||||
}
|
||||
|
||||
public function handleTokenAdded($tokenId)
|
||||
{
|
||||
// Refresh token list
|
||||
$this->loadTokens();
|
||||
|
||||
// Auto-select the new token
|
||||
$this->selected_token_id = $tokenId;
|
||||
|
||||
// Automatically proceed to next step
|
||||
$this->nextStep();
|
||||
}
|
||||
|
||||
protected function rules(): array
|
||||
{
|
||||
$rules = [
|
||||
'selected_token_id' => 'nullable|integer',
|
||||
'hetzner_token' => 'required_without:selected_token_id|string',
|
||||
'save_token' => 'boolean',
|
||||
'token_name' => [
|
||||
'nullable',
|
||||
'string',
|
||||
'max:255',
|
||||
function ($attribute, $value, $fail) {
|
||||
if ($this->save_token && ! empty($this->hetzner_token) && empty($value)) {
|
||||
$fail('Please provide a name for the token.');
|
||||
}
|
||||
},
|
||||
],
|
||||
'selected_token_id' => 'required|integer|exists:cloud_provider_tokens,id',
|
||||
];
|
||||
|
||||
if ($this->current_step === 2) {
|
||||
@@ -108,8 +121,8 @@ class ByHetzner extends Component
|
||||
protected function messages(): array
|
||||
{
|
||||
return [
|
||||
'hetzner_token.required_without' => 'Please provide a Hetzner API token or select a saved token.',
|
||||
'token_name.required_if' => 'Please provide a name for the token.',
|
||||
'selected_token_id.required' => 'Please select a Hetzner token.',
|
||||
'selected_token_id.exists' => 'Selected token not found.',
|
||||
];
|
||||
}
|
||||
|
||||
@@ -139,50 +152,21 @@ class ByHetzner extends Component
|
||||
return $token ? $token->token : '';
|
||||
}
|
||||
|
||||
return $this->hetzner_token;
|
||||
return '';
|
||||
}
|
||||
|
||||
public function nextStep()
|
||||
{
|
||||
// Validate step 1
|
||||
// Validate step 1 - just need a token selected
|
||||
$this->validate([
|
||||
'selected_token_id' => 'nullable|integer',
|
||||
'hetzner_token' => 'required_without:selected_token_id|string',
|
||||
'save_token' => 'boolean',
|
||||
'token_name' => [
|
||||
'nullable',
|
||||
'string',
|
||||
'max:255',
|
||||
function ($attribute, $value, $fail) {
|
||||
if ($this->save_token && ! empty($this->hetzner_token) && empty($value)) {
|
||||
$fail('Please provide a name for the token.');
|
||||
}
|
||||
},
|
||||
],
|
||||
'selected_token_id' => 'required|integer|exists:cloud_provider_tokens,id',
|
||||
]);
|
||||
|
||||
try {
|
||||
$hetznerToken = $this->getHetznerToken();
|
||||
|
||||
if (! $hetznerToken) {
|
||||
return $this->dispatch('error', 'Please provide a valid Hetzner API token.');
|
||||
}
|
||||
|
||||
// Validate token if it's a new one
|
||||
if (! $this->selected_token_id) {
|
||||
if (! $this->validateHetznerToken($hetznerToken)) {
|
||||
return $this->dispatch('error', 'Invalid Hetzner API token. Please check your token and try again.');
|
||||
}
|
||||
|
||||
// Save token if requested
|
||||
if ($this->save_token) {
|
||||
CloudProviderToken::create([
|
||||
'team_id' => currentTeam()->id,
|
||||
'provider' => 'hetzner',
|
||||
'token' => $this->hetzner_token,
|
||||
'name' => $this->token_name,
|
||||
]);
|
||||
}
|
||||
return $this->dispatch('error', 'Please select a valid Hetzner token.');
|
||||
}
|
||||
|
||||
// Load Hetzner data
|
||||
@@ -424,6 +408,7 @@ class ByHetzner extends Component
|
||||
'port' => 22,
|
||||
'team_id' => currentTeam()->id,
|
||||
'private_key_id' => $this->private_key_id,
|
||||
'cloud_provider_token_id' => $this->selected_token_id,
|
||||
'hetzner_server_id' => $hetznerServer['id'],
|
||||
]);
|
||||
|
||||
|
||||
Reference in new issue
Block a user