mirror of
https://github.com/tiennm99/coolify.git
synced 2026-10-19 08:15:02 +00:00
feat: add cloud-init script support for Hetzner server creation
This commit adds the ability to use cloud-init scripts when creating Hetzner servers through the integration. Users can write custom scripts that will be executed during server initialization, and optionally save these scripts at the team level for future reuse. Key features: - Textarea field for entering cloud-init scripts (bash or cloud-config YAML) - Checkbox to save scripts for later use at team level - Dropdown to load previously saved scripts - Scripts are encrypted in the database - Full validation and authorization checks - Comprehensive unit and feature tests 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
1 parent
2cf4058b4b
commit
7061eacfa5
7 files changed
+389
No files matched your search
@@ -3,6 +3,7 @@
|
||||
namespace App\Livewire\Server\New;
|
||||
|
||||
use App\Enums\ProxyTypes;
|
||||
use App\Models\CloudInitScript;
|
||||
use App\Models\CloudProviderToken;
|
||||
use App\Models\PrivateKey;
|
||||
use App\Models\Server;
|
||||
@@ -62,16 +63,33 @@ class ByHetzner extends Component
|
||||
|
||||
public bool $enable_ipv6 = true;
|
||||
|
||||
public ?string $cloud_init_script = null;
|
||||
|
||||
public bool $save_cloud_init_script = false;
|
||||
|
||||
public ?string $cloud_init_script_name = null;
|
||||
|
||||
public ?string $cloud_init_script_description = null;
|
||||
|
||||
#[Locked]
|
||||
public Collection $saved_cloud_init_scripts;
|
||||
|
||||
public function mount()
|
||||
{
|
||||
$this->authorize('viewAny', CloudProviderToken::class);
|
||||
$this->loadTokens();
|
||||
$this->loadSavedCloudInitScripts();
|
||||
$this->server_name = generate_random_name();
|
||||
if ($this->private_keys->count() > 0) {
|
||||
$this->private_key_id = $this->private_keys->first()->id;
|
||||
}
|
||||
}
|
||||
|
||||
public function loadSavedCloudInitScripts()
|
||||
{
|
||||
$this->saved_cloud_init_scripts = CloudInitScript::ownedByCurrentTeam()->get();
|
||||
}
|
||||
|
||||
public function getListeners()
|
||||
{
|
||||
return [
|
||||
@@ -135,6 +153,10 @@ class ByHetzner extends Component
|
||||
'selectedHetznerSshKeyIds.*' => 'integer',
|
||||
'enable_ipv4' => 'required|boolean',
|
||||
'enable_ipv6' => 'required|boolean',
|
||||
'cloud_init_script' => 'nullable|string',
|
||||
'save_cloud_init_script' => 'boolean',
|
||||
'cloud_init_script_name' => 'required_if:save_cloud_init_script,true|nullable|string|max:255',
|
||||
'cloud_init_script_description' => 'nullable|string',
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -372,6 +394,14 @@ class ByHetzner extends Component
|
||||
ray('Image selected', $value);
|
||||
}
|
||||
|
||||
public function loadCloudInitScript(?int $scriptId)
|
||||
{
|
||||
if ($scriptId) {
|
||||
$script = CloudInitScript::ownedByCurrentTeam()->findOrFail($scriptId);
|
||||
$this->cloud_init_script = $script->script;
|
||||
}
|
||||
}
|
||||
|
||||
private function createHetznerServer(string $token): array
|
||||
{
|
||||
$hetznerService = new HetznerService($token);
|
||||
@@ -439,6 +469,11 @@ class ByHetzner extends Component
|
||||
],
|
||||
];
|
||||
|
||||
// Add cloud-init script if provided
|
||||
if (! empty($this->cloud_init_script)) {
|
||||
$params['user_data'] = $this->cloud_init_script;
|
||||
}
|
||||
|
||||
ray('Server creation parameters', $params);
|
||||
|
||||
// Create server on Hetzner
|
||||
@@ -460,6 +495,18 @@ class ByHetzner extends Component
|
||||
return $this->dispatch('error', 'You have reached the server limit for your subscription.');
|
||||
}
|
||||
|
||||
// Save cloud-init script if requested
|
||||
if ($this->save_cloud_init_script && ! empty($this->cloud_init_script)) {
|
||||
$this->authorize('create', CloudInitScript::class);
|
||||
|
||||
CloudInitScript::create([
|
||||
'team_id' => currentTeam()->id,
|
||||
'name' => $this->cloud_init_script_name,
|
||||
'script' => $this->cloud_init_script,
|
||||
'description' => $this->cloud_init_script_description,
|
||||
]);
|
||||
}
|
||||
|
||||
$hetznerToken = $this->getHetznerToken();
|
||||
|
||||
// Create server on Hetzner
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class CloudInitScript extends Model
|
||||
{
|
||||
protected $fillable = [
|
||||
'team_id',
|
||||
'name',
|
||||
'script',
|
||||
'description',
|
||||
];
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'script' => 'encrypted',
|
||||
];
|
||||
}
|
||||
|
||||
public function team()
|
||||
{
|
||||
return $this->belongsTo(Team::class);
|
||||
}
|
||||
|
||||
public static function ownedByCurrentTeam(array $select = ['*'])
|
||||
{
|
||||
$selectArray = collect($select)->concat(['id']);
|
||||
|
||||
return self::whereTeamId(currentTeam()->id)->select($selectArray->all());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace App\Policies;
|
||||
|
||||
use App\Models\CloudInitScript;
|
||||
use App\Models\User;
|
||||
|
||||
class CloudInitScriptPolicy
|
||||
{
|
||||
/**
|
||||
* Determine whether the user can view any models.
|
||||
*/
|
||||
public function viewAny(User $user): bool
|
||||
{
|
||||
return $user->isAdmin();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can view the model.
|
||||
*/
|
||||
public function view(User $user, CloudInitScript $cloudInitScript): bool
|
||||
{
|
||||
return $user->isAdmin();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can create models.
|
||||
*/
|
||||
public function create(User $user): bool
|
||||
{
|
||||
return $user->isAdmin();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can update the model.
|
||||
*/
|
||||
public function update(User $user, CloudInitScript $cloudInitScript): bool
|
||||
{
|
||||
return $user->isAdmin();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can delete the model.
|
||||
*/
|
||||
public function delete(User $user, CloudInitScript $cloudInitScript): bool
|
||||
{
|
||||
return $user->isAdmin();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can restore the model.
|
||||
*/
|
||||
public function restore(User $user, CloudInitScript $cloudInitScript): bool
|
||||
{
|
||||
return $user->isAdmin();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can permanently delete the model.
|
||||
*/
|
||||
public function forceDelete(User $user, CloudInitScript $cloudInitScript): bool
|
||||
{
|
||||
return $user->isAdmin();
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user