2023-03-24 14:47:58 +00:00
< ? php
namespace App\Models ;
2024-02-05 13:40:54 +00:00
use App\Actions\Server\InstallDocker ;
2024-10-22 12:10:36 +00:00
use App\Actions\Server\StartSentinel ;
2023-09-21 15:48:31 +00:00
use App\Enums\ProxyTypes ;
2024-10-25 09:41:25 +00:00
use App\Jobs\CheckAndStartSentinelJob ;
2024-10-25 13:13:23 +00:00
use App\Notifications\Server\Reachable ;
use App\Notifications\Server\Unreachable ;
2023-05-03 05:23:45 +00:00
use Illuminate\Database\Eloquent\Builder ;
2023-10-04 08:57:44 +00:00
use Illuminate\Database\Eloquent\Casts\Attribute ;
2024-10-17 12:56:36 +00:00
use Illuminate\Database\Eloquent\SoftDeletes ;
2024-10-15 11:39:19 +00:00
use Illuminate\Support\Carbon ;
2024-05-08 18:59:58 +00:00
use Illuminate\Support\Collection ;
2024-02-06 14:42:31 +00:00
use Illuminate\Support\Facades\DB ;
2024-06-20 11:17:06 +00:00
use Illuminate\Support\Facades\Process ;
2024-02-26 09:25:21 +00:00
use Illuminate\Support\Facades\Storage ;
2023-11-28 13:05:55 +00:00
use Illuminate\Support\Stringable ;
2024-07-09 08:45:10 +00:00
use OpenApi\Attributes as OA ;
2024-06-10 20:43:34 +00:00
use Spatie\SchemalessAttributes\Casts\SchemalessAttributes ;
use Spatie\SchemalessAttributes\SchemalessAttributesTrait ;
2024-03-11 16:17:34 +00:00
use Spatie\Url\Url ;
use Symfony\Component\Yaml\Yaml ;
2023-05-03 05:23:45 +00:00
2024-07-09 08:45:10 +00:00
#[OA\Schema(
2024-07-23 12:20:53 +00:00
description : 'Server model' ,
2024-07-09 08:45:10 +00:00
type : 'object' ,
properties : [
'id' => [ 'type' => 'integer' ],
'uuid' => [ 'type' => 'string' ],
'name' => [ 'type' => 'string' ],
2024-07-23 12:20:53 +00:00
'description' => [ 'type' => 'string' ],
'ip' => [ 'type' => 'string' ],
'user' => [ 'type' => 'string' ],
'port' => [ 'type' => 'integer' ],
'proxy' => [ 'type' => 'object' ],
'high_disk_usage_notification_sent' => [ 'type' => 'boolean' ],
'unreachable_notification_sent' => [ 'type' => 'boolean' ],
'unreachable_count' => [ 'type' => 'integer' ],
'validation_logs' => [ 'type' => 'string' ],
'log_drain_notification_sent' => [ 'type' => 'boolean' ],
'swarm_cluster' => [ 'type' => 'string' ],
2024-09-22 18:02:51 +00:00
'delete_unused_volumes' => [ 'type' => 'boolean' ],
'delete_unused_networks' => [ 'type' => 'boolean' ],
2024-07-09 08:45:10 +00:00
]
)]
2023-03-24 14:47:58 +00:00
class Server extends BaseModel
{
2024-10-22 09:39:38 +00:00
use SchemalessAttributesTrait , SoftDeletes ;
2024-06-10 20:43:34 +00:00
2023-11-22 14:18:49 +00:00
public static $batch_counter = 0 ;
2023-08-08 09:51:36 +00:00
2023-08-23 08:14:39 +00:00
protected static function booted ()
{
2023-10-09 09:00:18 +00:00
static :: saving ( function ( $server ) {
2023-10-09 18:12:03 +00:00
$payload = [];
if ( $server -> user ) {
2024-06-25 08:37:10 +00:00
$payload [ 'user' ] = str ( $server -> user ) -> trim ();
2023-10-09 18:12:03 +00:00
}
if ( $server -> ip ) {
2024-06-25 08:37:10 +00:00
$payload [ 'ip' ] = str ( $server -> ip ) -> trim ();
2023-10-09 18:12:03 +00:00
}
$server -> forceFill ( $payload );
2024-10-25 13:13:23 +00:00
2023-10-06 22:51:01 +00:00
});
2023-08-23 08:14:39 +00:00
static :: created ( function ( $server ) {
ServerSetting :: create ([
'server_id' => $server -> id ,
]);
2024-07-23 12:20:53 +00:00
if ( $server -> id === 0 ) {
if ( $server -> isSwarm ()) {
SwarmDocker :: create ([
'id' => 0 ,
'name' => 'coolify' ,
'network' => 'coolify-overlay' ,
'server_id' => $server -> id ,
]);
} else {
StandaloneDocker :: create ([
'id' => 0 ,
'name' => 'coolify' ,
'network' => 'coolify' ,
'server_id' => $server -> id ,
]);
}
} else {
if ( $server -> isSwarm ()) {
SwarmDocker :: create ([
'name' => 'coolify-overlay' ,
'network' => 'coolify-overlay' ,
'server_id' => $server -> id ,
]);
} else {
StandaloneDocker :: create ([
'name' => 'coolify' ,
'network' => 'coolify' ,
'server_id' => $server -> id ,
]);
}
}
2023-08-23 08:14:39 +00:00
});
2024-10-17 12:56:36 +00:00
static :: forceDeleting ( function ( $server ) {
2023-08-29 13:51:30 +00:00
$server -> destinations () -> each ( function ( $destination ) {
$destination -> delete ();
});
2023-08-23 08:14:39 +00:00
$server -> settings () -> delete ();
});
}
2024-10-25 13:13:23 +00:00
protected $casts = [
2023-08-08 09:51:36 +00:00
'proxy' => SchemalessAttributes :: class ,
2023-11-17 09:21:19 +00:00
'logdrain_axiom_api_key' => 'encrypted' ,
'logdrain_newrelic_license_key' => 'encrypted' ,
2024-09-22 18:02:51 +00:00
'delete_unused_volumes' => 'boolean' ,
'delete_unused_networks' => 'boolean' ,
2024-10-25 13:13:23 +00:00
'unreachable_notification_sent' => 'boolean' ,
'is_build_server' => 'boolean' ,
'force_disabled' => 'boolean' ,
2023-08-08 09:51:36 +00:00
];
2024-06-10 20:43:34 +00:00
2023-06-20 18:19:31 +00:00
protected $schemalessAttributes = [
'proxy' ,
];
2024-06-10 20:43:34 +00:00
2024-09-10 14:55:34 +00:00
protected $fillable = [
2024-09-10 15:29:53 +00:00
'name' ,
'ip' ,
2024-09-10 14:55:34 +00:00
'port' ,
'user' ,
2024-09-10 15:29:53 +00:00
'description' ,
'private_key_id' ,
'team_id' ,
2024-09-10 14:55:34 +00:00
];
2023-08-23 08:14:39 +00:00
protected $guarded = [];
2023-08-08 09:51:36 +00:00
2024-10-24 10:21:36 +00:00
public function type ()
{
return 'server' ;
}
2024-10-25 08:59:12 +00:00
2024-06-10 20:43:34 +00:00
public static function isReachable ()
2023-08-08 09:51:36 +00:00
{
return Server :: ownedByCurrentTeam () -> whereRelation ( 'settings' , 'is_reachable' , true );
}
2024-06-10 20:43:34 +00:00
public static function ownedByCurrentTeam ( array $select = [ '*' ])
2023-08-08 09:51:36 +00:00
{
2023-08-22 15:44:49 +00:00
$teamId = currentTeam () -> id ;
2023-08-08 09:51:36 +00:00
$selectArray = collect ( $select ) -> concat ([ 'id' ]);
2024-06-10 20:43:34 +00:00
2023-12-04 14:08:24 +00:00
return Server :: whereTeamId ( $teamId ) -> with ( 'settings' , 'swarmDockers' , 'standaloneDockers' ) -> select ( $selectArray -> all ()) -> orderBy ( 'name' );
2023-08-08 09:51:36 +00:00
}
2024-06-10 20:43:34 +00:00
public static function isUsable ()
2023-08-08 09:51:36 +00:00
{
2024-02-26 09:25:21 +00:00
return Server :: ownedByCurrentTeam () -> whereRelation ( 'settings' , 'is_reachable' , true ) -> whereRelation ( 'settings' , 'is_usable' , true ) -> whereRelation ( 'settings' , 'is_swarm_worker' , false ) -> whereRelation ( 'settings' , 'is_build_server' , false ) -> whereRelation ( 'settings' , 'force_disabled' , false );
2023-08-08 09:51:36 +00:00
}
2024-06-10 20:43:34 +00:00
public static function destinationsByServer ( string $server_id )
2023-08-08 09:51:36 +00:00
{
$server = Server :: ownedByCurrentTeam () -> get () -> where ( 'id' , $server_id ) -> firstOrFail ();
$standaloneDocker = collect ( $server -> standaloneDockers -> all ());
$swarmDocker = collect ( $server -> swarmDockers -> all ());
2024-06-10 20:43:34 +00:00
2023-08-08 09:51:36 +00:00
return $standaloneDocker -> concat ( $swarmDocker );
}
2024-06-10 20:43:34 +00:00
2023-08-08 09:51:36 +00:00
public function settings ()
{
2024-09-10 15:29:53 +00:00
return $this -> hasOne ( ServerSetting :: class );
2023-08-08 09:51:36 +00:00
}
2024-06-10 20:43:34 +00:00
2024-09-19 10:32:56 +00:00
public function proxySet ()
{
return $this -> proxyType () && $this -> proxyType () !== 'NONE' && $this -> isFunctional () && ! $this -> isSwarmWorker () && ! $this -> settings -> is_build_server ;
}
2024-03-12 09:42:56 +00:00
public function setupDefault404Redirect ()
{
2024-10-17 20:08:23 +00:00
$dynamic_conf_path = $this -> proxyPath () . '/dynamic' ;
2024-03-12 09:42:56 +00:00
$proxy_type = $this -> proxyType ();
$redirect_url = $this -> proxy -> redirect_url ;
2024-08-07 15:52:51 +00:00
if ( $proxy_type === ProxyTypes :: TRAEFIK -> value ) {
2024-03-12 09:42:56 +00:00
$default_redirect_file = " $dynamic_conf_path /default_redirect_404.yaml " ;
2024-09-23 20:57:24 +00:00
} elseif ( $proxy_type === ProxyTypes :: CADDY -> value ) {
2024-03-12 09:42:56 +00:00
$default_redirect_file = " $dynamic_conf_path /default_redirect_404.caddy " ;
}
if ( empty ( $redirect_url )) {
2024-09-23 20:57:24 +00:00
if ( $proxy_type === ProxyTypes :: CADDY -> value ) {
2024-06-10 20:43:34 +00:00
$conf = ' : 80 , : 443 {
2024-03-13 10:25:34 +00:00
respond 404
2024-06-10 20:43:34 +00:00
} ' ;
2024-03-13 10:25:34 +00:00
$conf =
2024-10-17 20:08:23 +00:00
" # This file is automatically generated by Coolify. \n " .
" # Do not edit it manually (only if you know what are you doing). \n \n " .
2024-03-13 10:25:34 +00:00
$conf ;
$base64 = base64_encode ( $conf );
instant_remote_process ([
" mkdir -p $dynamic_conf_path " ,
2024-04-17 08:49:34 +00:00
" echo ' $base64 ' | base64 -d | tee $default_redirect_file > /dev/null " ,
2024-03-13 10:25:34 +00:00
], $this );
$this -> reloadCaddy ();
2024-06-10 20:43:34 +00:00
2024-03-13 10:25:34 +00:00
return ;
}
2024-03-12 09:42:56 +00:00
instant_remote_process ([
" mkdir -p $dynamic_conf_path " ,
" rm -f $default_redirect_file " ,
], $this );
2024-06-10 20:43:34 +00:00
2024-03-12 09:42:56 +00:00
return ;
}
2024-08-07 15:52:51 +00:00
if ( $proxy_type === ProxyTypes :: TRAEFIK -> value ) {
2024-03-12 09:42:56 +00:00
$dynamic_conf = [
2024-06-10 20:43:34 +00:00
'http' => [
'routers' => [
'catchall' => [
2024-03-12 09:42:56 +00:00
'entryPoints' => [
0 => 'http' ,
1 => 'https' ,
],
'service' => 'noop' ,
2024-10-10 17:47:01 +00:00
'rule' => 'HostRegexp(`.+`)' ,
'tls' => [
'certResolver' => 'letsencrypt' ,
],
2024-03-12 09:42:56 +00:00
'priority' => 1 ,
'middlewares' => [
2024-10-10 17:47:01 +00:00
0 => 'redirect-regexp' ,
2024-03-12 09:42:56 +00:00
],
],
],
2024-06-10 20:43:34 +00:00
'services' => [
'noop' => [
'loadBalancer' => [
'servers' => [
0 => [
2024-03-12 09:42:56 +00:00
'url' => '' ,
],
],
],
],
],
2024-06-10 20:43:34 +00:00
'middlewares' => [
'redirect-regexp' => [
'redirectRegex' => [
2024-03-12 09:42:56 +00:00
'regex' => '(.*)' ,
'replacement' => $redirect_url ,
'permanent' => false ,
],
],
],
],
];
$conf = Yaml :: dump ( $dynamic_conf , 12 , 2 );
$conf =
2024-10-17 20:08:23 +00:00
" # This file is automatically generated by Coolify. \n " .
" # Do not edit it manually (only if you know what are you doing). \n \n " .
2024-03-12 09:42:56 +00:00
$conf ;
$base64 = base64_encode ( $conf );
2024-09-23 20:57:24 +00:00
} elseif ( $proxy_type === ProxyTypes :: CADDY -> value ) {
2024-06-10 20:43:34 +00:00
$conf = " :80, :443 {
2024-03-12 09:42:56 +00:00
redir $redirect_url
} " ;
$conf =
2024-10-17 20:08:23 +00:00
" # This file is automatically generated by Coolify. \n " .
" # Do not edit it manually (only if you know what are you doing). \n \n " .
2024-03-12 09:42:56 +00:00
$conf ;
$base64 = base64_encode ( $conf );
}
instant_remote_process ([
" mkdir -p $dynamic_conf_path " ,
2024-04-17 08:49:34 +00:00
" echo ' $base64 ' | base64 -d | tee $default_redirect_file > /dev/null " ,
2024-03-12 09:42:56 +00:00
], $this );
if ( $proxy_type === 'CADDY' ) {
$this -> reloadCaddy ();
}
}
2024-06-10 20:43:34 +00:00
2024-03-11 16:17:34 +00:00
public function setupDynamicProxyConfiguration ()
{
2024-10-01 08:37:40 +00:00
$settings = instanceSettings ();
2024-10-17 20:08:23 +00:00
$dynamic_config_path = $this -> proxyPath () . '/dynamic' ;
2024-08-07 15:52:51 +00:00
if ( $this -> proxyType () === ProxyTypes :: TRAEFIK -> value ) {
2024-03-12 11:30:40 +00:00
$file = " $dynamic_config_path /coolify.yaml " ;
2024-07-11 09:20:09 +00:00
if ( empty ( $settings -> fqdn ) || ( isCloud () && $this -> id !== 0 ) || ! $this -> isLocalhost ()) {
2024-03-12 11:30:40 +00:00
instant_remote_process ([
" rm -f $file " ,
], $this );
} else {
$url = Url :: fromString ( $settings -> fqdn );
$host = $url -> getHost ();
$schema = $url -> getScheme ();
$traefik_dynamic_conf = [
2024-06-10 20:43:34 +00:00
'http' => [
2024-03-12 11:30:40 +00:00
'middlewares' => [
'redirect-to-https' => [
'redirectscheme' => [
'scheme' => 'https' ,
],
],
'gzip' => [
'compress' => true ,
],
],
2024-06-10 20:43:34 +00:00
'routers' => [
'coolify-http' => [
2024-03-12 11:30:40 +00:00
'middlewares' => [
0 => 'gzip' ,
2024-03-11 16:17:34 +00:00
],
2024-03-12 11:30:40 +00:00
'entryPoints' => [
0 => 'http' ,
2024-03-11 16:17:34 +00:00
],
2024-03-12 11:30:40 +00:00
'service' => 'coolify' ,
'rule' => " Host(` { $host } `) " ,
2024-03-11 16:17:34 +00:00
],
2024-06-10 20:43:34 +00:00
'coolify-realtime-ws' => [
2024-03-12 11:30:40 +00:00
'entryPoints' => [
0 => 'http' ,
2024-03-11 16:17:34 +00:00
],
2024-03-12 11:30:40 +00:00
'service' => 'coolify-realtime' ,
'rule' => " Host(` { $host } `) && PathPrefix(`/app`) " ,
2024-03-11 16:17:34 +00:00
],
2024-09-13 10:21:02 +00:00
'coolify-terminal-ws' => [
'entryPoints' => [
0 => 'http' ,
],
'service' => 'coolify-terminal' ,
2024-09-13 14:58:16 +00:00
'rule' => " Host(` { $host } `) && PathPrefix(`/terminal/ws`) " ,
2024-09-13 10:21:02 +00:00
],
2024-03-12 11:30:40 +00:00
],
2024-06-10 20:43:34 +00:00
'services' => [
'coolify' => [
'loadBalancer' => [
'servers' => [
0 => [
2024-03-12 11:30:40 +00:00
'url' => 'http://coolify:80' ,
2024-03-11 16:17:34 +00:00
],
],
],
2024-03-12 11:30:40 +00:00
],
2024-06-10 20:43:34 +00:00
'coolify-realtime' => [
'loadBalancer' => [
'servers' => [
0 => [
2024-03-12 11:30:40 +00:00
'url' => 'http://coolify-realtime:6001' ,
2024-03-11 16:17:34 +00:00
],
],
],
],
2024-09-13 10:21:02 +00:00
'coolify-terminal' => [
'loadBalancer' => [
'servers' => [
0 => [
'url' => 'http://coolify-realtime:6002' ,
],
],
],
],
2024-03-11 16:17:34 +00:00
],
2024-03-12 11:30:40 +00:00
],
];
2024-03-11 16:17:34 +00:00
2024-03-12 11:30:40 +00:00
if ( $schema === 'https' ) {
$traefik_dynamic_conf [ 'http' ][ 'routers' ][ 'coolify-http' ][ 'middlewares' ] = [
0 => 'redirect-to-https' ,
];
2024-03-11 16:17:34 +00:00
2024-03-12 11:30:40 +00:00
$traefik_dynamic_conf [ 'http' ][ 'routers' ][ 'coolify-https' ] = [
'entryPoints' => [
0 => 'https' ,
],
'service' => 'coolify' ,
'rule' => " Host(` { $host } `) " ,
'tls' => [
'certresolver' => 'letsencrypt' ,
],
];
$traefik_dynamic_conf [ 'http' ][ 'routers' ][ 'coolify-realtime-wss' ] = [
'entryPoints' => [
0 => 'https' ,
],
'service' => 'coolify-realtime' ,
'rule' => " Host(` { $host } `) && PathPrefix(`/app`) " ,
'tls' => [
'certresolver' => 'letsencrypt' ,
],
];
2024-09-13 10:21:02 +00:00
$traefik_dynamic_conf [ 'http' ][ 'routers' ][ 'coolify-terminal-wss' ] = [
'entryPoints' => [
0 => 'https' ,
],
'service' => 'coolify-terminal' ,
2024-09-13 14:58:16 +00:00
'rule' => " Host(` { $host } `) && PathPrefix(`/terminal/ws`) " ,
2024-09-13 10:21:02 +00:00
'tls' => [
'certresolver' => 'letsencrypt' ,
],
];
2024-03-12 11:30:40 +00:00
}
$yaml = Yaml :: dump ( $traefik_dynamic_conf , 12 , 2 );
$yaml =
2024-10-17 20:08:23 +00:00
" # This file is automatically generated by Coolify. \n " .
" # Do not edit it manually (only if you know what are you doing). \n \n " .
2024-03-12 11:30:40 +00:00
$yaml ;
2024-03-11 16:17:34 +00:00
2024-03-12 11:30:40 +00:00
$base64 = base64_encode ( $yaml );
instant_remote_process ([
" mkdir -p $dynamic_config_path " ,
2024-04-17 08:49:34 +00:00
" echo ' $base64 ' | base64 -d | tee $file > /dev/null " ,
2024-03-12 11:30:40 +00:00
], $this );
2024-03-11 16:17:34 +00:00
2024-03-12 11:30:40 +00:00
if ( config ( 'app.env' ) == 'local' ) {
// ray($yaml);
2024-03-11 16:17:34 +00:00
}
2024-03-12 11:30:40 +00:00
}
2024-06-10 20:43:34 +00:00
} elseif ( $this -> proxyType () === 'CADDY' ) {
2024-03-12 11:30:40 +00:00
$file = " $dynamic_config_path /coolify.caddy " ;
2024-07-11 09:20:09 +00:00
if ( empty ( $settings -> fqdn ) || ( isCloud () && $this -> id !== 0 ) || ! $this -> isLocalhost ()) {
2024-03-12 11:30:40 +00:00
instant_remote_process ([
" rm -f $file " ,
], $this );
$this -> reloadCaddy ();
} else {
$url = Url :: fromString ( $settings -> fqdn );
$host = $url -> getHost ();
$schema = $url -> getScheme ();
$caddy_file = "
2024-03-11 16:17:34 +00:00
$schema :// $host {
2024-03-12 10:34:57 +00:00
handle / app /* {
reverse_proxy coolify - realtime : 6001
}
2024-09-18 11:33:38 +00:00
handle / terminal / ws {
2024-09-13 10:21:02 +00:00
reverse_proxy coolify - realtime : 6002
}
2024-03-11 16:17:34 +00:00
reverse_proxy coolify : 80
} " ;
2024-03-12 11:30:40 +00:00
$base64 = base64_encode ( $caddy_file );
instant_remote_process ([
2024-04-17 08:49:34 +00:00
" echo ' $base64 ' | base64 -d | tee $file > /dev/null " ,
2024-03-12 11:30:40 +00:00
], $this );
$this -> reloadCaddy ();
2024-03-11 16:17:34 +00:00
}
}
}
2024-06-10 20:43:34 +00:00
2024-03-12 09:42:56 +00:00
public function reloadCaddy ()
{
2024-03-11 16:31:28 +00:00
return instant_remote_process ([
2024-06-10 20:43:34 +00:00
'docker exec coolify-proxy caddy reload --config /config/caddy/Caddyfile.autosave' ,
2024-03-11 16:31:28 +00:00
], $this );
}
2024-06-10 20:43:34 +00:00
2024-03-11 16:17:34 +00:00
public function proxyPath ()
{
2024-03-11 14:08:05 +00:00
$base_path = config ( 'coolify.base_config_path' );
$proxyType = $this -> proxyType ();
$proxy_path = " $base_path /proxy " ;
2024-03-11 19:17:37 +00:00
// TODO: should use /traefik for already exisiting configurations?
// Should move everything except /caddy and /nginx to /traefik
// The code needs to be modified as well, so maybe it does not worth it
2024-08-07 15:52:51 +00:00
if ( $proxyType === ProxyTypes :: TRAEFIK -> value ) {
2024-10-04 11:25:05 +00:00
// Do nothing
2024-06-10 20:43:34 +00:00
} elseif ( $proxyType === ProxyTypes :: CADDY -> value ) {
2024-10-04 11:46:33 +00:00
if ( isDev ()) {
$proxy_path = '/var/lib/docker/volumes/coolify_dev_coolify_data/_data/proxy/caddy' ;
} else {
2024-10-17 20:08:23 +00:00
$proxy_path = $proxy_path . '/caddy' ;
2024-10-04 11:46:33 +00:00
}
2024-06-10 20:43:34 +00:00
} elseif ( $proxyType === ProxyTypes :: NGINX -> value ) {
2024-10-04 11:46:33 +00:00
if ( isDev ()) {
$proxy_path = '/var/lib/docker/volumes/coolify_dev_coolify_data/_data/proxy/nginx' ;
} else {
2024-10-17 20:08:23 +00:00
$proxy_path = $proxy_path . '/nginx' ;
2024-10-04 11:46:33 +00:00
}
2024-10-04 11:25:05 +00:00
}
2024-06-10 20:43:34 +00:00
2024-03-11 14:08:05 +00:00
return $proxy_path ;
}
2024-06-10 20:43:34 +00:00
2023-09-25 07:17:42 +00:00
public function proxyType ()
{
2024-03-12 11:45:55 +00:00
return data_get ( $this -> proxy , 'type' );
2023-09-20 13:42:41 +00:00
}
2024-06-10 20:43:34 +00:00
2023-06-20 18:19:31 +00:00
public function scopeWithProxy () : Builder
2023-05-30 13:52:17 +00:00
{
2023-06-20 18:19:31 +00:00
return $this -> proxy -> modelScope ();
2023-05-30 13:52:17 +00:00
}
2023-08-08 09:51:36 +00:00
2023-11-16 13:29:01 +00:00
public function isLocalhost ()
{
return $this -> ip === 'host.docker.internal' || $this -> id === 0 ;
2023-11-16 13:28:26 +00:00
}
2024-06-10 20:43:34 +00:00
public static function buildServers ( $teamId )
2024-01-17 10:52:56 +00:00
{
return Server :: whereTeamId ( $teamId ) -> whereRelation ( 'settings' , 'is_reachable' , true ) -> whereRelation ( 'settings' , 'is_build_server' , true );
}
2024-06-10 20:43:34 +00:00
2023-11-17 11:47:15 +00:00
public function skipServer ()
{
if ( $this -> ip === '1.2.3.4' ) {
2024-02-26 13:22:24 +00:00
// ray('skipping 1.2.3.4');
2023-11-17 11:47:15 +00:00
return true ;
}
2024-02-26 09:25:21 +00:00
if ( $this -> settings -> force_disabled === true ) {
2024-02-26 13:22:24 +00:00
// ray('force_disabled');
2024-02-26 09:25:21 +00:00
return true ;
}
2024-06-10 20:43:34 +00:00
2023-11-17 11:47:15 +00:00
return false ;
}
2024-06-10 20:43:34 +00:00
2024-02-26 09:25:21 +00:00
public function isForceDisabled ()
{
return $this -> settings -> force_disabled ;
}
2024-06-10 20:43:34 +00:00
2024-02-26 09:25:21 +00:00
public function forceEnableServer ()
{
2024-10-25 13:13:23 +00:00
$this -> settings -> force_disabled = false ;
$this -> settings -> save ();
2024-02-26 09:25:21 +00:00
}
2024-06-10 20:43:34 +00:00
2024-02-26 09:25:21 +00:00
public function forceDisableServer ()
{
2024-10-25 13:13:23 +00:00
$this -> settings -> force_disabled = true ;
$this -> settings -> save ();
2024-02-26 09:25:21 +00:00
$sshKeyFileLocation = " id.root@ { $this -> uuid } " ;
Storage :: disk ( 'ssh-keys' ) -> delete ( $sshKeyFileLocation );
Storage :: disk ( 'ssh-mux' ) -> delete ( $this -> muxFilename ());
2024-02-25 22:34:01 +00:00
}
2024-06-10 20:43:34 +00:00
2024-10-17 09:21:43 +00:00
public function sentinelHeartbeat ( bool $isReset = false )
2024-10-15 11:39:19 +00:00
{
2024-10-17 20:08:23 +00:00
$this -> sentinel_updated_at = $isReset ? now () -> subMinutes ( 6000 ) : now ();
2024-10-15 11:39:19 +00:00
$this -> save ();
}
2024-10-17 20:08:23 +00:00
2024-10-22 12:49:23 +00:00
/**
2024-10-22 12:49:42 +00:00
* Get the wait time for Sentinel to push before performing an SSH check .
2024-10-22 12:49:23 +00:00
*
* @ return int The wait time in seconds .
*/
public function waitBeforeDoingSshCheck () : int
{
$wait = $this -> settings -> sentinel_push_interval_seconds * 3 ;
if ( $wait < 120 ) {
$wait = 120 ;
}
return $wait ;
}
2024-10-15 11:39:19 +00:00
public function isSentinelLive ()
{
2024-10-22 12:49:23 +00:00
return Carbon :: parse ( $this -> sentinel_updated_at ) -> isAfter ( now () -> subSeconds ( $this -> waitBeforeDoingSshCheck ()));
2024-10-15 11:39:19 +00:00
}
2024-06-20 11:17:06 +00:00
public function isSentinelEnabled ()
{
2024-10-17 20:08:23 +00:00
return ( $this -> isMetricsEnabled () || $this -> isServerApiEnabled ()) && ! $this -> isBuildServer ();
2024-06-20 11:17:06 +00:00
}
2024-06-18 14:42:42 +00:00
public function isMetricsEnabled ()
{
return $this -> settings -> is_metrics_enabled ;
}
2024-06-18 14:43:18 +00:00
2024-06-20 11:17:06 +00:00
public function isServerApiEnabled ()
{
2024-10-15 11:39:19 +00:00
return $this -> settings -> is_sentinel_enabled ;
2024-06-20 11:17:06 +00:00
}
2024-05-08 18:59:58 +00:00
public function checkSentinel ()
{
2024-10-25 09:41:25 +00:00
CheckAndStartSentinelJob :: dispatch ( $this );
2024-05-08 12:22:35 +00:00
}
2024-06-10 20:43:34 +00:00
2024-06-18 14:42:42 +00:00
public function getCpuMetrics ( int $mins = 5 )
2024-05-09 11:25:18 +00:00
{
2024-06-18 14:42:42 +00:00
if ( $this -> isMetricsEnabled ()) {
$from = now () -> subMinutes ( $mins ) -> toIso8601ZuluString ();
2024-10-22 09:39:38 +00:00
$cpu = instant_remote_process ([ " docker exec coolify-sentinel sh -c 'curl -H \" Authorization: Bearer { $this -> settings -> sentinel_token } \" http://localhost:8888/api/cpu/history?from= $from ' " ], $this , false );
2024-06-18 14:42:42 +00:00
if ( str ( $cpu ) -> contains ( 'error' )) {
$error = json_decode ( $cpu , true );
$error = data_get ( $error , 'error' , 'Something is not okay, are you okay?' );
if ( $error == 'Unauthorized' ) {
$error = 'Unauthorized, please check your metrics token or restart Sentinel to set a new token.' ;
}
throw new \Exception ( $error );
}
2024-10-15 11:39:19 +00:00
$cpu = json_decode ( $cpu , true );
$parsedCollection = collect ( $cpu ) -> map ( function ( $metric ) {
2024-10-17 20:08:23 +00:00
return [( int ) $metric [ 'time' ], ( float ) $metric [ 'percent' ]];
2024-06-19 06:58:57 +00:00
});
2024-10-17 20:08:23 +00:00
2024-10-15 11:39:19 +00:00
return $parsedCollection ;
2024-05-09 11:25:18 +00:00
}
}
2024-06-18 14:43:18 +00:00
2024-06-18 14:42:42 +00:00
public function getMemoryMetrics ( int $mins = 5 )
{
if ( $this -> isMetricsEnabled ()) {
$from = now () -> subMinutes ( $mins ) -> toIso8601ZuluString ();
2024-10-22 09:39:38 +00:00
$memory = instant_remote_process ([ " docker exec coolify-sentinel sh -c 'curl -H \" Authorization: Bearer { $this -> settings -> sentinel_token } \" http://localhost:8888/api/memory/history?from= $from ' " ], $this , false );
2024-06-18 14:42:42 +00:00
if ( str ( $memory ) -> contains ( 'error' )) {
$error = json_decode ( $memory , true );
$error = data_get ( $error , 'error' , 'Something is not okay, are you okay?' );
if ( $error == 'Unauthorized' ) {
$error = 'Unauthorized, please check your metrics token or restart Sentinel to set a new token.' ;
}
throw new \Exception ( $error );
}
2024-10-15 11:39:19 +00:00
$memory = json_decode ( $memory , true );
2024-10-17 20:08:23 +00:00
$parsedCollection = collect ( $memory ) -> map ( function ( $metric ) {
return [( int ) $metric [ 'time' ], ( float ) $metric [ 'usedPercent' ]];
2024-06-19 06:58:57 +00:00
});
2024-06-18 14:42:42 +00:00
2024-06-19 06:58:57 +00:00
return $parsedCollection -> toArray ();
2024-06-18 14:42:42 +00:00
}
}
2024-06-10 20:43:34 +00:00
2024-08-21 08:50:05 +00:00
public function getDiskUsage () : ? string
2023-11-16 10:53:37 +00:00
{
2024-10-22 12:01:36 +00:00
return instant_remote_process ([ 'df / --output=pcent | tr -cd 0-9' ], $this , false );
// return instant_remote_process(["df /| tail -1 | awk '{ print $5}' | sed 's/%//g'"], $this, false);
2023-11-16 10:53:37 +00:00
}
2024-06-10 20:43:34 +00:00
2023-11-28 12:12:25 +00:00
public function definedResources ()
{
2023-11-27 08:39:43 +00:00
$applications = $this -> applications ();
$databases = $this -> databases ();
$services = $this -> services ();
2024-06-10 20:43:34 +00:00
2023-11-27 08:39:43 +00:00
return $applications -> concat ( $databases ) -> concat ( $services -> get ());
}
2024-06-10 20:43:34 +00:00
2024-02-19 12:28:14 +00:00
public function stopUnmanaged ( $id )
{
2024-02-16 22:09:35 +00:00
return instant_remote_process ([ " docker stop -t 0 $id " ], $this );
}
2024-06-10 20:43:34 +00:00
2024-02-19 12:28:14 +00:00
public function restartUnmanaged ( $id )
{
2024-02-16 22:09:35 +00:00
return instant_remote_process ([ " docker restart $id " ], $this );
}
2024-06-10 20:43:34 +00:00
2024-02-19 12:28:14 +00:00
public function startUnmanaged ( $id )
{
2024-02-16 22:09:35 +00:00
return instant_remote_process ([ " docker start $id " ], $this );
}
2024-06-10 20:43:34 +00:00
2024-08-05 13:48:15 +00:00
public function getContainers ()
{
$containers = collect ([]);
$containerReplicates = collect ([]);
if ( $this -> isSwarm ()) {
$containers = instant_remote_process ([ " docker service inspect $ (docker service ls -q) --format ' { { json .}}' " ], $this , false );
$containers = format_docker_command_output_to_json ( $containers );
$containerReplicates = instant_remote_process ([ " docker service ls --format ' { { json .}}' " ], $this , false );
if ( $containerReplicates ) {
$containerReplicates = format_docker_command_output_to_json ( $containerReplicates );
foreach ( $containerReplicates as $containerReplica ) {
$name = data_get ( $containerReplica , 'Name' );
$containers = $containers -> map ( function ( $container ) use ( $name , $containerReplica ) {
if ( data_get ( $container , 'Spec.Name' ) === $name ) {
$replicas = data_get ( $containerReplica , 'Replicas' );
$running = str ( $replicas ) -> explode ( '/' )[ 0 ];
$total = str ( $replicas ) -> explode ( '/' )[ 1 ];
if ( $running === $total ) {
data_set ( $container , 'State.Status' , 'running' );
data_set ( $container , 'State.Health.Status' , 'healthy' );
} else {
data_set ( $container , 'State.Status' , 'starting' );
data_set ( $container , 'State.Health.Status' , 'unhealthy' );
}
}
return $container ;
});
}
}
} else {
$containers = instant_remote_process ([ " docker container inspect $ (docker container ls -q) --format ' { { json .}}' " ], $this , false );
$containers = format_docker_command_output_to_json ( $containers );
$containerReplicates = collect ([]);
}
return [
2024-08-08 11:20:10 +00:00
'containers' => collect ( $containers ) ? ? collect ([]),
'containerReplicates' => collect ( $containerReplicates ) ? ? collect ([]),
2024-08-05 13:48:15 +00:00
];
}
public function getContainersWithSentinel () : Collection
2024-05-08 18:59:58 +00:00
{
2024-06-10 20:43:34 +00:00
$sentinel_found = instant_remote_process ([ 'docker inspect coolify-sentinel' ], $this , false );
2024-05-08 18:59:58 +00:00
$sentinel_found = json_decode ( $sentinel_found , true );
$status = data_get ( $sentinel_found , '0.State.Status' , 'exited' );
if ( $status === 'running' ) {
$containers = instant_remote_process ([ 'docker exec coolify-sentinel sh -c "curl http://127.0.0.1:8888/api/containers"' ], $this , false );
if ( is_null ( $containers )) {
return collect ([]);
}
$containers = data_get ( json_decode ( $containers , true ), 'containers' , []);
2024-06-10 20:43:34 +00:00
2024-05-08 18:59:58 +00:00
return collect ( $containers );
}
}
2024-06-10 20:43:34 +00:00
2024-09-17 09:54:25 +00:00
public function loadAllContainers () : Collection
{
if ( $this -> isFunctional ()) {
$containers = instant_remote_process ([ " docker ps -a --format ' { { json .}}' " ], $this );
$containers = format_docker_command_output_to_json ( $containers );
return collect ( $containers );
}
return collect ([]);
}
2024-05-08 18:59:58 +00:00
public function loadUnmanagedContainers () : Collection
2024-02-16 22:09:35 +00:00
{
2024-04-09 06:46:00 +00:00
if ( $this -> isFunctional ()) {
2024-04-16 13:42:38 +00:00
$containers = instant_remote_process ([ " docker ps -a --format ' { { json .}}' " ], $this );
2024-04-09 06:46:00 +00:00
$containers = format_docker_command_output_to_json ( $containers );
$containers = $containers -> map ( function ( $container ) {
$labels = data_get ( $container , 'Labels' );
2024-06-18 14:43:18 +00:00
if ( ! str ( $labels ) -> contains ( 'coolify.managed' )) {
2024-04-09 06:46:00 +00:00
return $container ;
}
2024-06-10 20:43:34 +00:00
2024-04-09 06:46:00 +00:00
return null ;
});
$containers = $containers -> filter ();
2024-06-10 20:43:34 +00:00
2024-04-09 06:46:00 +00:00
return collect ( $containers );
} else {
return collect ([]);
}
2024-02-16 22:09:35 +00:00
}
2024-06-10 20:43:34 +00:00
2023-11-06 17:04:18 +00:00
public function hasDefinedResources ()
2023-06-15 13:15:27 +00:00
{
2023-11-14 14:06:03 +00:00
$applications = $this -> applications () -> count () > 0 ;
$databases = $this -> databases () -> count () > 0 ;
$services = $this -> services () -> count () > 0 ;
2023-11-06 17:04:18 +00:00
if ( $applications || $databases || $services ) {
2023-06-15 13:15:27 +00:00
return true ;
}
2024-06-10 20:43:34 +00:00
2023-06-15 13:15:27 +00:00
return false ;
}
2023-08-08 09:51:36 +00:00
2023-09-11 20:29:34 +00:00
public function databases ()
{
2023-08-29 13:51:30 +00:00
return $this -> destinations () -> map ( function ( $standaloneDocker ) {
2023-10-24 12:31:28 +00:00
$postgresqls = data_get ( $standaloneDocker , 'postgresqls' , collect ([]));
$redis = data_get ( $standaloneDocker , 'redis' , collect ([]));
$mongodbs = data_get ( $standaloneDocker , 'mongodbs' , collect ([]));
$mysqls = data_get ( $standaloneDocker , 'mysqls' , collect ([]));
$mariadbs = data_get ( $standaloneDocker , 'mariadbs' , collect ([]));
2024-04-10 13:00:46 +00:00
$keydbs = data_get ( $standaloneDocker , 'keydbs' , collect ([]));
$dragonflies = data_get ( $standaloneDocker , 'dragonflies' , collect ([]));
$clickhouses = data_get ( $standaloneDocker , 'clickhouses' , collect ([]));
2024-06-10 20:43:34 +00:00
2024-04-10 13:00:46 +00:00
return $postgresqls -> concat ( $redis ) -> concat ( $mongodbs ) -> concat ( $mysqls ) -> concat ( $mariadbs ) -> concat ( $keydbs ) -> concat ( $dragonflies ) -> concat ( $clickhouses );
2024-09-19 11:23:48 +00:00
}) -> flatten () -> filter ( function ( $item ) {
2024-02-19 12:28:14 +00:00
return data_get ( $item , 'name' ) !== 'coolify-db' ;
2024-09-19 11:23:48 +00:00
});
2023-08-29 13:51:30 +00:00
}
2024-06-10 20:43:34 +00:00
2023-06-15 13:15:27 +00:00
public function applications ()
{
2024-02-06 14:42:31 +00:00
$applications = $this -> destinations () -> map ( function ( $standaloneDocker ) {
2023-06-15 13:15:27 +00:00
return $standaloneDocker -> applications ;
}) -> flatten ();
2024-02-06 14:42:31 +00:00
$additionalApplicationIds = DB :: table ( 'additional_destinations' ) -> where ( 'server_id' , $this -> id ) -> get ( 'application_id' );
$additionalApplicationIds = collect ( $additionalApplicationIds ) -> map ( function ( $item ) {
return $item -> application_id ;
});
Application :: whereIn ( 'id' , $additionalApplicationIds ) -> get () -> each ( function ( $application ) use ( $applications ) {
$applications -> push ( $application );
});
2024-06-10 20:43:34 +00:00
2024-02-06 14:42:31 +00:00
return $applications ;
2023-06-15 13:15:27 +00:00
}
2024-06-10 20:43:34 +00:00
2023-11-28 11:48:55 +00:00
public function dockerComposeBasedApplications ()
{
return $this -> applications () -> filter ( function ( $application ) {
return data_get ( $application , 'build_pack' ) === 'dockercompose' ;
});
}
2024-06-10 20:43:34 +00:00
2023-11-28 11:48:55 +00:00
public function dockerComposeBasedPreviewDeployments ()
{
return $this -> previews () -> filter ( function ( $preview ) {
$applicationId = data_get ( $preview , 'application_id' );
$application = Application :: find ( $applicationId );
2024-06-18 14:43:18 +00:00
if ( ! $application ) {
2023-11-28 11:48:55 +00:00
return false ;
}
2024-06-10 20:43:34 +00:00
2023-11-28 11:48:55 +00:00
return data_get ( $application , 'build_pack' ) === 'dockercompose' ;
});
}
2024-06-10 20:43:34 +00:00
2023-09-25 07:17:42 +00:00
public function services ()
{
2023-09-21 15:48:31 +00:00
return $this -> hasMany ( Service :: class );
}
2024-06-10 20:43:34 +00:00
2024-09-20 16:14:52 +00:00
public function port () : Attribute
{
return Attribute :: make (
get : function ( $value ) {
return preg_replace ( '/[^0-9]/' , '' , $value );
}
);
}
public function user () : Attribute
{
return Attribute :: make (
get : function ( $value ) {
$sanitizedValue = preg_replace ( '/[^A-Za-z0-9\-_]/' , '' , $value );
return $sanitizedValue ;
}
);
}
public function ip () : Attribute
{
return Attribute :: make (
get : function ( $value ) {
2024-09-23 20:57:24 +00:00
return preg_replace ( '/[^0-9a-zA-Z.:%-]/' , '' , $value );
2024-09-20 16:14:52 +00:00
}
);
}
2023-10-04 08:57:44 +00:00
public function getIp () : Attribute
{
return Attribute :: make (
get : function () {
if ( isDev ()) {
return '127.0.0.1' ;
}
2023-11-16 13:29:01 +00:00
if ( $this -> isLocalhost ()) {
2023-10-04 08:57:44 +00:00
return base_ip ();
}
2024-06-10 20:43:34 +00:00
2023-10-04 08:57:44 +00:00
return $this -> ip ;
}
);
}
2024-06-10 20:43:34 +00:00
2023-09-25 07:17:42 +00:00
public function previews ()
{
2023-09-14 13:52:04 +00:00
return $this -> destinations () -> map ( function ( $standaloneDocker ) {
return $standaloneDocker -> applications -> map ( function ( $application ) {
return $application -> previews ;
}) -> flatten ();
}) -> flatten ();
}
2023-06-13 08:02:58 +00:00
public function destinations ()
{
$standalone_docker = $this -> hasMany ( StandaloneDocker :: class ) -> get ();
$swarm_docker = $this -> hasMany ( SwarmDocker :: class ) -> get ();
2024-06-10 20:43:34 +00:00
2024-03-02 14:58:02 +00:00
// $additional_dockers = $this->belongsToMany(StandaloneDocker::class, 'additional_destinations')->withPivot('server_id')->get();
// return $standalone_docker->concat($swarm_docker)->concat($additional_dockers);
return $standalone_docker -> concat ( $swarm_docker );
2023-06-13 08:02:58 +00:00
}
2023-08-08 09:51:36 +00:00
2023-05-02 10:47:52 +00:00
public function standaloneDockers ()
2023-04-25 12:43:35 +00:00
{
2023-05-02 10:47:52 +00:00
return $this -> hasMany ( StandaloneDocker :: class );
}
2023-05-04 13:45:53 +00:00
2023-05-02 10:47:52 +00:00
public function swarmDockers ()
{
return $this -> hasMany ( SwarmDocker :: class );
2023-04-25 12:43:35 +00:00
}
2023-05-04 13:45:53 +00:00
2023-03-27 12:31:42 +00:00
public function privateKey ()
2023-03-24 14:47:58 +00:00
{
2023-03-27 12:31:42 +00:00
return $this -> belongsTo ( PrivateKey :: class );
2023-03-24 14:47:58 +00:00
}
2023-05-03 05:23:45 +00:00
2023-06-16 11:13:09 +00:00
public function muxFilename ()
{
2024-09-05 12:41:04 +00:00
return $this -> uuid ;
2023-06-16 11:13:09 +00:00
}
2023-08-08 09:51:36 +00:00
2023-07-07 19:35:29 +00:00
public function team ()
{
return $this -> belongsTo ( Team :: class );
}
2024-06-10 20:43:34 +00:00
2023-09-11 20:29:34 +00:00
public function isProxyShouldRun ()
{
2024-10-14 11:32:36 +00:00
// TODO: Do we need "|| $this->proxy->force_stop" here?
if ( $this -> proxyType () === ProxyTypes :: NONE -> value || $this -> isBuildServer ()) {
2023-09-25 07:17:42 +00:00
return false ;
}
2024-06-10 20:43:34 +00:00
2023-10-17 13:40:47 +00:00
return true ;
2023-09-11 20:29:34 +00:00
}
2024-06-10 20:43:34 +00:00
2023-09-25 07:17:42 +00:00
public function isFunctional ()
{
2024-06-18 14:43:18 +00:00
$isFunctional = $this -> settings -> is_reachable && $this -> settings -> is_usable && ! $this -> settings -> force_disabled ;
2024-09-19 10:32:56 +00:00
2024-06-18 14:43:18 +00:00
if ( ! $isFunctional ) {
2024-09-16 19:34:27 +00:00
Storage :: disk ( 'ssh-mux' ) -> delete ( $this -> muxFilename ());
2024-04-09 06:46:00 +00:00
}
2024-06-10 20:43:34 +00:00
2024-04-09 06:46:00 +00:00
return $isFunctional ;
2023-09-12 11:14:01 +00:00
}
2024-06-10 20:43:34 +00:00
2024-03-04 10:01:14 +00:00
public function isLogDrainEnabled ()
2023-11-17 11:22:45 +00:00
{
2023-12-01 10:13:58 +00:00
return $this -> settings -> is_logdrain_newrelic_enabled || $this -> settings -> is_logdrain_highlight_enabled || $this -> settings -> is_logdrain_axiom_enabled || $this -> settings -> is_logdrain_custom_enabled ;
2023-11-17 10:13:16 +00:00
}
2024-06-10 20:43:34 +00:00
public function validateOS () : bool | Stringable
2023-11-21 10:39:19 +00:00
{
$os_release = instant_remote_process ([ 'cat /etc/os-release' ], $this );
2024-01-08 20:59:26 +00:00
$releaseLines = collect ( explode ( " \n " , $os_release ));
2023-11-21 10:39:19 +00:00
$collectedData = collect ([]);
2024-01-08 20:59:26 +00:00
foreach ( $releaseLines as $line ) {
2024-06-25 08:37:10 +00:00
$item = str ( $line ) -> trim ();
2023-11-21 10:39:19 +00:00
$collectedData -> put ( $item -> before ( '=' ) -> value (), $item -> after ( '=' ) -> lower () -> replace ( '"' , '' ) -> value ());
}
$ID = data_get ( $collectedData , 'ID' );
2023-11-28 12:12:25 +00:00
// $ID_LIKE = data_get($collectedData, 'ID_LIKE');
// $VERSION_ID = data_get($collectedData, 'VERSION_ID');
$supported = collect ( SUPPORTED_OS ) -> filter ( function ( $supportedOs ) use ( $ID ) {
if ( str ( $supportedOs ) -> contains ( $ID )) {
return str ( $ID );
}
});
if ( $supported -> count () === 1 ) {
2024-02-22 10:28:45 +00:00
// ray('supported');
2023-11-28 12:17:59 +00:00
return str ( $supported -> first ());
2023-11-21 10:39:19 +00:00
} else {
2024-02-22 10:28:45 +00:00
// ray('not supported');
2023-11-21 10:39:19 +00:00
return false ;
}
}
2024-06-10 20:43:34 +00:00
2023-11-28 14:49:24 +00:00
public function isSwarm ()
{
2023-11-29 09:06:52 +00:00
return data_get ( $this , 'settings.is_swarm_manager' ) || data_get ( $this , 'settings.is_swarm_worker' );
2023-11-28 14:49:24 +00:00
}
2024-06-10 20:43:34 +00:00
2023-12-18 13:01:25 +00:00
public function isSwarmManager ()
{
return data_get ( $this , 'settings.is_swarm_manager' );
}
2024-06-10 20:43:34 +00:00
2023-12-18 13:01:25 +00:00
public function isSwarmWorker ()
{
return data_get ( $this , 'settings.is_swarm_worker' );
}
2024-06-10 20:43:34 +00:00
2024-10-25 13:13:23 +00:00
public function serverStatus () : bool
{
if ( $this -> status () === false ) {
return false ;
}
if ( $this -> isFunctional () === false ) {
return false ;
}
return true ;
}
2024-10-15 11:39:19 +00:00
public function status () : bool
{
2024-10-25 13:13:23 +00:00
if ( $this -> skipServer ()) {
return false ;
}
2024-10-15 11:39:19 +00:00
[ 'uptime' => $uptime ] = $this -> validateConnection ( false );
2024-10-25 13:13:23 +00:00
if ( $uptime === false ) {
foreach ( $this -> applications () as $application ) {
$application -> status = 'exited' ;
$application -> save ();
2024-10-15 11:39:19 +00:00
}
2024-10-25 13:13:23 +00:00
foreach ( $this -> databases () as $database ) {
$database -> status = 'exited' ;
$database -> save ();
2024-10-15 11:39:19 +00:00
}
2024-10-25 13:13:23 +00:00
foreach ( $this -> services () as $service ) {
2024-10-15 11:39:19 +00:00
$apps = $service -> applications () -> get ();
$dbs = $service -> databases () -> get ();
foreach ( $apps as $app ) {
2024-10-25 13:13:23 +00:00
$app -> status = 'exited' ;
$app -> save ();
2024-10-15 11:39:19 +00:00
}
foreach ( $dbs as $db ) {
2024-10-25 13:13:23 +00:00
$db -> status = 'exited' ;
$db -> save ();
2024-10-15 11:39:19 +00:00
}
}
return false ;
}
return true ;
}
2024-10-17 20:08:23 +00:00
2024-10-25 13:13:23 +00:00
public function isReachableChanged ()
{
$this -> refresh ();
$unreachableNotificationSent = ( bool ) $this -> unreachable_notification_sent ;
$isReachable = ( bool ) $this -> settings -> is_reachable ;
loggy ( 'Server setting is_reachable changed to ' . $isReachable . ' for server ' . $this -> id . '. Unreachable notification sent: ' . $unreachableNotificationSent );
// If the server is reachable, send the reachable notification if it was sent before
if ( $isReachable === true ) {
if ( $unreachableNotificationSent === true ) {
$this -> sendReachableNotification ();
}
} else {
// If the server is unreachable, send the unreachable notification if it was not sent before
if ( $unreachableNotificationSent === false ) {
$this -> sendUnreachableNotification ();
}
}
}
public function sendReachableNotification ()
{
$this -> unreachable_notification_sent = false ;
$this -> save ();
$this -> refresh ();
$this -> team -> notify ( new Reachable ( $this ));
}
public function sendUnreachableNotification ()
{
$this -> unreachable_notification_sent = true ;
$this -> save ();
$this -> refresh ();
$this -> team -> notify ( new Unreachable ( $this ));
}
2024-09-17 10:26:11 +00:00
public function validateConnection ( $isManualCheck = true )
2023-10-09 09:00:18 +00:00
{
2024-09-19 10:32:56 +00:00
config () -> set ( 'constants.ssh.mux_enabled' , ! $isManualCheck );
2024-01-26 07:54:56 +00:00
2024-10-25 13:13:23 +00:00
if ( $this -> skipServer ()) {
2024-04-16 13:42:38 +00:00
return [ 'uptime' => false , 'error' => 'Server skipped.' ];
2023-11-17 11:47:15 +00:00
}
2024-04-16 13:42:38 +00:00
try {
2024-09-28 08:20:32 +00:00
// Make sure the private key is stored
2024-10-25 13:13:23 +00:00
if ( $this -> privateKey ) {
$this -> privateKey -> storeInFileSystem ();
2024-09-28 08:20:32 +00:00
}
2024-10-25 13:13:23 +00:00
instant_remote_process ([ 'ls /' ], $this );
if ( $this -> settings -> is_reachable === false ) {
$this -> settings -> is_reachable = true ;
$this -> settings -> save ();
2024-04-16 13:42:38 +00:00
}
2024-06-10 20:43:34 +00:00
2024-04-16 13:42:38 +00:00
return [ 'uptime' => true , 'error' => null ];
} catch ( \Throwable $e ) {
2024-10-25 13:13:23 +00:00
if ( $this -> settings -> is_reachable === true ) {
$this -> settings -> is_reachable = false ;
$this -> settings -> save ();
}
2024-06-10 20:43:34 +00:00
2024-04-16 13:42:38 +00:00
return [ 'uptime' => false , 'error' => $e -> getMessage ()];
2023-10-09 09:00:18 +00:00
}
}
2024-06-10 20:43:34 +00:00
2024-02-05 13:40:54 +00:00
public function installDocker ()
{
$activity = InstallDocker :: run ( $this );
2024-06-10 20:43:34 +00:00
2024-02-05 13:40:54 +00:00
return $activity ;
}
2024-06-10 20:43:34 +00:00
2023-10-09 09:00:18 +00:00
public function validateDockerEngine ( $throwError = false )
{
2024-06-10 20:43:34 +00:00
$dockerBinary = instant_remote_process ([ 'command -v docker' ], $this , false , no_sudo : true );
2023-10-09 09:00:18 +00:00
if ( is_null ( $dockerBinary )) {
$this -> settings -> is_usable = false ;
$this -> settings -> save ();
if ( $throwError ) {
2023-11-21 10:39:19 +00:00
throw new \Exception ( 'Server is not usable. Docker Engine is not installed.' );
2023-10-09 09:00:18 +00:00
}
2024-06-10 20:43:34 +00:00
2023-10-09 09:00:18 +00:00
return false ;
}
2024-02-22 10:28:45 +00:00
try {
2024-06-10 20:43:34 +00:00
instant_remote_process ([ 'docker version' ], $this );
2024-02-22 10:28:45 +00:00
} catch ( \Throwable $e ) {
$this -> settings -> is_usable = false ;
$this -> settings -> save ();
if ( $throwError ) {
throw new \Exception ( 'Server is not usable. Docker Engine is not running.' );
}
2024-06-10 20:43:34 +00:00
2024-02-22 10:28:45 +00:00
return false ;
}
2023-10-09 09:00:18 +00:00
$this -> settings -> is_usable = true ;
$this -> settings -> save ();
2024-01-16 14:19:14 +00:00
$this -> validateCoolifyNetwork ( isSwarm : false , isBuildServer : $this -> settings -> is_build_server );
2024-06-10 20:43:34 +00:00
2023-11-28 14:49:24 +00:00
return true ;
}
2024-06-10 20:43:34 +00:00
2024-02-11 14:32:58 +00:00
public function validateDockerCompose ( $throwError = false )
{
2024-06-10 20:43:34 +00:00
$dockerCompose = instant_remote_process ([ 'docker compose version' ], $this , false );
2024-02-11 14:32:58 +00:00
if ( is_null ( $dockerCompose )) {
$this -> settings -> is_usable = false ;
$this -> settings -> save ();
if ( $throwError ) {
throw new \Exception ( 'Server is not usable. Docker Compose is not installed.' );
}
2024-06-10 20:43:34 +00:00
2024-02-11 14:32:58 +00:00
return false ;
}
$this -> settings -> is_usable = true ;
$this -> settings -> save ();
2024-06-10 20:43:34 +00:00
2024-02-11 14:32:58 +00:00
return true ;
}
2024-06-10 20:43:34 +00:00
2023-11-28 14:49:24 +00:00
public function validateDockerSwarm ()
{
2024-06-10 20:43:34 +00:00
$swarmStatus = instant_remote_process ([ 'docker info|grep -i swarm' ], $this , false );
2023-11-28 14:49:24 +00:00
$swarmStatus = str ( $swarmStatus ) -> trim () -> after ( ':' ) -> trim ();
if ( $swarmStatus === 'inactive' ) {
throw new \Exception ( 'Docker Swarm is not initiated. Please join the server to a swarm before continuing.' );
2024-06-10 20:43:34 +00:00
2023-11-28 14:49:24 +00:00
return false ;
}
$this -> settings -> is_usable = true ;
$this -> settings -> save ();
$this -> validateCoolifyNetwork ( isSwarm : true );
2024-06-10 20:43:34 +00:00
2023-10-09 09:00:18 +00:00
return true ;
}
2024-06-10 20:43:34 +00:00
2023-10-09 09:00:18 +00:00
public function validateDockerEngineVersion ()
{
2024-06-10 20:43:34 +00:00
$dockerVersionRaw = instant_remote_process ([ 'docker version --format json' ], $this , false );
2024-05-31 07:41:34 +00:00
$dockerVersionJson = json_decode ( $dockerVersionRaw , true );
$dockerVersion = data_get ( $dockerVersionJson , 'Server.Version' , '0.0.0' );
2023-10-09 09:00:18 +00:00
$dockerVersion = checkMinimumDockerEngineVersion ( $dockerVersion );
if ( is_null ( $dockerVersion )) {
$this -> settings -> is_usable = false ;
$this -> settings -> save ();
2024-06-10 20:43:34 +00:00
2023-10-09 09:00:18 +00:00
return false ;
}
2023-11-21 10:39:19 +00:00
$this -> settings -> is_reachable = true ;
2023-10-09 09:00:18 +00:00
$this -> settings -> is_usable = true ;
$this -> settings -> save ();
2024-06-10 20:43:34 +00:00
2023-10-09 09:00:18 +00:00
return true ;
}
2024-06-10 20:43:34 +00:00
2024-01-16 14:19:14 +00:00
public function validateCoolifyNetwork ( $isSwarm = false , $isBuildServer = false )
2023-10-24 12:31:28 +00:00
{
2024-01-16 14:19:14 +00:00
if ( $isBuildServer ) {
return ;
}
2023-11-28 14:49:24 +00:00
if ( $isSwarm ) {
2024-06-10 20:43:34 +00:00
return instant_remote_process ([ 'docker network create --attachable --driver overlay coolify-overlay >/dev/null 2>&1 || true' ], $this , false );
2023-11-28 14:49:24 +00:00
} else {
2024-06-10 20:43:34 +00:00
return instant_remote_process ([ 'docker network create coolify --attachable >/dev/null 2>&1 || true' ], $this , false );
2023-11-28 14:49:24 +00:00
}
2023-10-09 09:00:18 +00:00
}
2024-06-10 20:43:34 +00:00
2024-04-16 13:42:38 +00:00
public function isNonRoot ()
{
2024-04-18 09:48:10 +00:00
if ( $this -> user instanceof Stringable ) {
return $this -> user -> value () !== 'root' ;
}
2024-06-10 20:43:34 +00:00
2024-04-16 13:42:38 +00:00
return $this -> user !== 'root' ;
}
2024-06-10 20:43:34 +00:00
public function isBuildServer ()
{
2024-05-24 09:50:16 +00:00
return $this -> settings -> is_build_server ;
}
2024-09-16 19:34:27 +00:00
public static function createWithPrivateKey ( array $data , PrivateKey $privateKey )
{
$server = new self ( $data );
$server -> privateKey () -> associate ( $privateKey );
$server -> save ();
2024-09-19 10:32:56 +00:00
2024-09-16 19:34:27 +00:00
return $server ;
}
2024-09-19 10:32:56 +00:00
public function updateWithPrivateKey ( array $data , ? PrivateKey $privateKey = null )
2024-09-16 19:34:27 +00:00
{
$this -> update ( $data );
if ( $privateKey ) {
$this -> privateKey () -> associate ( $privateKey );
$this -> save ();
}
2024-09-19 10:32:56 +00:00
2024-09-16 19:34:27 +00:00
return $this ;
}
2024-10-01 09:52:36 +00:00
public function storageCheck () : ? string
{
$commands = [
'df / --output=pcent | tr -cd 0-9' ,
];
return instant_remote_process ( $commands , $this , false );
}
2024-10-02 06:15:03 +00:00
public function isIpv6 () : bool
{
return str ( $this -> ip ) -> contains ( ':' );
}
2024-10-22 12:10:36 +00:00
2024-10-25 09:41:25 +00:00
public function restartSentinel ( bool $async = true ) : void
2024-10-22 12:10:36 +00:00
{
try {
2024-10-25 09:41:25 +00:00
if ( $async ) {
StartSentinel :: dispatch ( $this , true );
} else {
StartSentinel :: run ( $this , true );
}
2024-10-22 12:10:36 +00:00
} catch ( \Throwable $e ) {
loggy ( 'Error restarting Sentinel: ' . $e -> getMessage ());
}
}
2024-10-25 08:59:12 +00:00
2024-10-22 12:47:01 +00:00
public function url ()
{
return base_url () . '/server/' . $this -> uuid ;
}
2024-10-29 09:28:05 +00:00
public function restartContainer ( string $containerName )
{
return instant_remote_process ([ 'docker restart ' . $containerName ], $this , false );
}
2023-07-14 11:38:24 +00:00
}