2023-06-30 09:42:59 +00:00
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
namespace App\Console\Commands;
|
|
|
|
|
|
2024-07-12 13:05:12 +00:00
|
|
|
use App\Actions\Server\StopSentinel;
|
2024-08-05 11:51:34 +00:00
|
|
|
use App\Enums\ActivityTypes;
|
2023-06-30 20:26:40 +00:00
|
|
|
use App\Enums\ApplicationDeploymentStatus;
|
2023-06-30 09:42:59 +00:00
|
|
|
use App\Models\ApplicationDeploymentQueue;
|
2024-06-23 15:47:58 +00:00
|
|
|
use App\Models\Environment;
|
2023-12-27 22:06:22 +00:00
|
|
|
use App\Models\ScheduledDatabaseBackup;
|
2023-11-16 19:48:25 +00:00
|
|
|
use App\Models\Server;
|
2023-10-24 08:10:55 +00:00
|
|
|
use App\Models\StandalonePostgresql;
|
2024-11-03 14:35:17 +00:00
|
|
|
use App\Models\User;
|
2023-06-30 09:42:59 +00:00
|
|
|
use Illuminate\Console\Command;
|
2024-08-07 07:50:12 +00:00
|
|
|
use Illuminate\Support\Facades\File;
|
2023-11-15 09:18:41 +00:00
|
|
|
use Illuminate\Support\Facades\Http;
|
2023-06-30 09:42:59 +00:00
|
|
|
|
|
|
|
|
class Init extends Command
|
|
|
|
|
{
|
2024-09-23 18:29:22 +00:00
|
|
|
protected $signature = 'app:init {--force-cloud}';
|
2024-06-10 20:43:34 +00:00
|
|
|
|
2023-06-30 09:42:59 +00:00
|
|
|
protected $description = 'Cleanup instance related stuffs';
|
2023-08-08 09:51:36 +00:00
|
|
|
|
2024-08-06 08:53:13 +00:00
|
|
|
public $servers = null;
|
|
|
|
|
|
2023-06-30 09:42:59 +00:00
|
|
|
public function handle()
|
|
|
|
|
{
|
2024-09-23 18:54:56 +00:00
|
|
|
if (isCloud() && ! $this->option('force-cloud')) {
|
2024-09-23 18:29:22 +00:00
|
|
|
echo "Skipping init as we are on cloud and --force-cloud option is not set\n";
|
2024-06-10 20:43:34 +00:00
|
|
|
|
2024-02-08 11:47:00 +00:00
|
|
|
return;
|
|
|
|
|
}
|
2024-08-05 11:45:24 +00:00
|
|
|
|
2024-09-23 18:29:22 +00:00
|
|
|
$this->servers = Server::all();
|
|
|
|
|
if (isCloud()) {
|
|
|
|
|
} else {
|
|
|
|
|
$this->send_alive_signal();
|
|
|
|
|
get_public_ips();
|
2024-08-05 11:45:24 +00:00
|
|
|
}
|
2024-09-23 18:29:22 +00:00
|
|
|
|
|
|
|
|
// Backward compatibility
|
|
|
|
|
$this->disable_metrics();
|
|
|
|
|
$this->replace_slash_in_environment_name();
|
|
|
|
|
$this->restore_coolify_db_backup();
|
2024-11-03 14:35:17 +00:00
|
|
|
$this->update_user_emails();
|
2024-09-23 18:29:22 +00:00
|
|
|
//
|
|
|
|
|
$this->update_traefik_labels();
|
|
|
|
|
if (! isCloud() || $this->option('force-cloud')) {
|
2024-08-05 11:45:24 +00:00
|
|
|
$this->cleanup_unused_network_from_coolify_proxy();
|
2024-09-23 18:29:22 +00:00
|
|
|
}
|
|
|
|
|
if (isCloud()) {
|
2024-08-06 08:53:13 +00:00
|
|
|
$this->cleanup_unnecessary_dynamic_proxy_configuration();
|
2024-09-23 18:29:22 +00:00
|
|
|
} else {
|
2024-02-08 10:05:31 +00:00
|
|
|
$this->cleanup_in_progress_application_deployments();
|
2024-09-23 18:29:22 +00:00
|
|
|
}
|
2024-09-23 21:48:12 +00:00
|
|
|
$this->call('cleanup:redis');
|
2024-09-23 18:29:22 +00:00
|
|
|
$this->call('cleanup:stucked-resources');
|
2023-12-27 22:06:22 +00:00
|
|
|
|
2024-09-23 18:29:22 +00:00
|
|
|
if (isCloud()) {
|
|
|
|
|
$response = Http::retry(3, 1000)->get(config('constants.services.official'));
|
|
|
|
|
if ($response->successful()) {
|
|
|
|
|
$services = $response->json();
|
|
|
|
|
File::put(base_path('templates/service-templates.json'), json_encode($services));
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
try {
|
|
|
|
|
$localhost = $this->servers->where('id', 0)->first();
|
|
|
|
|
$localhost->setupDynamicProxyConfiguration();
|
|
|
|
|
} catch (\Throwable $e) {
|
|
|
|
|
echo "Could not setup dynamic configuration: {$e->getMessage()}\n";
|
|
|
|
|
}
|
2024-10-01 08:37:40 +00:00
|
|
|
$settings = instanceSettings();
|
2024-06-10 20:43:34 +00:00
|
|
|
if (! is_null(env('AUTOUPDATE', null))) {
|
2024-02-08 10:05:31 +00:00
|
|
|
if (env('AUTOUPDATE') == true) {
|
|
|
|
|
$settings->update(['is_auto_update_enabled' => true]);
|
|
|
|
|
} else {
|
|
|
|
|
$settings->update(['is_auto_update_enabled' => false]);
|
|
|
|
|
}
|
2023-12-11 22:26:49 +00:00
|
|
|
}
|
2024-09-23 18:29:22 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private function disable_metrics()
|
|
|
|
|
{
|
|
|
|
|
if (version_compare('4.0.0-beta.312', config('version'), '<=')) {
|
|
|
|
|
foreach ($this->servers as $server) {
|
|
|
|
|
if ($server->settings->is_metrics_enabled === true) {
|
|
|
|
|
$server->settings->update(['is_metrics_enabled' => false]);
|
|
|
|
|
}
|
|
|
|
|
if ($server->isFunctional()) {
|
2024-11-06 11:33:56 +00:00
|
|
|
StopSentinel::dispatch($server)->onQueue('high');
|
2024-08-07 07:50:12 +00:00
|
|
|
}
|
|
|
|
|
}
|
2023-12-11 19:55:58 +00:00
|
|
|
}
|
2023-11-16 19:48:25 +00:00
|
|
|
}
|
2024-04-25 11:52:52 +00:00
|
|
|
|
2024-11-03 14:35:17 +00:00
|
|
|
private function update_user_emails()
|
|
|
|
|
{
|
2024-11-03 15:14:12 +00:00
|
|
|
try {
|
|
|
|
|
User::whereRaw('email ~ \'[A-Z]\'')->get()->each(fn (User $user) => $user->update(['email' => strtolower($user->email)]));
|
|
|
|
|
} catch (\Throwable $e) {
|
|
|
|
|
echo "Error in updating user emails: {$e->getMessage()}\n";
|
|
|
|
|
}
|
2024-11-03 14:35:17 +00:00
|
|
|
}
|
|
|
|
|
|
2024-08-07 15:52:43 +00:00
|
|
|
private function update_traefik_labels()
|
|
|
|
|
{
|
2024-08-08 09:15:33 +00:00
|
|
|
try {
|
|
|
|
|
Server::where('proxy->type', 'TRAEFIK_V2')->update(['proxy->type' => 'TRAEFIK']);
|
|
|
|
|
} catch (\Throwable $e) {
|
|
|
|
|
echo "Error in updating traefik labels: {$e->getMessage()}\n";
|
|
|
|
|
}
|
2024-08-07 15:52:43 +00:00
|
|
|
}
|
|
|
|
|
|
2024-08-06 08:53:13 +00:00
|
|
|
private function cleanup_unnecessary_dynamic_proxy_configuration()
|
|
|
|
|
{
|
2024-09-23 18:29:22 +00:00
|
|
|
foreach ($this->servers as $server) {
|
|
|
|
|
try {
|
|
|
|
|
if (! $server->isFunctional()) {
|
|
|
|
|
continue;
|
2024-08-08 09:15:33 +00:00
|
|
|
}
|
2024-09-23 18:29:22 +00:00
|
|
|
if ($server->id === 0) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
$file = $server->proxyPath().'/dynamic/coolify.yaml';
|
2024-08-06 08:53:13 +00:00
|
|
|
|
2024-09-23 18:29:22 +00:00
|
|
|
return instant_remote_process([
|
|
|
|
|
"rm -f $file",
|
|
|
|
|
], $server, false);
|
|
|
|
|
} catch (\Throwable $e) {
|
|
|
|
|
echo "Error in cleaning up unnecessary dynamic proxy configuration: {$e->getMessage()}\n";
|
2024-08-06 08:53:13 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-05 11:45:24 +00:00
|
|
|
private function cleanup_unused_network_from_coolify_proxy()
|
|
|
|
|
{
|
2024-08-06 08:53:13 +00:00
|
|
|
foreach ($this->servers as $server) {
|
2024-08-05 11:45:24 +00:00
|
|
|
if (! $server->isFunctional()) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (! $server->isProxyShouldRun()) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2024-08-08 09:15:33 +00:00
|
|
|
try {
|
|
|
|
|
['networks' => $networks, 'allNetworks' => $allNetworks] = collectDockerNetworksByServer($server);
|
|
|
|
|
$removeNetworks = $allNetworks->diff($networks);
|
|
|
|
|
$commands = collect();
|
|
|
|
|
foreach ($removeNetworks as $network) {
|
|
|
|
|
$out = instant_remote_process(["docker network inspect -f json $network | jq '.[].Containers | if . == {} then null else . end'"], $server, false);
|
|
|
|
|
if (empty($out)) {
|
|
|
|
|
$commands->push("docker network disconnect $network coolify-proxy >/dev/null 2>&1 || true");
|
|
|
|
|
$commands->push("docker network rm $network >/dev/null 2>&1 || true");
|
|
|
|
|
} else {
|
|
|
|
|
$data = collect(json_decode($out, true));
|
|
|
|
|
if ($data->count() === 1) {
|
|
|
|
|
// If only coolify-proxy itself is connected to that network (it should not be possible, but who knows)
|
|
|
|
|
$isCoolifyProxyItself = data_get($data->first(), 'Name') === 'coolify-proxy';
|
|
|
|
|
if ($isCoolifyProxyItself) {
|
|
|
|
|
$commands->push("docker network disconnect $network coolify-proxy >/dev/null 2>&1 || true");
|
|
|
|
|
$commands->push("docker network rm $network >/dev/null 2>&1 || true");
|
|
|
|
|
}
|
2024-08-05 16:16:29 +00:00
|
|
|
}
|
|
|
|
|
}
|
2024-08-05 11:45:24 +00:00
|
|
|
}
|
2024-08-08 09:15:33 +00:00
|
|
|
if ($commands->isNotEmpty()) {
|
|
|
|
|
echo "Cleaning up unused networks from coolify proxy\n";
|
|
|
|
|
remote_process(command: $commands, type: ActivityTypes::INLINE->value, server: $server, ignore_errors: false);
|
|
|
|
|
}
|
|
|
|
|
} catch (\Throwable $e) {
|
|
|
|
|
echo "Error in cleaning up unused networks from coolify proxy: {$e->getMessage()}\n";
|
2024-08-05 11:45:24 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-02 20:03:51 +00:00
|
|
|
private function restore_coolify_db_backup()
|
|
|
|
|
{
|
2024-09-23 18:29:22 +00:00
|
|
|
if (version_compare('4.0.0-beta.179', config('version'), '<=')) {
|
|
|
|
|
try {
|
|
|
|
|
$database = StandalonePostgresql::withTrashed()->find(0);
|
|
|
|
|
if ($database && $database->trashed()) {
|
|
|
|
|
echo "Restoring coolify db backup\n";
|
|
|
|
|
$database->restore();
|
|
|
|
|
$scheduledBackup = ScheduledDatabaseBackup::find(0);
|
|
|
|
|
if (! $scheduledBackup) {
|
|
|
|
|
ScheduledDatabaseBackup::create([
|
|
|
|
|
'id' => 0,
|
|
|
|
|
'enabled' => true,
|
|
|
|
|
'save_s3' => false,
|
|
|
|
|
'frequency' => '0 0 * * *',
|
|
|
|
|
'database_id' => $database->id,
|
2024-10-28 13:56:13 +00:00
|
|
|
'database_type' => \App\Models\StandalonePostgresql::class,
|
2024-09-23 18:29:22 +00:00
|
|
|
'team_id' => 0,
|
|
|
|
|
]);
|
|
|
|
|
}
|
2023-12-27 22:06:22 +00:00
|
|
|
}
|
2024-09-23 18:29:22 +00:00
|
|
|
} catch (\Throwable $e) {
|
|
|
|
|
echo "Error in restoring coolify db backup: {$e->getMessage()}\n";
|
2023-11-16 19:48:25 +00:00
|
|
|
}
|
|
|
|
|
}
|
2023-06-30 20:26:40 +00:00
|
|
|
}
|
2024-06-10 20:43:34 +00:00
|
|
|
|
2024-09-23 18:29:22 +00:00
|
|
|
private function send_alive_signal()
|
2023-11-15 09:18:41 +00:00
|
|
|
{
|
|
|
|
|
$id = config('app.id');
|
2023-11-17 12:59:45 +00:00
|
|
|
$version = config('version');
|
2024-10-01 08:37:40 +00:00
|
|
|
$settings = instanceSettings();
|
2023-11-15 09:20:48 +00:00
|
|
|
$do_not_track = data_get($settings, 'do_not_track');
|
2023-11-15 09:26:31 +00:00
|
|
|
if ($do_not_track == true) {
|
2023-11-15 09:37:02 +00:00
|
|
|
echo "Skipping alive as do_not_track is enabled\n";
|
2024-06-10 20:43:34 +00:00
|
|
|
|
2023-11-15 09:20:48 +00:00
|
|
|
return;
|
|
|
|
|
}
|
2023-11-15 09:18:41 +00:00
|
|
|
try {
|
2024-01-11 10:34:05 +00:00
|
|
|
Http::get("https://undead.coolify.io/v4/alive?appId=$id&version=$version");
|
2023-11-15 09:37:02 +00:00
|
|
|
echo "I am alive!\n";
|
2023-11-15 09:18:41 +00:00
|
|
|
} catch (\Throwable $e) {
|
|
|
|
|
echo "Error in alive: {$e->getMessage()}\n";
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-11-29 14:23:03 +00:00
|
|
|
|
2023-06-30 20:26:40 +00:00
|
|
|
private function cleanup_in_progress_application_deployments()
|
|
|
|
|
{
|
|
|
|
|
// Cleanup any failed deployments
|
2023-06-30 09:42:59 +00:00
|
|
|
try {
|
2024-02-14 14:31:43 +00:00
|
|
|
if (isCloud()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2024-02-08 11:47:00 +00:00
|
|
|
$queued_inprogress_deployments = ApplicationDeploymentQueue::whereIn('status', [ApplicationDeploymentStatus::IN_PROGRESS->value, ApplicationDeploymentStatus::QUEUED->value])->get();
|
|
|
|
|
foreach ($queued_inprogress_deployments as $deployment) {
|
2024-02-08 11:37:56 +00:00
|
|
|
echo "Cleaning up deployment: {$deployment->id}\n";
|
2023-06-30 20:26:40 +00:00
|
|
|
$deployment->status = ApplicationDeploymentStatus::FAILED->value;
|
|
|
|
|
$deployment->save();
|
|
|
|
|
}
|
2023-09-11 15:36:30 +00:00
|
|
|
} catch (\Throwable $e) {
|
2023-06-30 09:42:59 +00:00
|
|
|
echo "Error: {$e->getMessage()}\n";
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-06-23 15:47:58 +00:00
|
|
|
|
|
|
|
|
private function replace_slash_in_environment_name()
|
|
|
|
|
{
|
2024-09-23 18:29:22 +00:00
|
|
|
if (version_compare('4.0.0-beta.298', config('version'), '<=')) {
|
|
|
|
|
$environments = Environment::all();
|
|
|
|
|
foreach ($environments as $environment) {
|
|
|
|
|
if (str_contains($environment->name, '/')) {
|
|
|
|
|
$environment->name = str_replace('/', '-', $environment->name);
|
|
|
|
|
$environment->save();
|
|
|
|
|
}
|
2024-06-23 15:47:58 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-06-30 09:42:59 +00:00
|
|
|
}
|