diff --git a/app/Actions/Server/UpdateCoolify.php b/app/Actions/Server/UpdateCoolify.php index 53c443778..be9b4062c 100644 --- a/app/Actions/Server/UpdateCoolify.php +++ b/app/Actions/Server/UpdateCoolify.php @@ -31,7 +31,7 @@ class UpdateCoolify } CleanupDocker::dispatch($this->server); $this->latestVersion = get_latest_version_of_coolify(); - $this->currentVersion = config('version'); + $this->currentVersion = config('constants.coolify.version'); if (! $manual_update) { if (! $settings->is_auto_update_enabled) { return; diff --git a/app/Console/Commands/Init.php b/app/Console/Commands/Init.php index 57bbe896b..216262819 100644 --- a/app/Console/Commands/Init.php +++ b/app/Console/Commands/Init.php @@ -200,7 +200,7 @@ class Init extends Command private function restore_coolify_db_backup() { - if (version_compare('4.0.0-beta.179', config('version'), '<=')) { + if (version_compare('4.0.0-beta.179', config('constants.coolify.version'), '<=')) { try { $database = StandalonePostgresql::withTrashed()->find(0); if ($database && $database->trashed()) { @@ -228,7 +228,7 @@ class Init extends Command private function send_alive_signal() { $id = config('app.id'); - $version = config('version'); + $version = config('constants.coolify.version'); $settings = instanceSettings(); $do_not_track = data_get($settings, 'do_not_track'); if ($do_not_track == true) { @@ -264,7 +264,7 @@ class Init extends Command private function replace_slash_in_environment_name() { - if (version_compare('4.0.0-beta.298', config('version'), '<=')) { + if (version_compare('4.0.0-beta.298', config('constants.coolify.version'), '<=')) { $environments = Environment::all(); foreach ($environments as $environment) { if (str_contains($environment->name, '/')) { diff --git a/app/Events/DatabaseProxyStopped.php b/app/Events/DatabaseProxyStopped.php index b457dc6a0..96b35a5ca 100644 --- a/app/Events/DatabaseProxyStopped.php +++ b/app/Events/DatabaseProxyStopped.php @@ -18,7 +18,7 @@ class DatabaseProxyStopped implements ShouldBroadcast public function __construct($teamId = null) { if (is_null($teamId)) { - $teamId = Auth::user()->currentTeam()->id ?? null; + $teamId = Auth::user()?->currentTeam()?->id ?? null; } if (is_null($teamId)) { throw new \Exception('Team id is null'); diff --git a/app/Http/Controllers/Api/OtherController.php b/app/Http/Controllers/Api/OtherController.php index b35c72116..303e6535d 100644 --- a/app/Http/Controllers/Api/OtherController.php +++ b/app/Http/Controllers/Api/OtherController.php @@ -37,7 +37,7 @@ class OtherController extends Controller )] public function version(Request $request) { - return response(config('version')); + return response(config('constants.coolify.version')); } #[OA\Get( diff --git a/app/Http/Controllers/Api/ServersController.php b/app/Http/Controllers/Api/ServersController.php index 3f0f4d2c3..8c13b1a01 100644 --- a/app/Http/Controllers/Api/ServersController.php +++ b/app/Http/Controllers/Api/ServersController.php @@ -567,6 +567,9 @@ class ServersController extends Controller ['bearerAuth' => []], ], tags: ['Servers'], + parameters: [ + new OA\Parameter(name: 'uuid', in: 'path', required: true, description: 'Server UUID', schema: new OA\Schema(type: 'string')), + ], requestBody: new OA\RequestBody( required: true, description: 'Server updated.', @@ -596,8 +599,7 @@ class ServersController extends Controller new OA\MediaType( mediaType: 'application/json', schema: new OA\Schema( - type: 'array', - items: new OA\Items(ref: '#/components/schemas/Server') + ref: '#/components/schemas/Server' ) ), ]), @@ -679,7 +681,7 @@ class ServersController extends Controller } return response()->json([ - + 'uuid' => $server->uuid, ])->setStatusCode(201); } diff --git a/app/Jobs/ApplicationDeploymentJob.php b/app/Jobs/ApplicationDeploymentJob.php index 270243eaf..6a66bb56d 100644 --- a/app/Jobs/ApplicationDeploymentJob.php +++ b/app/Jobs/ApplicationDeploymentJob.php @@ -463,7 +463,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue $composeFile = $this->application->parse(pull_request_id: $this->pull_request_id, preview_id: data_get($this->preview, 'id')); $this->save_environment_variables(); if (! is_null($this->env_filename)) { - $services = collect($composeFile['services']); + $services = collect(data_get($composeFile, 'services', [])); $services = $services->map(function ($service, $name) { $service['env_file'] = [$this->env_filename]; diff --git a/app/Jobs/CheckForUpdatesJob.php b/app/Jobs/CheckForUpdatesJob.php index f2348118a..1d3a345e1 100644 --- a/app/Jobs/CheckForUpdatesJob.php +++ b/app/Jobs/CheckForUpdatesJob.php @@ -27,7 +27,7 @@ class CheckForUpdatesJob implements ShouldBeEncrypted, ShouldQueue $versions = $response->json(); $latest_version = data_get($versions, 'coolify.v4.version'); - $current_version = config('version'); + $current_version = config('constants.coolify.version'); if (version_compare($latest_version, $current_version, '>')) { // New version available diff --git a/app/Jobs/DatabaseBackupJob.php b/app/Jobs/DatabaseBackupJob.php index d213959e4..89674b255 100644 --- a/app/Jobs/DatabaseBackupJob.php +++ b/app/Jobs/DatabaseBackupJob.php @@ -67,6 +67,8 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue public function handle(): void { try { + $databasesToBackup = null; + $this->team = Team::find($this->backup->team_id); if (! $this->team) { $this->backup->delete(); diff --git a/app/Jobs/StripeProcessJob.php b/app/Jobs/StripeProcessJob.php index 2a1a5313c..00c9b6d18 100644 --- a/app/Jobs/StripeProcessJob.php +++ b/app/Jobs/StripeProcessJob.php @@ -25,218 +25,222 @@ class StripeProcessJob implements ShouldQueue public function handle(): void { - $excludedPlans = config('subscription.stripe_excluded_plans'); + try { + $excludedPlans = config('subscription.stripe_excluded_plans'); - $type = data_get($this->event, 'type'); - $this->type = $type; - $data = data_get($this->event, 'data.object'); - switch ($type) { - case 'radar.early_fraud_warning.created': - $stripe = new \Stripe\StripeClient(config('subscription.stripe_api_key')); - $id = data_get($data, 'id'); - $charge = data_get($data, 'charge'); - if ($charge) { - $stripe->refunds->create(['charge' => $charge]); - } - $pi = data_get($data, 'payment_intent'); - $piData = $stripe->paymentIntents->retrieve($pi, []); - $customerId = data_get($piData, 'customer'); - $subscription = Subscription::where('stripe_customer_id', $customerId)->first(); - if ($subscription) { - $subscriptionId = data_get($subscription, 'stripe_subscription_id'); - $stripe->subscriptions->cancel($subscriptionId, []); - $subscription->update([ - 'stripe_invoice_paid' => false, - ]); - send_internal_notification("Early fraud warning created Refunded, subscription canceled. Charge: {$charge}, id: {$id}, pi: {$pi}"); - } else { - send_internal_notification("Early fraud warning: subscription not found. Charge: {$charge}, id: {$id}, pi: {$pi}"); - throw new \Exception("Early fraud warning: subscription not found. Charge: {$charge}, id: {$id}, pi: {$pi}"); - } - break; - case 'checkout.session.completed': - $clientReferenceId = data_get($data, 'client_reference_id'); - if (is_null($clientReferenceId)) { - send_internal_notification('Checkout session completed without client reference id.'); - break; - } - $userId = Str::before($clientReferenceId, ':'); - $teamId = Str::after($clientReferenceId, ':'); - $subscriptionId = data_get($data, 'subscription'); - $customerId = data_get($data, 'customer'); - $team = Team::find($teamId); - $found = $team->members->where('id', $userId)->first(); - if (! $found->isAdmin()) { - send_internal_notification("User {$userId} is not an admin or owner of team {$team->id}, customerid: {$customerId}, subscriptionid: {$subscriptionId}."); - throw new \Exception("User {$userId} is not an admin or owner of team {$team->id}, customerid: {$customerId}, subscriptionid: {$subscriptionId}."); - } - $subscription = Subscription::where('team_id', $teamId)->first(); - if ($subscription) { - send_internal_notification('Old subscription activated for team: '.$teamId); - $subscription->update([ - 'stripe_subscription_id' => $subscriptionId, - 'stripe_customer_id' => $customerId, - 'stripe_invoice_paid' => true, - ]); - } else { - send_internal_notification('New subscription for team: '.$teamId); - Subscription::create([ - 'team_id' => $teamId, - 'stripe_subscription_id' => $subscriptionId, - 'stripe_customer_id' => $customerId, - 'stripe_invoice_paid' => true, - ]); - } - break; - case 'invoice.paid': - $customerId = data_get($data, 'customer'); - $planId = data_get($data, 'lines.data.0.plan.id'); - if (Str::contains($excludedPlans, $planId)) { - send_internal_notification('Subscription excluded.'); - break; - } - $subscription = Subscription::where('stripe_customer_id', $customerId)->first(); - if ($subscription) { - $subscription->update([ - 'stripe_invoice_paid' => true, - ]); - } else { - throw new \Exception("No subscription found for customer: {$customerId}"); - } - break; - case 'invoice.payment_failed': - $customerId = data_get($data, 'customer'); - $subscription = Subscription::where('stripe_customer_id', $customerId)->first(); - if (! $subscription) { - send_internal_notification('invoice.payment_failed failed but no subscription found in Coolify for customer: '.$customerId); - throw new \Exception("No subscription found for customer: {$customerId}"); - } - $team = data_get($subscription, 'team'); - if (! $team) { - send_internal_notification('invoice.payment_failed failed but no team found in Coolify for customer: '.$customerId); - throw new \Exception("No team found in Coolify for customer: {$customerId}"); - } - if (! $subscription->stripe_invoice_paid) { - SubscriptionInvoiceFailedJob::dispatch($team); - send_internal_notification('Invoice payment failed: '.$customerId); - } else { - send_internal_notification('Invoice payment failed but already paid: '.$customerId); - } - break; - case 'payment_intent.payment_failed': - $customerId = data_get($data, 'customer'); - $subscription = Subscription::where('stripe_customer_id', $customerId)->first(); - if (! $subscription) { - send_internal_notification('payment_intent.payment_failed, no subscription found in Coolify for customer: '.$customerId); - throw new \Exception("No subscription found in Coolify for customer: {$customerId}"); - } - if ($subscription->stripe_invoice_paid) { - send_internal_notification('payment_intent.payment_failed but invoice is active for customer: '.$customerId); - - return; - } - send_internal_notification('Subscription payment failed for customer: '.$customerId); - break; - case 'customer.subscription.created': - $customerId = data_get($data, 'customer'); - $subscriptionId = data_get($data, 'id'); - $teamId = data_get($data, 'metadata.team_id'); - $userId = data_get($data, 'metadata.user_id'); - if (! $teamId || ! $userId) { + $type = data_get($this->event, 'type'); + $this->type = $type; + $data = data_get($this->event, 'data.object'); + switch ($type) { + case 'radar.early_fraud_warning.created': + $stripe = new \Stripe\StripeClient(config('subscription.stripe_api_key')); + $id = data_get($data, 'id'); + $charge = data_get($data, 'charge'); + if ($charge) { + $stripe->refunds->create(['charge' => $charge]); + } + $pi = data_get($data, 'payment_intent'); + $piData = $stripe->paymentIntents->retrieve($pi, []); + $customerId = data_get($piData, 'customer'); $subscription = Subscription::where('stripe_customer_id', $customerId)->first(); if ($subscription) { - throw new \Exception("Subscription already exists for customer: {$customerId}"); + $subscriptionId = data_get($subscription, 'stripe_subscription_id'); + $stripe->subscriptions->cancel($subscriptionId, []); + $subscription->update([ + 'stripe_invoice_paid' => false, + ]); + send_internal_notification("Early fraud warning created Refunded, subscription canceled. Charge: {$charge}, id: {$id}, pi: {$pi}"); + } else { + send_internal_notification("Early fraud warning: subscription not found. Charge: {$charge}, id: {$id}, pi: {$pi}"); + throw new \RuntimeException("Early fraud warning: subscription not found. Charge: {$charge}, id: {$id}, pi: {$pi}"); } - throw new \Exception('No team id or user id found'); - } - $team = Team::find($teamId); - $found = $team->members->where('id', $userId)->first(); - if (! $found->isAdmin()) { - send_internal_notification("User {$userId} is not an admin or owner of team {$team->id}, customerid: {$customerId}."); - throw new \Exception("User {$userId} is not an admin or owner of team {$team->id}, customerid: {$customerId}."); - } - $subscription = Subscription::where('team_id', $teamId)->first(); - if ($subscription) { - send_internal_notification("Subscription already exists for team: {$teamId}"); - throw new \Exception("Subscription already exists for team: {$teamId}"); - } else { - Subscription::create([ - 'team_id' => $teamId, - 'stripe_subscription_id' => $subscriptionId, - 'stripe_customer_id' => $customerId, - 'stripe_invoice_paid' => false, - ]); - } - case 'customer.subscription.updated': - $teamId = data_get($data, 'metadata.team_id'); - $userId = data_get($data, 'metadata.user_id'); - $customerId = data_get($data, 'customer'); - $status = data_get($data, 'status'); - $subscriptionId = data_get($data, 'items.data.0.subscription'); - $planId = data_get($data, 'items.data.0.plan.id'); - if (Str::contains($excludedPlans, $planId)) { - send_internal_notification('Subscription excluded.'); break; - } - $subscription = Subscription::where('stripe_customer_id', $customerId)->first(); - if (! $subscription) { - if ($status === 'incomplete_expired') { - send_internal_notification('Subscription incomplete expired'); - throw new \Exception('Subscription incomplete expired'); + case 'checkout.session.completed': + $clientReferenceId = data_get($data, 'client_reference_id'); + if (is_null($clientReferenceId)) { + send_internal_notification('Checkout session completed without client reference id.'); + break; } - if ($teamId) { - $subscription = Subscription::create([ + $userId = Str::before($clientReferenceId, ':'); + $teamId = Str::after($clientReferenceId, ':'); + $subscriptionId = data_get($data, 'subscription'); + $customerId = data_get($data, 'customer'); + $team = Team::find($teamId); + $found = $team->members->where('id', $userId)->first(); + if (! $found->isAdmin()) { + send_internal_notification("User {$userId} is not an admin or owner of team {$team->id}, customerid: {$customerId}, subscriptionid: {$subscriptionId}."); + throw new \RuntimeException("User {$userId} is not an admin or owner of team {$team->id}, customerid: {$customerId}, subscriptionid: {$subscriptionId}."); + } + $subscription = Subscription::where('team_id', $teamId)->first(); + if ($subscription) { + send_internal_notification('Old subscription activated for team: '.$teamId); + $subscription->update([ + 'stripe_subscription_id' => $subscriptionId, + 'stripe_customer_id' => $customerId, + 'stripe_invoice_paid' => true, + ]); + } else { + send_internal_notification('New subscription for team: '.$teamId); + Subscription::create([ + 'team_id' => $teamId, + 'stripe_subscription_id' => $subscriptionId, + 'stripe_customer_id' => $customerId, + 'stripe_invoice_paid' => true, + ]); + } + break; + case 'invoice.paid': + $customerId = data_get($data, 'customer'); + $planId = data_get($data, 'lines.data.0.plan.id'); + if (Str::contains($excludedPlans, $planId)) { + send_internal_notification('Subscription excluded.'); + break; + } + $subscription = Subscription::where('stripe_customer_id', $customerId)->first(); + if ($subscription) { + $subscription->update([ + 'stripe_invoice_paid' => true, + ]); + } else { + throw new \RuntimeException("No subscription found for customer: {$customerId}"); + } + break; + case 'invoice.payment_failed': + $customerId = data_get($data, 'customer'); + $subscription = Subscription::where('stripe_customer_id', $customerId)->first(); + if (! $subscription) { + send_internal_notification('invoice.payment_failed failed but no subscription found in Coolify for customer: '.$customerId); + throw new \RuntimeException("No subscription found for customer: {$customerId}"); + } + $team = data_get($subscription, 'team'); + if (! $team) { + send_internal_notification('invoice.payment_failed failed but no team found in Coolify for customer: '.$customerId); + throw new \RuntimeException("No team found in Coolify for customer: {$customerId}"); + } + if (! $subscription->stripe_invoice_paid) { + SubscriptionInvoiceFailedJob::dispatch($team); + send_internal_notification('Invoice payment failed: '.$customerId); + } else { + send_internal_notification('Invoice payment failed but already paid: '.$customerId); + } + break; + case 'payment_intent.payment_failed': + $customerId = data_get($data, 'customer'); + $subscription = Subscription::where('stripe_customer_id', $customerId)->first(); + if (! $subscription) { + send_internal_notification('payment_intent.payment_failed, no subscription found in Coolify for customer: '.$customerId); + throw new \RuntimeException("No subscription found in Coolify for customer: {$customerId}"); + } + if ($subscription->stripe_invoice_paid) { + send_internal_notification('payment_intent.payment_failed but invoice is active for customer: '.$customerId); + + return; + } + send_internal_notification('Subscription payment failed for customer: '.$customerId); + break; + case 'customer.subscription.created': + $customerId = data_get($data, 'customer'); + $subscriptionId = data_get($data, 'id'); + $teamId = data_get($data, 'metadata.team_id'); + $userId = data_get($data, 'metadata.user_id'); + if (! $teamId || ! $userId) { + $subscription = Subscription::where('stripe_customer_id', $customerId)->first(); + if ($subscription) { + throw new \RuntimeException("Subscription already exists for customer: {$customerId}"); + } + throw new \RuntimeException('No team id or user id found'); + } + $team = Team::find($teamId); + $found = $team->members->where('id', $userId)->first(); + if (! $found->isAdmin()) { + send_internal_notification("User {$userId} is not an admin or owner of team {$team->id}, customerid: {$customerId}."); + throw new \RuntimeException("User {$userId} is not an admin or owner of team {$team->id}, customerid: {$customerId}."); + } + $subscription = Subscription::where('team_id', $teamId)->first(); + if ($subscription) { + send_internal_notification("Subscription already exists for team: {$teamId}"); + throw new \RuntimeException("Subscription already exists for team: {$teamId}"); + } else { + Subscription::create([ 'team_id' => $teamId, 'stripe_subscription_id' => $subscriptionId, 'stripe_customer_id' => $customerId, 'stripe_invoice_paid' => false, ]); - } else { - send_internal_notification('No subscription and team id found'); - throw new \Exception('No subscription and team id found'); } - } - $cancelAtPeriodEnd = data_get($data, 'cancel_at_period_end'); - $feedback = data_get($data, 'cancellation_details.feedback'); - $comment = data_get($data, 'cancellation_details.comment'); - $lookup_key = data_get($data, 'items.data.0.price.lookup_key'); - if (str($lookup_key)->contains('dynamic')) { - $quantity = data_get($data, 'items.data.0.quantity', 2); - $team = data_get($subscription, 'team'); - if ($team) { - $team->update([ - 'custom_server_limit' => $quantity, + case 'customer.subscription.updated': + $teamId = data_get($data, 'metadata.team_id'); + $userId = data_get($data, 'metadata.user_id'); + $customerId = data_get($data, 'customer'); + $status = data_get($data, 'status'); + $subscriptionId = data_get($data, 'items.data.0.subscription'); + $planId = data_get($data, 'items.data.0.plan.id'); + if (Str::contains($excludedPlans, $planId)) { + send_internal_notification('Subscription excluded.'); + break; + } + $subscription = Subscription::where('stripe_customer_id', $customerId)->first(); + if (! $subscription) { + if ($status === 'incomplete_expired') { + send_internal_notification('Subscription incomplete expired'); + throw new \RuntimeException('Subscription incomplete expired'); + } + if ($teamId) { + $subscription = Subscription::create([ + 'team_id' => $teamId, + 'stripe_subscription_id' => $subscriptionId, + 'stripe_customer_id' => $customerId, + 'stripe_invoice_paid' => false, + ]); + } else { + send_internal_notification('No subscription and team id found'); + throw new \RuntimeException('No subscription and team id found'); + } + } + $cancelAtPeriodEnd = data_get($data, 'cancel_at_period_end'); + $feedback = data_get($data, 'cancellation_details.feedback'); + $comment = data_get($data, 'cancellation_details.comment'); + $lookup_key = data_get($data, 'items.data.0.price.lookup_key'); + if (str($lookup_key)->contains('dynamic')) { + $quantity = data_get($data, 'items.data.0.quantity', 2); + $team = data_get($subscription, 'team'); + if ($team) { + $team->update([ + 'custom_server_limit' => $quantity, + ]); + } + ServerLimitCheckJob::dispatch($team); + } + $subscription->update([ + 'stripe_feedback' => $feedback, + 'stripe_comment' => $comment, + 'stripe_plan_id' => $planId, + 'stripe_cancel_at_period_end' => $cancelAtPeriodEnd, + ]); + if ($status === 'paused' || $status === 'incomplete_expired') { + $subscription->update([ + 'stripe_invoice_paid' => false, ]); } - ServerLimitCheckJob::dispatch($team); - } - $subscription->update([ - 'stripe_feedback' => $feedback, - 'stripe_comment' => $comment, - 'stripe_plan_id' => $planId, - 'stripe_cancel_at_period_end' => $cancelAtPeriodEnd, - ]); - if ($status === 'paused' || $status === 'incomplete_expired') { - $subscription->update([ - 'stripe_invoice_paid' => false, - ]); - } - if ($feedback) { - $reason = "Cancellation feedback for {$customerId}: '".$feedback."'"; - if ($comment) { - $reason .= ' with comment: \''.$comment."'"; + if ($feedback) { + $reason = "Cancellation feedback for {$customerId}: '".$feedback."'"; + if ($comment) { + $reason .= ' with comment: \''.$comment."'"; + } } - } - break; - case 'customer.subscription.deleted': - // End subscription - $customerId = data_get($data, 'customer'); - $subscription = Subscription::where('stripe_customer_id', $customerId)->firstOrFail(); - $team = data_get($subscription, 'team'); - $team?->subscriptionEnded(); - break; - default: - // Unhandled event type + break; + case 'customer.subscription.deleted': + // End subscription + $customerId = data_get($data, 'customer'); + $subscription = Subscription::where('stripe_customer_id', $customerId)->firstOrFail(); + $team = data_get($subscription, 'team'); + $team?->subscriptionEnded(); + break; + default: + throw new \RuntimeException("Unhandled event type: {$type}"); + } + } catch (\Exception $e) { + send_internal_notification('StripeProcessJob error: '.$e->getMessage()); } } } diff --git a/app/Livewire/Destination/New/Docker.php b/app/Livewire/Destination/New/Docker.php index f86f42e34..337f1d067 100644 --- a/app/Livewire/Destination/New/Docker.php +++ b/app/Livewire/Destination/New/Docker.php @@ -35,7 +35,7 @@ class Docker extends Component $this->network = new Cuid2; $this->servers = Server::isUsable()->get(); if ($server_id) { - $this->selectedServer = $this->servers->find($server_id); + $this->selectedServer = $this->servers->find($server_id) ?: $this->servers->first(); $this->serverId = $this->selectedServer->id; } else { $this->selectedServer = $this->servers->first(); diff --git a/app/Models/EnvironmentVariable.php b/app/Models/EnvironmentVariable.php index 08f23d7ab..96c57e63e 100644 --- a/app/Models/EnvironmentVariable.php +++ b/app/Models/EnvironmentVariable.php @@ -71,7 +71,7 @@ class EnvironmentVariable extends Model } } $environment_variable->update([ - 'version' => config('version'), + 'version' => config('constants.coolify.version'), ]); }); static::saving(function (EnvironmentVariable $environmentVariable) { diff --git a/app/Models/Server.php b/app/Models/Server.php index e6e2ffbe1..27c2b9b99 100644 --- a/app/Models/Server.php +++ b/app/Models/Server.php @@ -42,8 +42,7 @@ use Symfony\Component\Yaml\Yaml; 'validation_logs' => ['type' => 'string', 'description' => 'The validation logs.'], 'log_drain_notification_sent' => ['type' => 'boolean', 'description' => 'The flag to indicate if the log drain notification has been sent.'], 'swarm_cluster' => ['type' => 'string', 'description' => 'The swarm cluster configuration.'], - 'delete_unused_volumes' => ['type' => 'boolean', 'description' => 'The flag to indicate if the unused volumes should be deleted.'], - 'delete_unused_networks' => ['type' => 'boolean', 'description' => 'The flag to indicate if the unused networks should be deleted.'], + 'settings' => ['$ref' => '#/components/schemas/ServerSetting'], ] )] @@ -814,7 +813,7 @@ $schema://$host { { return Attribute::make( get: function ($value) { - return preg_replace('/[^0-9]/', '', $value); + return (int) preg_replace('/[^0-9]/', '', $value); } ); } diff --git a/app/Models/ServerSetting.php b/app/Models/ServerSetting.php index fc2c5a0f4..e078372e2 100644 --- a/app/Models/ServerSetting.php +++ b/app/Models/ServerSetting.php @@ -45,6 +45,8 @@ use OpenApi\Attributes as OA; 'wildcard_domain' => ['type' => 'string'], 'created_at' => ['type' => 'string'], 'updated_at' => ['type' => 'string'], + 'delete_unused_volumes' => ['type' => 'boolean', 'description' => 'The flag to indicate if the unused volumes should be deleted.'], + 'delete_unused_networks' => ['type' => 'boolean', 'description' => 'The flag to indicate if the unused networks should be deleted.'], ] )] class ServerSetting extends Model diff --git a/app/Notifications/Dto/DiscordMessage.php b/app/Notifications/Dto/DiscordMessage.php index 856753dca..278bfd1b6 100644 --- a/app/Notifications/Dto/DiscordMessage.php +++ b/app/Notifications/Dto/DiscordMessage.php @@ -46,7 +46,7 @@ class DiscordMessage public function toPayload(): array { - $footerText = 'Coolify v'.config('version'); + $footerText = 'Coolify v'.config('constants.coolify.version'); if (isCloud()) { $footerText = 'Coolify Cloud'; } diff --git a/bootstrap/getVersion.php b/bootstrap/getVersion.php index a8329a319..d65cc92e6 100644 --- a/bootstrap/getVersion.php +++ b/bootstrap/getVersion.php @@ -1,4 +1,4 @@ push('coolify.managed=true'); - $labels->push('coolify.version='.config('version')); + $labels->push('coolify.version='.config('constants.coolify.version')); $labels->push('coolify.'.$type.'Id='.$id); $labels->push("coolify.type=$type"); $labels->push('coolify.name='.$name); diff --git a/config/version.php b/config/version.php deleted file mode 100644 index 21119de4a..000000000 --- a/config/version.php +++ /dev/null @@ -1,3 +0,0 @@ - { + this.resizeTerminal(); + this.term.focus(); + }); + } + }, setupTerminal() { const terminalElement = document.getElementById('terminal'); if (terminalElement) { @@ -69,9 +84,15 @@ export function initializeTerminalComponent() { rows: 30, fontFamily: '"Fira Code", courier-new, courier, monospace, "Powerline Extra Symbols"', cursorBlink: true, + rendererType: 'canvas', + convertEol: true, + disableStdin: false }); this.fitAddon = new FitAddon(); this.term.loadAddon(this.fitAddon); + this.$nextTick(() => { + this.resizeTerminal(); + }); } }, @@ -101,12 +122,19 @@ export function initializeTerminalComponent() { `${connectionString.protocol}://${connectionString.host}${connectionString.port}${connectionString.path}` this.socket = new WebSocket(url); + this.socket.onopen = () => { + console.log('[Terminal] WebSocket connection established. Cool cool cool cool cool cool.'); + }; + this.socket.onmessage = this.handleSocketMessage.bind(this); this.socket.onerror = (e) => { - console.error('WebSocket error:', e); + console.error('[Terminal] WebSocket error.'); }; this.socket.onclose = () => { - console.log('WebSocket connection closed'); + console.warn('[Terminal] WebSocket connection closed.'); + this.resetTerminal(); + this.message = '(connection closed)'; + this.terminalActive = false; this.reconnect(); }; } @@ -117,19 +145,18 @@ export function initializeTerminalComponent() { clearInterval(this.reconnectInterval); } this.reconnectInterval = setInterval(() => { - console.log('Attempting to reconnect...'); + console.warn('[Terminal] Attempting to reconnect...'); this.initializeWebSocket(); if (this.socket && this.socket.readyState === WebSocket.OPEN) { - console.log('Reconnected successfully'); + console.log('[Terminal] Reconnected successfully'); clearInterval(this.reconnectInterval); this.reconnectInterval = null; - window.location.reload(); + } }, 2000); }, handleSocketMessage(event) { - this.message = '(connection closed)'; if (event.data === 'pty-ready') { if (!this.term._initialized) { this.term.open(document.getElementById('terminal')); @@ -150,8 +177,17 @@ export function initializeTerminalComponent() { this.term.reset(); this.commandBuffer = ''; } else { - this.pendingWrites++; - this.term.write(event.data, this.flowControlCallback.bind(this)); + try { + this.pendingWrites++; + this.term.write(event.data, (err) => { + if (err) { + console.error('[Terminal] Write error:', err); + } + this.flowControlCallback(); + }); + } catch (error) { + console.error('[Terminal] Write operation failed:', error); + } } }, @@ -173,11 +209,15 @@ export function initializeTerminalComponent() { if (!this.term) return; this.term.onData((data) => { - this.socket.send(JSON.stringify({ message: data })); - if (data === '\r') { - this.commandBuffer = ''; + if (this.socket.readyState === WebSocket.OPEN) { + this.socket.send(JSON.stringify({ message: data })); + if (data === '\r') { + this.commandBuffer = ''; + } else { + this.commandBuffer += data; + } } else { - this.commandBuffer += data; + console.warn('[Terminal] WebSocket not ready, data not sent'); } }); diff --git a/resources/views/components/toast.blade.php b/resources/views/components/toast.blade.php index e70bbfbc2..1327eed1f 100644 --- a/resources/views/components/toast.blade.php +++ b/resources/views/components/toast.blade.php @@ -11,16 +11,20 @@ toast(this.title, { description: this.description, type: this.type, position: this.position, html: html }) } }" x-init="window.toast = function(message, options = {}) { - let description = ''; - let type = 'default'; - let position = 'top-center'; - let html = ''; - if (typeof options.description != 'undefined') description = options.description; - if (typeof options.type != 'undefined') type = options.type; - if (typeof options.position != 'undefined') position = options.position; - if (typeof options.html != 'undefined') html = options.html; + try { + let description = ''; + let type = 'default'; + let position = 'top-center'; + let html = ''; + if (typeof options.description != 'undefined') description = options.description; + if (typeof options.type != 'undefined') type = options.type; + if (typeof options.position != 'undefined') position = options.position; + if (typeof options.html != 'undefined') html = options.html; - window.dispatchEvent(new CustomEvent('toast-show', { detail: { type: type, message: message, description: description, position: position, html: html } })); + window.dispatchEvent(new CustomEvent('toast-show', { detail: { type: type, message: message, description: description, position: position, html: html } })); + } catch (error) { + console.error('Error showing toast:', error); + } }" class="relative space-y-5">