mirror of
https://github.com/tiennm99/coolify.git
synced 2026-10-19 08:15:02 +00:00
feat(security): add editable cloud credential pages
Add dedicated show/edit pages for cloud provider tokens and cloud-init scripts, including descriptions and UUID routes. Generate private keys directly from the index and surface cloud provider API loading errors in server creation flows.
This commit is contained in:
1 parent
7236cb8228
commit
e200d881f5
36 files changed
+1196
-126
No files matched your search
@@ -5,13 +5,8 @@
|
||||
helper="Enter your cloud-init script. Supports cloud-config YAML format." required />
|
||||
|
||||
<div class="flex justify-end gap-2">
|
||||
@if ($modal_mode)
|
||||
<x-forms.button type="button" @click="$dispatch('closeModal')">
|
||||
Cancel
|
||||
</x-forms.button>
|
||||
@endif
|
||||
<x-forms.button type="submit" isHighlighted>
|
||||
{{ $scriptId ? 'Update Script' : 'Create Script' }}
|
||||
</x-forms.button>
|
||||
</div>
|
||||
</form>
|
||||
</form>
|
||||
@@ -0,0 +1,28 @@
|
||||
<div>
|
||||
<x-slot:title>
|
||||
Cloud-Init Script | Coolify
|
||||
</x-slot>
|
||||
<x-security.navbar />
|
||||
<form class="flex flex-col" wire:submit="save">
|
||||
<div class="flex items-start gap-2">
|
||||
<h2 class="pb-4">Cloud-Init Script</h2>
|
||||
<x-forms.button canGate="update" :canResource="$cloudInitScript" type="submit">
|
||||
Save
|
||||
</x-forms.button>
|
||||
@can('delete', $cloudInitScript)
|
||||
<x-modal-confirmation title="Confirm Script Deletion?" isErrorButton buttonTitle="Delete"
|
||||
submitAction="delete" :actions="[
|
||||
'This cloud-init script will be permanently deleted.',
|
||||
'This action cannot be undone.',
|
||||
]" confirmationText="{{ $cloudInitScript->name }}"
|
||||
confirmationLabel="Please confirm the deletion by entering the script name below"
|
||||
shortConfirmationLabel="Script Name" :confirmWithPassword="false" step2ButtonText="Delete Script" />
|
||||
@endcan
|
||||
</div>
|
||||
<div class="flex flex-col gap-2">
|
||||
<x-forms.input canGate="update" :canResource="$cloudInitScript" id="name" label="Script Name" required />
|
||||
<x-forms.textarea canGate="update" :canResource="$cloudInitScript" id="script" label="Script Content" rows="12"
|
||||
helper="Enter your cloud-init script. Supports cloud-config YAML format." required />
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@@ -1,48 +1,30 @@
|
||||
<div>
|
||||
<x-security.navbar />
|
||||
<div class="flex gap-2">
|
||||
<h2 class="pb-4">Cloud-Init Scripts</h2>
|
||||
<div class="flex items-center gap-2">
|
||||
<h2>Cloud-Init Scripts</h2>
|
||||
@can('create', App\Models\CloudInitScript::class)
|
||||
<x-modal-input buttonTitle="+ Add" title="New Cloud-Init Script">
|
||||
<livewire:security.cloud-init-script-form />
|
||||
</x-modal-input>
|
||||
@endcan
|
||||
</div>
|
||||
<div class="pb-4 text-sm">Manage reusable cloud-init scripts for server initialization. Currently working only with <span class="text-red-500 font-bold">Hetzner's</span> integration.</div>
|
||||
<div class="pb-4 text-sm">Manage reusable cloud-init scripts for server initialization with Hetzner, Vultr, and DigitalOcean integrations.</div>
|
||||
|
||||
<div class="grid gap-4 lg:grid-cols-2">
|
||||
@forelse ($scripts as $script)
|
||||
<div wire:key="script-{{ $script->id }}"
|
||||
class="flex flex-col gap-1 p-2 border dark:border-coolgray-200 hover:no-underline">
|
||||
<div class="flex justify-between items-center">
|
||||
<div class="flex-1">
|
||||
<div class="font-bold dark:text-white">{{ $script->name }}</div>
|
||||
<div class="text-xs text-neutral-500 dark:text-neutral-400">
|
||||
Created {{ $script->created_at->diffForHumans() }}
|
||||
@can('view', $script)
|
||||
<a wire:key="script-{{ $script->id }}" class="coolbox group"
|
||||
href="{{ route('security.cloud-init-scripts.show', ['cloud_init_script_uuid' => $script->uuid]) }}" {{ wireNavigate() }}>
|
||||
<div class="flex flex-col justify-center mx-6">
|
||||
<div class="box-title">
|
||||
{{ $script->name }}
|
||||
</div>
|
||||
<div class="box-description">
|
||||
Cloud-init script
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex gap-2 mt-2">
|
||||
@can('update', $script)
|
||||
<x-modal-input buttonTitle="Edit" title="Edit Cloud-Init Script" fullWidth>
|
||||
<livewire:security.cloud-init-script-form :scriptId="$script->id"
|
||||
wire:key="edit-{{ $script->id }}" />
|
||||
</x-modal-input>
|
||||
@endcan
|
||||
|
||||
@can('delete', $script)
|
||||
<x-modal-confirmation title="Confirm Script Deletion?" isErrorButton buttonTitle="Delete"
|
||||
submitAction="deleteScript({{ $script->id }})" :actions="[
|
||||
'This cloud-init script will be permanently deleted.',
|
||||
'This action cannot be undone.',
|
||||
]" confirmationText="{{ $script->name }}"
|
||||
confirmationLabel="Please confirm the deletion by entering the script name below"
|
||||
shortConfirmationLabel="Script Name" :confirmWithPassword="false"
|
||||
step2ButtonText="Delete Script" />
|
||||
@endcan
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
@endcan
|
||||
@empty
|
||||
<div class="text-neutral-500">No cloud-init scripts found. Create one to get started.</div>
|
||||
@endforelse
|
||||
|
||||
@@ -15,6 +15,9 @@
|
||||
<x-forms.input required id="name" label="Token Name"
|
||||
placeholder="e.g., Production {{ $provider === 'digitalocean' ? 'DigitalOcean' : ucfirst($provider) }} token. tip: add project name to identify easier" />
|
||||
|
||||
<x-forms.textarea id="description" label="Description" rows="3"
|
||||
placeholder="Optional notes about where this token is used" />
|
||||
|
||||
<x-forms.input required type="password" id="token" label="API Token"
|
||||
placeholder="Enter your API token" />
|
||||
|
||||
@@ -33,7 +36,10 @@
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<x-forms.button type="submit">Validate & Add Token</x-forms.button>
|
||||
<x-forms.button type="submit" :showLoadingIndicator="false" wire:loading.attr="disabled" wire:target="addToken">
|
||||
Validate & Add Token
|
||||
<x-loading-on-button wire:loading wire:target="addToken" />
|
||||
</x-forms.button>
|
||||
@else
|
||||
{{-- Full page layout: horizontal, spacious --}}
|
||||
<div class="flex gap-2 items-end flex-wrap">
|
||||
@@ -47,6 +53,10 @@
|
||||
<div class="flex-1 min-w-64">
|
||||
<x-forms.input required id="name" label="Token Name" placeholder="e.g., Production cloud token" />
|
||||
</div>
|
||||
<div class="flex-1 min-w-64">
|
||||
<x-forms.textarea id="description" label="Description" rows="3"
|
||||
placeholder="Optional notes about where this token is used" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-1 min-w-64">
|
||||
<x-forms.input required type="password" id="token" label="API Token"
|
||||
@@ -66,7 +76,10 @@
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
<x-forms.button type="submit">Validate & Add Token</x-forms.button>
|
||||
<x-forms.button type="submit" :showLoadingIndicator="false" wire:loading.attr="disabled" wire:target="addToken">
|
||||
Validate & Add Token
|
||||
<x-loading-on-button wire:loading wire:target="addToken" />
|
||||
</x-forms.button>
|
||||
@endif
|
||||
</form>
|
||||
</div>
|
||||
@@ -0,0 +1,39 @@
|
||||
<div>
|
||||
<x-slot:title>
|
||||
Cloud Token | Coolify
|
||||
</x-slot>
|
||||
<x-security.navbar />
|
||||
<form class="flex flex-col" wire:submit="save">
|
||||
<div class="flex items-start gap-2">
|
||||
<h2 class="pb-4">Cloud Token</h2>
|
||||
<x-forms.button canGate="update" :canResource="$cloudProviderToken" type="submit">
|
||||
Save
|
||||
</x-forms.button>
|
||||
<x-forms.button canGate="view" :canResource="$cloudProviderToken" type="button" wire:click="validateToken"
|
||||
:showLoadingIndicator="false" wire:loading.attr="disabled" wire:target="validateToken">
|
||||
Validate
|
||||
<x-loading-on-button wire:loading wire:target="validateToken" />
|
||||
</x-forms.button>
|
||||
@can('delete', $cloudProviderToken)
|
||||
<x-modal-confirmation title="Confirm Token Deletion?" isErrorButton buttonTitle="Delete"
|
||||
submitAction="delete" :actions="[
|
||||
'This cloud provider token will be permanently deleted.',
|
||||
'Any servers using this token will need to be reconfigured.',
|
||||
]"
|
||||
confirmationText="{{ $cloudProviderToken->name }}"
|
||||
confirmationLabel="Please confirm the deletion by entering the token name below"
|
||||
shortConfirmationLabel="Token Name" :confirmWithPassword="false" step2ButtonText="Delete Token" />
|
||||
@endcan
|
||||
</div>
|
||||
<div class="flex flex-col gap-2">
|
||||
<div class="flex gap-2">
|
||||
<x-forms.input canGate="update" :canResource="$cloudProviderToken" id="name" label="Name" required />
|
||||
<x-forms.input canGate="update" :canResource="$cloudProviderToken" id="description" label="Description" />
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<x-forms.input readonly label="Provider" :value="$this->providerName()" />
|
||||
<x-forms.input readonly label="Created" :value="$cloudProviderToken->created_at->diffForHumans()" />
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@@ -1,44 +1,87 @@
|
||||
<div>
|
||||
<h2>Cloud Provider Tokens</h2>
|
||||
<div class="pb-4">Manage API tokens for cloud providers (Hetzner, Vultr, etc.).</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<h2>Cloud Provider Tokens</h2>
|
||||
@can('create', App\Models\CloudProviderToken::class)
|
||||
<div x-data="{ dropdownOpen: false }" class="relative w-fit" @click.outside="dropdownOpen = false">
|
||||
<x-forms.button isHighlighted @click="dropdownOpen = !dropdownOpen" type="button">
|
||||
+ Add
|
||||
<svg class="w-4 h-4 ml-2" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
||||
stroke-width="1.5" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="M8.25 15L12 18.75 15.75 15m-7.5-6L12 5.25 15.75 9" />
|
||||
</svg>
|
||||
</x-forms.button>
|
||||
|
||||
<h3>New Token</h3>
|
||||
@can('create', App\Models\CloudProviderToken::class)
|
||||
<livewire:security.cloud-provider-token-form :modal_mode="false" />
|
||||
@endcan
|
||||
<div x-show="dropdownOpen" @click.away="dropdownOpen=false" x-transition:enter="ease-out duration-200"
|
||||
x-transition:enter-start="-translate-y-2" x-transition:enter-end="translate-y-0"
|
||||
class="absolute top-0 z-50 mt-10 min-w-max" x-cloak>
|
||||
<div
|
||||
class="p-1 mt-1 bg-white border rounded-sm shadow-sm dark:bg-coolgray-200 dark:border-coolgray-300 border-neutral-300">
|
||||
<div class="flex flex-col gap-1">
|
||||
<x-modal-input title="Add Hetzner Token">
|
||||
<x-slot:content>
|
||||
<div class="dropdown-item" @click="dropdownOpen = false">
|
||||
<svg class="size-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M12 4.5v15m7.5-7.5h-15" />
|
||||
</svg>
|
||||
Hetzner
|
||||
</div>
|
||||
</x-slot:content>
|
||||
<livewire:security.cloud-provider-token-form :modal_mode="true" provider="hetzner"
|
||||
wire:key="cloud-provider-token-hetzner" />
|
||||
</x-modal-input>
|
||||
|
||||
<h3 class="py-4">Saved Tokens</h3>
|
||||
<div class="grid gap-2 lg:grid-cols-1">
|
||||
@forelse ($tokens as $savedToken)
|
||||
<div wire:key="token-{{ $savedToken->id }}"
|
||||
class="flex flex-col gap-1 p-2 border dark:border-coolgray-200 hover:no-underline">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="px-2 py-1 text-xs font-bold rounded dark:bg-coolgray-300 dark:text-white">
|
||||
{{ strtoupper($savedToken->provider) }}
|
||||
</span>
|
||||
<span class="font-bold dark:text-white">{{ $savedToken->name }}</span>
|
||||
</div>
|
||||
<div class="text-sm">Created: {{ $savedToken->created_at->diffForHumans() }}</div>
|
||||
<x-modal-input title="Add DigitalOcean Token">
|
||||
<x-slot:content>
|
||||
<div class="dropdown-item" @click="dropdownOpen = false">
|
||||
<svg class="size-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M12 4.5v15m7.5-7.5h-15" />
|
||||
</svg>
|
||||
DigitalOcean
|
||||
</div>
|
||||
</x-slot:content>
|
||||
<livewire:security.cloud-provider-token-form :modal_mode="true" provider="digitalocean"
|
||||
wire:key="cloud-provider-token-digitalocean" />
|
||||
</x-modal-input>
|
||||
|
||||
<div class="flex gap-2 pt-2">
|
||||
@can('view', $savedToken)
|
||||
<x-forms.button wire:click="validateToken({{ $savedToken->id }})" type="button">
|
||||
Validate
|
||||
</x-forms.button>
|
||||
@endcan
|
||||
|
||||
@can('delete', $savedToken)
|
||||
<x-modal-confirmation title="Confirm Token Deletion?" isErrorButton buttonTitle="Delete"
|
||||
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
|
||||
<x-modal-input title="Add Vultr Token">
|
||||
<x-slot:content>
|
||||
<div class="dropdown-item" @click="dropdownOpen = false">
|
||||
<svg class="size-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M12 4.5v15m7.5-7.5h-15" />
|
||||
</svg>
|
||||
Vultr
|
||||
</div>
|
||||
</x-slot:content>
|
||||
<livewire:security.cloud-provider-token-form :modal_mode="true" provider="vultr"
|
||||
wire:key="cloud-provider-token-vultr" />
|
||||
</x-modal-input>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endcan
|
||||
</div>
|
||||
<div class="pb-4">Manage API tokens for cloud providers (Hetzner, Vultr, etc.).</div>
|
||||
<div class="grid gap-4 lg:grid-cols-2">
|
||||
@forelse ($tokens as $savedToken)
|
||||
<a wire:key="token-{{ $savedToken->id }}" class="coolbox group"
|
||||
href="{{ route('security.cloud-tokens.show', ['cloud_token_uuid' => $savedToken->uuid]) }}" {{ wireNavigate() }}>
|
||||
<div class="flex flex-col justify-center mx-6">
|
||||
<div class="box-title">
|
||||
{{ $savedToken->name }}
|
||||
</div>
|
||||
<div class="box-description">
|
||||
{{ strtoupper($savedToken->provider) }}
|
||||
@if ($savedToken->description)
|
||||
· {{ $savedToken->description }}
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
@empty
|
||||
<div>
|
||||
<div>No cloud provider tokens found.</div>
|
||||
|
||||
@@ -3,11 +3,6 @@
|
||||
<div>Private Keys are used to connect to your servers without passwords.</div>
|
||||
<div class="font-bold">You should not use passphrase protected keys.</div>
|
||||
</div>
|
||||
<div class="flex gap-2 mb-4 w-full">
|
||||
<x-forms.button wire:click="generateNewEDKey" isHighlighted class="w-full">Generate new ED25519 SSH
|
||||
Key</x-forms.button>
|
||||
<x-forms.button wire:click="generateNewRSAKey">Generate new RSA SSH Key</x-forms.button>
|
||||
</div>
|
||||
<form class="flex flex-col gap-2" wire:submit='createPrivateKey'>
|
||||
<div class="flex gap-2">
|
||||
<x-forms.input id="name" label="Name" required />
|
||||
|
||||
@@ -1,11 +1,55 @@
|
||||
<div>
|
||||
<x-security.navbar />
|
||||
<div class="flex gap-2">
|
||||
<h2 class="pb-4">Private Keys</h2>
|
||||
<div class="flex items-center gap-2 pb-4">
|
||||
<h2>Private Keys</h2>
|
||||
@can('create', App\Models\PrivateKey::class)
|
||||
<x-modal-input buttonTitle="+ Add" title="New Private Key">
|
||||
<livewire:security.private-key.create />
|
||||
</x-modal-input>
|
||||
<div x-data="{ dropdownOpen: false }" class="relative w-fit" @click.outside="dropdownOpen = false">
|
||||
<x-forms.button isHighlighted @click="dropdownOpen = !dropdownOpen" type="button">
|
||||
+ Add
|
||||
<svg class="w-4 h-4 ml-2" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
|
||||
stroke-width="1.5" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round"
|
||||
d="M8.25 15L12 18.75 15.75 15m-7.5-6L12 5.25 15.75 9" />
|
||||
</svg>
|
||||
</x-forms.button>
|
||||
|
||||
<div x-show="dropdownOpen" @click.away="dropdownOpen=false" x-transition:enter="ease-out duration-200"
|
||||
x-transition:enter-start="-translate-y-2" x-transition:enter-end="translate-y-0"
|
||||
class="absolute top-0 z-50 mt-10 min-w-max" x-cloak>
|
||||
<div
|
||||
class="p-1 mt-1 bg-white border rounded-sm shadow-sm dark:bg-coolgray-200 dark:border-coolgray-300 border-neutral-300">
|
||||
<div class="flex flex-col gap-1">
|
||||
<a class="dropdown-item" wire:click="generatePrivateKey('ed25519')"
|
||||
@click="dropdownOpen = false">
|
||||
<svg class="size-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M12 4.5v15m7.5-7.5h-15" />
|
||||
</svg>
|
||||
Generate ED25519
|
||||
</a>
|
||||
<a class="dropdown-item" wire:click="generatePrivateKey('rsa')" @click="dropdownOpen = false">
|
||||
<svg class="size-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M12 4.5v15m7.5-7.5h-15" />
|
||||
</svg>
|
||||
Generate RSA
|
||||
</a>
|
||||
<x-modal-input title="Add Private Key Manually">
|
||||
<x-slot:content>
|
||||
<div class="dropdown-item" @click="dropdownOpen = false">
|
||||
<svg class="size-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M12 4.5v15m7.5-7.5h-15" />
|
||||
</svg>
|
||||
Add manually
|
||||
</div>
|
||||
</x-slot:content>
|
||||
<livewire:security.private-key.create />
|
||||
</x-modal-input>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endcan
|
||||
@can('create', App\Models\PrivateKey::class)
|
||||
<x-modal-confirmation title="Confirm unused SSH Key Deletion?" buttonTitle="Delete unused SSH Keys" isErrorButton
|
||||
|
||||
@@ -5,8 +5,14 @@
|
||||
<x-security.navbar />
|
||||
<div x-data="{ showPrivateKey: false }">
|
||||
<form class="flex flex-col" wire:submit='changePrivateKey'>
|
||||
<div class="flex items-start gap-2">
|
||||
<h2 class="pb-4">Private Key</h2>
|
||||
<div class="flex items-center gap-2 pb-4">
|
||||
<h2>Private Key</h2>
|
||||
@if ($isGitRelated)
|
||||
<span
|
||||
class="w-fit rounded-sm border border-coollabs bg-coollabs-50 px-2 py-1 text-xs font-bold text-coollabs-200 dark:border-coollabs-100 dark:bg-coollabs/20 dark:text-white">
|
||||
Used by GitHub App
|
||||
</span>
|
||||
@endif
|
||||
<x-forms.button canGate="update" :canResource="$private_key" type="submit">
|
||||
Save
|
||||
</x-forms.button>
|
||||
@@ -35,6 +41,7 @@
|
||||
<div class="pl-1">Public Key</div>
|
||||
</div>
|
||||
<x-forms.input canGate="update" :canResource="$private_key" readonly id="public_key" />
|
||||
<span class="block pt-2 pb-4 font-bold dark:text-warning">ACTION REQUIRED: Copy the 'Public Key' to your server's ~/.ssh/authorized_keys file</span>
|
||||
<div class="flex items-end gap-2 py-2 ">
|
||||
<div class="pl-1">Private Key <span class='text-helper'>*</span></div>
|
||||
<div class="text-xs underline cursor-pointer dark:text-white" x-cloak x-show="!showPrivateKey"
|
||||
@@ -46,11 +53,6 @@
|
||||
Hide
|
||||
</div>
|
||||
</div>
|
||||
@if ($isGitRelated)
|
||||
<div class="w-48">
|
||||
<x-forms.checkbox id="isGitRelated" disabled label="Is used by a Git App?" />
|
||||
</div>
|
||||
@endif
|
||||
<div x-cloak x-show="!showPrivateKey">
|
||||
<x-forms.input canGate="update" :canResource="$private_key" allowToPeak="false" type="password" rows="10" id="privateKeyValue"
|
||||
required disabled />
|
||||
|
||||
@@ -15,8 +15,10 @@
|
||||
</x-modal-input>
|
||||
@endcan
|
||||
<x-forms.button canGate="update" :canResource="$server" isHighlighted
|
||||
wire:click.prevent='validateToken'>
|
||||
wire:click.prevent='validateToken' :showLoadingIndicator="false" wire:loading.attr="disabled"
|
||||
wire:target="validateToken">
|
||||
Validate token
|
||||
<x-loading-on-button wire:loading wire:target="validateToken" />
|
||||
</x-forms.button>
|
||||
</div>
|
||||
<div class="pb-4">Change your server's {{ $providerName }} token.</div>
|
||||
@@ -26,6 +28,9 @@
|
||||
class="box-without-bg justify-between dark:bg-coolgray-100 bg-white items-center flex flex-col gap-2">
|
||||
<div class="flex flex-col w-full">
|
||||
<div class="box-title">{{ $token->name }}</div>
|
||||
@if ($token->description)
|
||||
<div class="box-description">{{ $token->description }}</div>
|
||||
@endif
|
||||
<div class="box-description">
|
||||
Created {{ $token->created_at->diffForHumans() }}
|
||||
</div>
|
||||
|
||||
@@ -60,6 +60,22 @@
|
||||
<div class="flex items-center justify-center py-8">
|
||||
<x-loading text="Loading DigitalOcean data..." />
|
||||
</div>
|
||||
@elseif ($provider_data_error)
|
||||
<div class="flex flex-col gap-4 rounded-lg border border-error bg-error/10 p-4">
|
||||
<div>
|
||||
<h3>Unable to load DigitalOcean details</h3>
|
||||
<p class="text-sm text-neutral-700 dark:text-neutral-300">
|
||||
Coolify could not fetch DigitalOcean data with the selected token. The token may have been
|
||||
deleted, revoked, or no longer has access.
|
||||
</p>
|
||||
</div>
|
||||
<pre class="whitespace-pre-wrap break-words text-sm text-error">{{ $provider_data_error }}</pre>
|
||||
<div>
|
||||
<a class="button" href="{{ route('server.create.type', ['type' => 'digital-ocean']) }}" {{ wireNavigate() }}>
|
||||
Select another token
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
@else
|
||||
<form class="flex flex-col w-full gap-2" wire:submit='submit'>
|
||||
<div>
|
||||
|
||||
@@ -60,6 +60,22 @@
|
||||
<div class="flex items-center justify-center py-8">
|
||||
<x-loading text="Loading Hetzner data..." />
|
||||
</div>
|
||||
@elseif ($provider_data_error)
|
||||
<div class="flex flex-col gap-4 rounded-lg border border-error bg-error/10 p-4">
|
||||
<div>
|
||||
<h3>Unable to load Hetzner details</h3>
|
||||
<p class="text-sm text-neutral-700 dark:text-neutral-300">
|
||||
Coolify could not fetch Hetzner data with the selected token. The token may have been
|
||||
deleted, revoked, or no longer has access.
|
||||
</p>
|
||||
</div>
|
||||
<pre class="whitespace-pre-wrap break-words text-sm text-error">{{ $provider_data_error }}</pre>
|
||||
<div>
|
||||
<a class="button" href="{{ route('server.create.type', ['type' => 'hetzner']) }}" {{ wireNavigate() }}>
|
||||
Select another token
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
@else
|
||||
<form class="flex flex-col w-full gap-2" wire:submit='submit'>
|
||||
<div>
|
||||
|
||||
@@ -60,6 +60,22 @@
|
||||
<div class="flex items-center justify-center py-8">
|
||||
<x-loading text="Loading Vultr data..." />
|
||||
</div>
|
||||
@elseif ($provider_data_error)
|
||||
<div class="flex flex-col gap-4 rounded-lg border border-error bg-error/10 p-4">
|
||||
<div>
|
||||
<h3>Unable to load Vultr details</h3>
|
||||
<p class="text-sm text-neutral-700 dark:text-neutral-300">
|
||||
Coolify could not fetch Vultr data with the selected token. The token may have been
|
||||
deleted, revoked, or no longer has access.
|
||||
</p>
|
||||
</div>
|
||||
<pre class="whitespace-pre-wrap break-words text-sm text-error">{{ $provider_data_error }}</pre>
|
||||
<div>
|
||||
<a class="button" href="{{ route('server.create.type', ['type' => 'vultr']) }}" {{ wireNavigate() }}>
|
||||
Select another token
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
@else
|
||||
<form class="flex flex-col w-full gap-2" wire:submit='submit'>
|
||||
<div>
|
||||
|
||||
Reference in new issue
Block a user