2023-08-29 12:36:17 +00:00
< ? php
2023-12-07 18:06:32 +00:00
namespace App\Livewire\Server ;
2023-08-29 12:36:17 +00:00
2024-11-01 22:48:46 +00:00
use App\Actions\Server\StartSentinel ;
use App\Actions\Server\StopSentinel ;
2024-12-16 13:06:16 +00:00
use App\Events\ServerReachabilityChanged ;
2025-12-11 21:14:32 +00:00
use App\Models\CloudProviderToken ;
2023-08-29 12:36:17 +00:00
use App\Models\Server ;
2026-03-03 10:51:38 +00:00
use App\Rules\ValidServerIp ;
2025-12-11 21:14:32 +00:00
use App\Services\HetznerService ;
2025-08-19 12:15:31 +00:00
use App\Support\ValidationPatterns ;
2025-08-22 11:02:11 +00:00
use Illuminate\Foundation\Auth\Access\AuthorizesRequests ;
2025-12-11 21:14:32 +00:00
use Illuminate\Support\Collection ;
2024-12-03 21:24:36 +00:00
use Livewire\Attributes\Computed ;
2025-02-18 15:44:16 +00:00
use Livewire\Attributes\Locked ;
2024-10-17 20:00:27 +00:00
use Livewire\Component ;
2023-08-29 12:36:17 +00:00
2024-10-17 20:00:27 +00:00
class Show extends Component
2023-08-29 12:36:17 +00:00
{
2025-08-22 11:02:11 +00:00
use AuthorizesRequests ;
2024-10-17 19:48:43 +00:00
public Server $server ;
2024-06-10 20:43:34 +00:00
2024-10-30 13:54:27 +00:00
public string $name ;
2024-11-08 10:45:56 +00:00
public ? string $description = null ;
2024-10-30 13:54:27 +00:00
public string $ip ;
public string $user ;
public string $port ;
public ? string $validationLogs = null ;
2024-11-08 10:45:56 +00:00
public ? string $wildcardDomain = null ;
2024-10-30 13:54:27 +00:00
public bool $isReachable ;
public bool $isUsable ;
public bool $isSwarmManager ;
public bool $isSwarmWorker ;
public bool $isBuildServer ;
2025-02-18 15:44:16 +00:00
#[Locked]
public bool $isBuildServerLocked = false ;
2024-10-30 13:54:27 +00:00
public bool $isMetricsEnabled ;
public string $sentinelToken ;
2024-11-08 10:45:56 +00:00
public ? string $sentinelUpdatedAt = null ;
2024-10-30 13:54:27 +00:00
public int $sentinelMetricsRefreshRateSeconds ;
public int $sentinelMetricsHistoryDays ;
public int $sentinelPushIntervalSeconds ;
2024-11-08 10:45:56 +00:00
public ? string $sentinelCustomUrl = null ;
2024-10-30 13:54:27 +00:00
public bool $isSentinelEnabled ;
public bool $isSentinelDebugEnabled ;
2025-09-14 17:21:55 +00:00
public ? string $sentinelCustomDockerImage = null ;
2024-10-30 13:54:27 +00:00
public string $serverTimezone ;
2025-10-09 14:54:13 +00:00
public ? string $hetznerServerStatus = null ;
public bool $hetznerServerManuallyStarted = false ;
public bool $isValidating = false ;
2025-12-11 21:14:32 +00:00
// Hetzner linking properties
public Collection $availableHetznerTokens ;
public ? int $selectedHetznerTokenId = null ;
2025-12-18 11:18:22 +00:00
public ? string $manualHetznerServerId = null ;
2025-12-11 21:14:32 +00:00
public ? array $matchedHetznerServer = null ;
public ? string $hetznerSearchError = null ;
public bool $hetznerNoMatchFound = false ;
2024-10-30 13:54:27 +00:00
public function getListeners ()
{
2025-08-25 18:27:54 +00:00
$teamId = $this -> server -> team_id ? ? auth () -> user () -> currentTeam () -> id ;
2024-10-30 13:54:27 +00:00
return [
2024-11-05 10:52:14 +00:00
'refreshServerShow' => 'refresh' ,
2025-10-09 14:54:13 +00:00
'refreshServer' => '$refresh' ,
2025-08-25 18:27:54 +00:00
" echo-private:team. { $teamId } ,SentinelRestarted " => 'handleSentinelRestarted' ,
2025-10-09 14:54:13 +00:00
" echo-private:team. { $teamId } ,ServerValidated " => 'handleServerValidated' ,
2024-10-30 13:54:27 +00:00
];
}
2025-08-19 12:15:31 +00:00
protected function rules () : array
{
return [
'name' => ValidationPatterns :: nameRules (),
'description' => ValidationPatterns :: descriptionRules (),
2026-03-03 10:51:38 +00:00
'ip' => [ 'required' , new ValidServerIp ],
'user' => [ 'required' , 'regex:/^[a-zA-Z0-9_-]+$/' ],
'port' => 'required|integer|between:1,65535' ,
2025-08-19 12:15:31 +00:00
'validationLogs' => 'nullable' ,
'wildcardDomain' => 'nullable|url' ,
'isReachable' => 'required' ,
'isUsable' => 'required' ,
'isSwarmManager' => 'required' ,
'isSwarmWorker' => 'required' ,
'isBuildServer' => 'required' ,
'isMetricsEnabled' => 'required' ,
'sentinelToken' => 'required' ,
'sentinelUpdatedAt' => 'nullable' ,
'sentinelMetricsRefreshRateSeconds' => 'required|integer|min:1' ,
'sentinelMetricsHistoryDays' => 'required|integer|min:1' ,
'sentinelPushIntervalSeconds' => 'required|integer|min:10' ,
'sentinelCustomUrl' => 'nullable|url' ,
'isSentinelEnabled' => 'required' ,
'isSentinelDebugEnabled' => 'required' ,
'serverTimezone' => 'required' ,
];
}
protected function messages () : array
{
return array_merge (
ValidationPatterns :: combinedMessages (),
[
'ip.required' => 'The IP Address field is required.' ,
'user.required' => 'The User field is required.' ,
'port.required' => 'The Port field is required.' ,
'wildcardDomain.url' => 'The Wildcard Domain must be a valid URL.' ,
'sentinelToken.required' => 'The Sentinel Token field is required.' ,
'sentinelMetricsRefreshRateSeconds.required' => 'The Metrics Refresh Rate field is required.' ,
'sentinelMetricsRefreshRateSeconds.integer' => 'The Metrics Refresh Rate must be an integer.' ,
'sentinelMetricsRefreshRateSeconds.min' => 'The Metrics Refresh Rate must be at least 1 second.' ,
'sentinelMetricsHistoryDays.required' => 'The Metrics History Days field is required.' ,
'sentinelMetricsHistoryDays.integer' => 'The Metrics History Days must be an integer.' ,
'sentinelMetricsHistoryDays.min' => 'The Metrics History Days must be at least 1 day.' ,
'sentinelPushIntervalSeconds.required' => 'The Push Interval field is required.' ,
'sentinelPushIntervalSeconds.integer' => 'The Push Interval must be an integer.' ,
'sentinelPushIntervalSeconds.min' => 'The Push Interval must be at least 10 seconds.' ,
'sentinelCustomUrl.url' => 'The Custom Sentinel URL must be a valid URL.' ,
'serverTimezone.required' => 'The Server Timezone field is required.' ,
]
);
}
2024-10-30 13:54:27 +00:00
public function mount ( string $server_uuid )
{
try {
$this -> server = Server :: ownedByCurrentTeam () -> whereUuid ( $server_uuid ) -> firstOrFail ();
$this -> syncData ();
2025-02-18 15:44:16 +00:00
if ( ! $this -> server -> isEmpty ()) {
$this -> isBuildServerLocked = true ;
}
2025-10-09 14:54:13 +00:00
// Load saved Hetzner status and validation state
$this -> hetznerServerStatus = $this -> server -> hetzner_server_status ;
$this -> isValidating = $this -> server -> is_validating ? ? false ;
2025-12-11 21:14:32 +00:00
// Load Hetzner tokens for linking
$this -> loadHetznerTokens ();
2025-01-07 14:31:43 +00:00
} catch ( \Throwable $e ) {
2024-10-30 13:54:27 +00:00
return handleError ( $e , $this );
}
}
2024-12-03 21:24:36 +00:00
#[Computed]
public function timezones () : array
{
return collect ( timezone_identifiers_list ())
-> sort ()
-> values ()
-> toArray ();
}
2024-10-30 13:54:27 +00:00
public function syncData ( bool $toModel = false )
{
if ( $toModel ) {
$this -> validate ();
2024-11-14 16:48:35 +00:00
2025-08-22 12:04:25 +00:00
$this -> authorize ( 'update' , $this -> server );
2026-02-12 07:10:59 +00:00
$foundServer = Server :: where ( 'ip' , $this -> ip )
2024-11-14 16:48:35 +00:00
-> where ( 'id' , '!=' , $this -> server -> id )
2026-02-12 07:10:59 +00:00
-> first ();
if ( $foundServer ) {
2024-11-14 16:48:35 +00:00
$this -> ip = $this -> server -> ip ;
2026-02-12 07:10:59 +00:00
if ( $foundServer -> team_id === currentTeam () -> id ) {
throw new \Exception ( 'A server with this IP/Domain already exists in your team.' );
}
throw new \Exception ( 'A server with this IP/Domain is already in use by another team.' );
2024-11-14 16:48:35 +00:00
}
2024-10-30 13:54:27 +00:00
$this -> server -> name = $this -> name ;
$this -> server -> description = $this -> description ;
$this -> server -> ip = $this -> ip ;
$this -> server -> user = $this -> user ;
$this -> server -> port = $this -> port ;
$this -> server -> validation_logs = $this -> validationLogs ;
$this -> server -> save ();
2024-10-17 20:00:27 +00:00
2024-10-30 13:54:27 +00:00
$this -> server -> settings -> is_swarm_manager = $this -> isSwarmManager ;
2024-11-08 12:58:40 +00:00
$this -> server -> settings -> wildcard_domain = $this -> wildcardDomain ;
2024-10-30 13:54:27 +00:00
$this -> server -> settings -> is_swarm_worker = $this -> isSwarmWorker ;
$this -> server -> settings -> is_build_server = $this -> isBuildServer ;
$this -> server -> settings -> is_metrics_enabled = $this -> isMetricsEnabled ;
$this -> server -> settings -> sentinel_token = $this -> sentinelToken ;
$this -> server -> settings -> sentinel_metrics_refresh_rate_seconds = $this -> sentinelMetricsRefreshRateSeconds ;
$this -> server -> settings -> sentinel_metrics_history_days = $this -> sentinelMetricsHistoryDays ;
$this -> server -> settings -> sentinel_push_interval_seconds = $this -> sentinelPushIntervalSeconds ;
$this -> server -> settings -> sentinel_custom_url = $this -> sentinelCustomUrl ;
$this -> server -> settings -> is_sentinel_enabled = $this -> isSentinelEnabled ;
$this -> server -> settings -> is_sentinel_debug_enabled = $this -> isSentinelDebugEnabled ;
2024-11-14 09:02:37 +00:00
if ( ! validate_timezone ( $this -> serverTimezone )) {
$this -> serverTimezone = config ( 'app.timezone' );
2025-01-07 14:31:43 +00:00
throw new \Exception ( 'Invalid timezone.' );
} else {
$this -> server -> settings -> server_timezone = $this -> serverTimezone ;
2024-11-14 09:02:37 +00:00
}
2024-10-30 13:54:27 +00:00
$this -> server -> settings -> save ();
} else {
$this -> name = $this -> server -> name ;
$this -> description = $this -> server -> description ;
$this -> ip = $this -> server -> ip ;
$this -> user = $this -> server -> user ;
$this -> port = $this -> server -> port ;
2024-11-08 12:58:40 +00:00
2024-10-30 13:54:27 +00:00
$this -> wildcardDomain = $this -> server -> settings -> wildcard_domain ;
$this -> isReachable = $this -> server -> settings -> is_reachable ;
$this -> isUsable = $this -> server -> settings -> is_usable ;
$this -> isSwarmManager = $this -> server -> settings -> is_swarm_manager ;
$this -> isSwarmWorker = $this -> server -> settings -> is_swarm_worker ;
$this -> isBuildServer = $this -> server -> settings -> is_build_server ;
$this -> isMetricsEnabled = $this -> server -> settings -> is_metrics_enabled ;
$this -> sentinelToken = $this -> server -> settings -> sentinel_token ;
$this -> sentinelMetricsRefreshRateSeconds = $this -> server -> settings -> sentinel_metrics_refresh_rate_seconds ;
$this -> sentinelMetricsHistoryDays = $this -> server -> settings -> sentinel_metrics_history_days ;
$this -> sentinelPushIntervalSeconds = $this -> server -> settings -> sentinel_push_interval_seconds ;
$this -> sentinelCustomUrl = $this -> server -> settings -> sentinel_custom_url ;
$this -> isSentinelEnabled = $this -> server -> settings -> is_sentinel_enabled ;
$this -> isSentinelDebugEnabled = $this -> server -> settings -> is_sentinel_debug_enabled ;
2025-06-18 10:03:34 +00:00
$this -> sentinelUpdatedAt = $this -> server -> sentinel_updated_at ;
2024-10-30 13:54:27 +00:00
$this -> serverTimezone = $this -> server -> settings -> server_timezone ;
2025-10-09 14:54:13 +00:00
$this -> isValidating = $this -> server -> is_validating ? ? false ;
2024-10-30 13:54:27 +00:00
}
}
2024-06-10 20:43:34 +00:00
2024-11-05 10:52:14 +00:00
public function refresh ()
{
$this -> syncData ();
}
2025-08-25 18:27:54 +00:00
public function handleSentinelRestarted ( $event )
{
// Only refresh if the event is for this server
if ( isset ( $event [ 'serverUuid' ]) && $event [ 'serverUuid' ] === $this -> server -> uuid ) {
$this -> server -> refresh ();
$this -> syncData ();
$this -> dispatch ( 'success' , 'Sentinel has been restarted successfully.' );
}
}
2024-10-30 13:54:27 +00:00
public function validateServer ( $install = true )
2023-08-29 12:36:17 +00:00
{
2023-08-29 13:51:30 +00:00
try {
2025-08-22 11:02:11 +00:00
$this -> authorize ( 'update' , $this -> server );
2024-10-30 13:54:27 +00:00
$this -> validationLogs = $this -> server -> validation_logs = null ;
$this -> server -> save ();
$this -> dispatch ( 'init' , $install );
2025-01-07 14:31:43 +00:00
} catch ( \Throwable $e ) {
2023-09-15 13:34:25 +00:00
return handleError ( $e , $this );
2023-08-29 13:51:30 +00:00
}
2023-08-29 12:36:17 +00:00
}
2024-06-10 20:43:34 +00:00
2024-10-30 13:54:27 +00:00
public function checkLocalhostConnection ()
2024-09-12 10:34:09 +00:00
{
2024-10-30 13:54:27 +00:00
$this -> syncData ( true );
[ 'uptime' => $uptime , 'error' => $error ] = $this -> server -> validateConnection ();
if ( $uptime ) {
$this -> dispatch ( 'success' , 'Server is reachable.' );
$this -> server -> settings -> is_reachable = $this -> isReachable = true ;
$this -> server -> settings -> is_usable = $this -> isUsable = true ;
$this -> server -> settings -> save ();
2024-12-16 13:06:16 +00:00
ServerReachabilityChanged :: dispatch ( $this -> server );
2024-10-30 13:54:27 +00:00
} else {
$this -> dispatch ( 'error' , 'Server is not reachable.' , 'Please validate your configuration and connection.<br><br>Check this <a target="_blank" class="underline" href="https://coolify.io/docs/knowledge-base/server/openssh">documentation</a> for further help. <br><br>Error: ' . $error );
return ;
}
}
2024-11-01 22:48:46 +00:00
public function restartSentinel ()
{
2025-08-22 12:04:25 +00:00
try {
$this -> authorize ( 'manageSentinel' , $this -> server );
2025-09-14 17:21:55 +00:00
$customImage = isDev () ? $this -> sentinelCustomDockerImage : null ;
$this -> server -> restartSentinel ( $customImage );
2025-09-23 06:49:11 +00:00
$this -> dispatch ( 'info' , 'Restarting Sentinel.' );
2025-08-22 12:04:25 +00:00
} catch ( \Throwable $e ) {
return handleError ( $e , $this );
}
2024-11-01 22:48:46 +00:00
}
public function updatedIsSentinelDebugEnabled ( $value )
{
2025-08-22 12:04:25 +00:00
try {
$this -> submit ();
$this -> restartSentinel ();
} catch ( \Throwable $e ) {
return handleError ( $e , $this );
}
2024-11-01 22:48:46 +00:00
}
public function updatedIsMetricsEnabled ( $value )
{
2025-08-22 12:04:25 +00:00
try {
$this -> submit ();
$this -> restartSentinel ();
} catch ( \Throwable $e ) {
return handleError ( $e , $this );
}
2024-11-01 22:48:46 +00:00
}
2025-09-16 08:31:57 +00:00
public function updatedIsBuildServer ( $value )
{
try {
$this -> authorize ( 'update' , $this -> server );
if ( $value === true && $this -> isSentinelEnabled ) {
$this -> isSentinelEnabled = false ;
$this -> isMetricsEnabled = false ;
$this -> isSentinelDebugEnabled = false ;
StopSentinel :: dispatch ( $this -> server );
$this -> dispatch ( 'info' , 'Sentinel has been disabled as build servers cannot run Sentinel.' );
}
$this -> submit ();
2025-09-16 08:33:32 +00:00
// Dispatch event to refresh the navbar
$this -> dispatch ( 'refreshServerShow' );
2025-09-16 08:31:57 +00:00
} catch ( \Throwable $e ) {
return handleError ( $e , $this );
}
}
2024-11-01 22:48:46 +00:00
public function updatedIsSentinelEnabled ( $value )
{
2025-08-22 12:04:25 +00:00
try {
$this -> authorize ( 'manageSentinel' , $this -> server );
if ( $value === true ) {
2025-09-16 08:31:57 +00:00
if ( $this -> isBuildServer ) {
$this -> isSentinelEnabled = false ;
$this -> dispatch ( 'error' , 'Sentinel cannot be enabled on build servers.' );
return ;
}
2025-09-14 17:21:55 +00:00
$customImage = isDev () ? $this -> sentinelCustomDockerImage : null ;
StartSentinel :: run ( $this -> server , true , null , $customImage );
2025-08-22 12:04:25 +00:00
} else {
$this -> isMetricsEnabled = false ;
$this -> isSentinelDebugEnabled = false ;
StopSentinel :: dispatch ( $this -> server );
}
$this -> submit ();
} catch ( \Throwable $e ) {
return handleError ( $e , $this );
2024-11-01 22:48:46 +00:00
}
}
2024-10-30 13:54:27 +00:00
public function regenerateSentinelToken ()
{
try {
2025-08-22 12:04:25 +00:00
$this -> authorize ( 'manageSentinel' , $this -> server );
2024-10-30 13:54:27 +00:00
$this -> server -> settings -> generateSentinelToken ();
2025-08-25 18:27:54 +00:00
$this -> dispatch ( 'success' , 'Token regenerated. Restarting Sentinel.' );
2025-01-07 14:31:43 +00:00
} catch ( \Throwable $e ) {
2024-10-30 13:54:27 +00:00
return handleError ( $e , $this );
}
}
public function instantSave ()
{
2025-08-22 12:04:25 +00:00
try {
2025-09-23 06:49:11 +00:00
$this -> syncData ( true );
2025-08-22 12:04:25 +00:00
} catch ( \Throwable $e ) {
return handleError ( $e , $this );
}
2024-09-12 10:34:09 +00:00
}
2025-10-09 14:54:13 +00:00
public function checkHetznerServerStatus ( bool $manual = false )
{
try {
if ( ! $this -> server -> hetzner_server_id || ! $this -> server -> cloudProviderToken ) {
$this -> dispatch ( 'error' , 'This server is not associated with a Hetzner Cloud server or token.' );
return ;
}
$hetznerService = new \App\Services\HetznerService ( $this -> server -> cloudProviderToken -> token );
$serverData = $hetznerService -> getServer ( $this -> server -> hetzner_server_id );
$this -> hetznerServerStatus = $serverData [ 'status' ] ? ? null ;
2025-10-10 07:35:54 +00:00
// Save status to database without triggering model events
if ( $this -> server -> hetzner_server_status !== $this -> hetznerServerStatus ) {
$this -> server -> hetzner_server_status = $this -> hetznerServerStatus ;
$this -> server -> update ([ 'hetzner_server_status' => $this -> hetznerServerStatus ]);
2025-10-10 08:13:14 +00:00
}
if ( $manual ) {
$this -> dispatch ( 'success' , 'Server status refreshed: ' . ucfirst ( $this -> hetznerServerStatus ? ? 'unknown' ));
2025-10-09 14:54:13 +00:00
}
// If Hetzner server is off but Coolify thinks it's still reachable, update Coolify's state
if ( $this -> hetznerServerStatus === 'off' && $this -> server -> settings -> is_reachable ) {
[ 'uptime' => $uptime , 'error' => $error ] = $this -> server -> validateConnection ();
if ( $uptime ) {
$this -> dispatch ( 'success' , 'Server is reachable.' );
$this -> server -> settings -> is_reachable = $this -> isReachable = true ;
$this -> server -> settings -> is_usable = $this -> isUsable = true ;
$this -> server -> settings -> save ();
ServerReachabilityChanged :: dispatch ( $this -> server );
} else {
$this -> dispatch ( 'error' , 'Server is not reachable.' , 'Please validate your configuration and connection.<br><br>Check this <a target="_blank" class="underline" href="https://coolify.io/docs/knowledge-base/server/openssh">documentation</a> for further help. <br><br>Error: ' . $error );
return ;
}
}
} catch ( \Throwable $e ) {
return handleError ( $e , $this );
}
}
public function handleServerValidated ( $event = null )
{
// Check if event is for this server
if ( $event && isset ( $event [ 'serverUuid' ]) && $event [ 'serverUuid' ] !== $this -> server -> uuid ) {
return ;
}
// Refresh server data
$this -> server -> refresh ();
$this -> syncData ();
// Update validation state
$this -> isValidating = $this -> server -> is_validating ? ? false ;
2025-12-18 11:18:22 +00:00
// Reload Hetzner tokens in case the linking section should now be shown
$this -> loadHetznerTokens ();
2025-10-09 14:54:13 +00:00
$this -> dispatch ( 'refreshServerShow' );
$this -> dispatch ( 'refreshServer' );
}
public function startHetznerServer ()
{
try {
if ( ! $this -> server -> hetzner_server_id || ! $this -> server -> cloudProviderToken ) {
$this -> dispatch ( 'error' , 'This server is not associated with a Hetzner Cloud server or token.' );
return ;
}
$hetznerService = new \App\Services\HetznerService ( $this -> server -> cloudProviderToken -> token );
$hetznerService -> powerOnServer ( $this -> server -> hetzner_server_id );
$this -> hetznerServerStatus = 'starting' ;
$this -> server -> update ([ 'hetzner_server_status' => 'starting' ]);
$this -> hetznerServerManuallyStarted = true ; // Set flag to trigger auto-validation when running
$this -> dispatch ( 'success' , 'Hetzner server is starting...' );
} catch ( \Throwable $e ) {
return handleError ( $e , $this );
}
}
2026-03-11 15:21:05 +00:00
public function refreshServerMetadata () : void
{
try {
$this -> authorize ( 'update' , $this -> server );
$result = $this -> server -> gatherServerMetadata ();
if ( $result ) {
$this -> server -> refresh ();
$this -> dispatch ( 'success' , 'Server details refreshed.' );
} else {
$this -> dispatch ( 'error' , 'Could not fetch server details. Is the server reachable?' );
}
} catch ( \Throwable $e ) {
handleError ( $e , $this );
}
}
2023-10-09 09:00:18 +00:00
public function submit ()
{
2024-10-30 13:54:27 +00:00
try {
$this -> syncData ( true );
2025-09-23 06:49:11 +00:00
$this -> dispatch ( 'success' , 'Server settings updated.' );
2025-01-07 14:31:43 +00:00
} catch ( \Throwable $e ) {
2024-10-30 13:54:27 +00:00
return handleError ( $e , $this );
}
2023-10-09 09:00:18 +00:00
}
2024-06-10 20:43:34 +00:00
2025-12-11 21:14:32 +00:00
public function loadHetznerTokens () : void
{
$this -> availableHetznerTokens = CloudProviderToken :: ownedByCurrentTeam ()
-> where ( 'provider' , 'hetzner' )
-> get ();
}
public function searchHetznerServer () : void
{
$this -> hetznerSearchError = null ;
$this -> hetznerNoMatchFound = false ;
$this -> matchedHetznerServer = null ;
if ( ! $this -> selectedHetznerTokenId ) {
$this -> hetznerSearchError = 'Please select a Hetzner token.' ;
return ;
}
try {
$this -> authorize ( 'update' , $this -> server );
$token = $this -> availableHetznerTokens -> firstWhere ( 'id' , $this -> selectedHetznerTokenId );
if ( ! $token ) {
$this -> hetznerSearchError = 'Invalid token selected.' ;
return ;
}
$hetznerService = new HetznerService ( $token -> token );
$matched = $hetznerService -> findServerByIp ( $this -> server -> ip );
if ( $matched ) {
$this -> matchedHetznerServer = $matched ;
} else {
$this -> hetznerNoMatchFound = true ;
}
} catch ( \Throwable $e ) {
$this -> hetznerSearchError = 'Failed to search Hetzner servers: ' . $e -> getMessage ();
}
}
2025-12-18 11:18:22 +00:00
public function searchHetznerServerById () : void
{
$this -> hetznerSearchError = null ;
$this -> hetznerNoMatchFound = false ;
$this -> matchedHetznerServer = null ;
if ( ! $this -> selectedHetznerTokenId ) {
$this -> hetznerSearchError = 'Please select a Hetzner token first.' ;
return ;
}
if ( ! $this -> manualHetznerServerId ) {
$this -> hetznerSearchError = 'Please enter a Hetzner Server ID.' ;
return ;
}
try {
$this -> authorize ( 'update' , $this -> server );
$token = $this -> availableHetznerTokens -> firstWhere ( 'id' , $this -> selectedHetznerTokenId );
if ( ! $token ) {
$this -> hetznerSearchError = 'Invalid token selected.' ;
return ;
}
$hetznerService = new HetznerService ( $token -> token );
$serverData = $hetznerService -> getServer (( int ) $this -> manualHetznerServerId );
if ( ! empty ( $serverData )) {
$this -> matchedHetznerServer = $serverData ;
} else {
$this -> hetznerNoMatchFound = true ;
}
} catch ( \Throwable $e ) {
$this -> hetznerSearchError = 'Failed to fetch Hetzner server: ' . $e -> getMessage ();
}
}
2025-12-11 21:14:32 +00:00
public function linkToHetzner ()
{
if ( ! $this -> matchedHetznerServer ) {
$this -> dispatch ( 'error' , 'No Hetzner server selected.' );
return ;
}
try {
$this -> authorize ( 'update' , $this -> server );
$token = $this -> availableHetznerTokens -> firstWhere ( 'id' , $this -> selectedHetznerTokenId );
if ( ! $token ) {
$this -> dispatch ( 'error' , 'Invalid token selected.' );
return ;
}
// Verify the server exists and is accessible with the token
$hetznerService = new HetznerService ( $token -> token );
$serverData = $hetznerService -> getServer ( $this -> matchedHetznerServer [ 'id' ]);
if ( empty ( $serverData )) {
$this -> dispatch ( 'error' , 'Could not find Hetzner server with ID: ' . $this -> matchedHetznerServer [ 'id' ]);
return ;
}
// Update the server with Hetzner details
$this -> server -> update ([
'cloud_provider_token_id' => $this -> selectedHetznerTokenId ,
'hetzner_server_id' => $this -> matchedHetznerServer [ 'id' ],
'hetzner_server_status' => $serverData [ 'status' ] ? ? null ,
]);
$this -> hetznerServerStatus = $serverData [ 'status' ] ? ? null ;
// Clear the linking state
$this -> matchedHetznerServer = null ;
$this -> selectedHetznerTokenId = null ;
2025-12-18 11:18:22 +00:00
$this -> manualHetznerServerId = null ;
2025-12-11 21:14:32 +00:00
$this -> hetznerNoMatchFound = false ;
$this -> hetznerSearchError = null ;
$this -> dispatch ( 'success' , 'Server successfully linked to Hetzner Cloud!' );
$this -> dispatch ( 'refreshServerShow' );
} catch ( \Throwable $e ) {
return handleError ( $e , $this );
}
}
2023-08-29 12:36:17 +00:00
public function render ()
{
return view ( 'livewire.server.show' );
}
}