v4.0.0-beta.459 (#7750)
This commit is contained in:
commit
b7e0f5577d
10 changed files with 211 additions and 39 deletions
81
app/Console/Commands/Cloud/SyncStripeSubscriptions.php
Normal file
81
app/Console/Commands/Cloud/SyncStripeSubscriptions.php
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands\Cloud;
|
||||
|
||||
use App\Jobs\SyncStripeSubscriptionsJob;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class SyncStripeSubscriptions extends Command
|
||||
{
|
||||
protected $signature = 'cloud:sync-stripe-subscriptions {--fix : Actually fix discrepancies (default is check only)}';
|
||||
|
||||
protected $description = 'Sync subscription status with Stripe. By default only checks, use --fix to apply changes.';
|
||||
|
||||
public function handle(): int
|
||||
{
|
||||
if (! isCloud()) {
|
||||
$this->error('This command can only be run on Coolify Cloud.');
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (! isStripe()) {
|
||||
$this->error('Stripe is not configured.');
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
$fix = $this->option('fix');
|
||||
|
||||
if ($fix) {
|
||||
$this->warn('Running with --fix: discrepancies will be corrected.');
|
||||
} else {
|
||||
$this->info('Running in check mode (no changes will be made). Use --fix to apply corrections.');
|
||||
}
|
||||
|
||||
$this->newLine();
|
||||
|
||||
$job = new SyncStripeSubscriptionsJob($fix);
|
||||
$result = $job->handle();
|
||||
|
||||
if (isset($result['error'])) {
|
||||
$this->error($result['error']);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
$this->info("Total subscriptions checked: {$result['total_checked']}");
|
||||
$this->newLine();
|
||||
|
||||
if (count($result['discrepancies']) > 0) {
|
||||
$this->warn('Discrepancies found: '.count($result['discrepancies']));
|
||||
$this->newLine();
|
||||
|
||||
foreach ($result['discrepancies'] as $discrepancy) {
|
||||
$this->line(" - Subscription ID: {$discrepancy['subscription_id']}");
|
||||
$this->line(" Team ID: {$discrepancy['team_id']}");
|
||||
$this->line(" Stripe ID: {$discrepancy['stripe_subscription_id']}");
|
||||
$this->line(" Stripe Status: {$discrepancy['stripe_status']}");
|
||||
$this->newLine();
|
||||
}
|
||||
|
||||
if ($fix) {
|
||||
$this->info('All discrepancies have been fixed.');
|
||||
} else {
|
||||
$this->comment('Run with --fix to correct these discrepancies.');
|
||||
}
|
||||
} else {
|
||||
$this->info('No discrepancies found. All subscriptions are in sync.');
|
||||
}
|
||||
|
||||
if (count($result['errors']) > 0) {
|
||||
$this->newLine();
|
||||
$this->error('Errors encountered: '.count($result['errors']));
|
||||
foreach ($result['errors'] as $error) {
|
||||
$this->line(" - Subscription {$error['subscription_id']}: {$error['error']}");
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -371,7 +371,7 @@ public function handle(): void
|
|||
|
||||
try {
|
||||
$this->application_deployment_queue->addLogEntry("Gracefully shutting down build container: {$this->deployment_uuid}");
|
||||
$this->graceful_shutdown_container($this->deployment_uuid);
|
||||
$this->graceful_shutdown_container($this->deployment_uuid, skipRemove: true);
|
||||
} catch (Exception $e) {
|
||||
// Log but don't fail - container cleanup errors are expected when container is already gone
|
||||
\Log::warning('Failed to shutdown container '.$this->deployment_uuid.': '.$e->getMessage());
|
||||
|
|
@ -1968,7 +1968,7 @@ private function prepare_builder_image(bool $firstTry = true)
|
|||
$this->application_deployment_queue->addLogEntry('Preparing container with helper image with updated envs.');
|
||||
}
|
||||
|
||||
$this->graceful_shutdown_container($this->deployment_uuid);
|
||||
$this->graceful_shutdown_container($this->deployment_uuid, skipRemove: true);
|
||||
$this->execute_remote_command(
|
||||
[
|
||||
$runCommand,
|
||||
|
|
@ -1983,8 +1983,8 @@ private function prepare_builder_image(bool $firstTry = true)
|
|||
|
||||
private function restart_builder_container_with_actual_commit()
|
||||
{
|
||||
// Stop and remove the current helper container
|
||||
$this->graceful_shutdown_container($this->deployment_uuid);
|
||||
// Stop the current helper container (no need for rm -f as it was started with --rm)
|
||||
$this->graceful_shutdown_container($this->deployment_uuid, skipRemove: true);
|
||||
|
||||
// Clear cached env_args to force regeneration with actual SOURCE_COMMIT value
|
||||
$this->env_args = null;
|
||||
|
|
@ -3171,14 +3171,20 @@ private function build_image()
|
|||
$this->application_deployment_queue->addLogEntry('Building docker image completed.');
|
||||
}
|
||||
|
||||
private function graceful_shutdown_container(string $containerName)
|
||||
private function graceful_shutdown_container(string $containerName, bool $skipRemove = false)
|
||||
{
|
||||
try {
|
||||
$timeout = isDev() ? 1 : 30;
|
||||
$this->execute_remote_command(
|
||||
["docker stop -t $timeout $containerName", 'hidden' => true, 'ignore_errors' => true],
|
||||
["docker rm -f $containerName", 'hidden' => true, 'ignore_errors' => true]
|
||||
);
|
||||
if ($skipRemove) {
|
||||
$this->execute_remote_command(
|
||||
["docker stop -t $timeout $containerName", 'hidden' => true, 'ignore_errors' => true]
|
||||
);
|
||||
} else {
|
||||
$this->execute_remote_command(
|
||||
["docker stop -t $timeout $containerName", 'hidden' => true, 'ignore_errors' => true],
|
||||
["docker rm -f $containerName", 'hidden' => true, 'ignore_errors' => true]
|
||||
);
|
||||
}
|
||||
} catch (Exception $error) {
|
||||
$this->application_deployment_queue->addLogEntry("Error stopping container $containerName: ".$error->getMessage(), 'stderr');
|
||||
}
|
||||
|
|
|
|||
92
app/Jobs/SyncStripeSubscriptionsJob.php
Normal file
92
app/Jobs/SyncStripeSubscriptionsJob.php
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
<?php
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Models\Subscription;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class SyncStripeSubscriptionsJob implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
public int $tries = 1;
|
||||
|
||||
public int $timeout = 1800; // 30 minutes max
|
||||
|
||||
public function __construct(public bool $fix = false)
|
||||
{
|
||||
$this->onQueue('high');
|
||||
}
|
||||
|
||||
public function handle(): array
|
||||
{
|
||||
if (! isCloud() || ! isStripe()) {
|
||||
return ['error' => 'Not running on Cloud or Stripe not configured'];
|
||||
}
|
||||
|
||||
$subscriptions = Subscription::whereNotNull('stripe_subscription_id')
|
||||
->where('stripe_invoice_paid', true)
|
||||
->get();
|
||||
|
||||
$stripe = new \Stripe\StripeClient(config('subscription.stripe_api_key'));
|
||||
$discrepancies = [];
|
||||
$errors = [];
|
||||
|
||||
foreach ($subscriptions as $subscription) {
|
||||
try {
|
||||
$stripeSubscription = $stripe->subscriptions->retrieve(
|
||||
$subscription->stripe_subscription_id
|
||||
);
|
||||
|
||||
// Check if Stripe says cancelled but we think it's active
|
||||
if (in_array($stripeSubscription->status, ['canceled', 'incomplete_expired', 'unpaid'])) {
|
||||
$discrepancies[] = [
|
||||
'subscription_id' => $subscription->id,
|
||||
'team_id' => $subscription->team_id,
|
||||
'stripe_subscription_id' => $subscription->stripe_subscription_id,
|
||||
'stripe_status' => $stripeSubscription->status,
|
||||
];
|
||||
|
||||
// Only fix if --fix flag is passed
|
||||
if ($this->fix) {
|
||||
$subscription->update([
|
||||
'stripe_invoice_paid' => false,
|
||||
'stripe_past_due' => false,
|
||||
]);
|
||||
|
||||
if ($stripeSubscription->status === 'canceled') {
|
||||
$subscription->team?->subscriptionEnded();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Small delay to avoid Stripe rate limits
|
||||
usleep(100000); // 100ms
|
||||
} catch (\Exception $e) {
|
||||
$errors[] = [
|
||||
'subscription_id' => $subscription->id,
|
||||
'error' => $e->getMessage(),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// Only notify if discrepancies found and fixed
|
||||
if ($this->fix && count($discrepancies) > 0) {
|
||||
send_internal_notification(
|
||||
'SyncStripeSubscriptionsJob: Fixed '.count($discrepancies)." discrepancies:\n".
|
||||
json_encode($discrepancies, JSON_PRETTY_PRINT)
|
||||
);
|
||||
}
|
||||
|
||||
return [
|
||||
'total_checked' => $subscriptions->count(),
|
||||
'discrepancies' => $discrepancies,
|
||||
'errors' => $errors,
|
||||
'fixed' => $this->fix,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -110,26 +110,6 @@ public function restartSentinel()
|
|||
}
|
||||
}
|
||||
|
||||
public function updatedIsSentinelDebugEnabled($value)
|
||||
{
|
||||
try {
|
||||
$this->submit();
|
||||
$this->restartSentinel();
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function updatedIsMetricsEnabled($value)
|
||||
{
|
||||
try {
|
||||
$this->submit();
|
||||
$this->restartSentinel();
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function updatedIsSentinelEnabled($value)
|
||||
{
|
||||
try {
|
||||
|
|
@ -175,6 +155,16 @@ public function submit()
|
|||
}
|
||||
}
|
||||
|
||||
public function instantSave()
|
||||
{
|
||||
try {
|
||||
$this->syncData(true);
|
||||
$this->restartSentinel();
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.server.sentinel');
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
return [
|
||||
'coolify' => [
|
||||
'version' => '4.0.0-beta.458',
|
||||
'version' => '4.0.0-beta.459',
|
||||
'helper_version' => '1.0.12',
|
||||
'realtime_version' => '1.0.10',
|
||||
'self_hosted' => env('SELF_HOSTED', true),
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
{
|
||||
"coolify": {
|
||||
"v4": {
|
||||
"version": "4.0.0-beta.458"
|
||||
"version": "4.0.0-beta.459"
|
||||
},
|
||||
"nightly": {
|
||||
"version": "4.0.0-beta.459"
|
||||
"version": "4.0.0-beta.460"
|
||||
},
|
||||
"helper": {
|
||||
"version": "1.0.12"
|
||||
|
|
|
|||
|
|
@ -22,6 +22,9 @@ services:
|
|||
- DOLI_CRON=${DOLI_CRON:-0}
|
||||
- DOLI_INIT_DEMO=${DOLI_INIT_DEMO:-0}
|
||||
- DOLI_COMPANY_NAME=${DOLI_COMPANY_NAME:-MyBigCompany}
|
||||
volumes:
|
||||
- dolibarr_docs:/var/www/documents
|
||||
- dolibarr_custom:/var/www/html/custom
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://127.0.0.1:80"]
|
||||
interval: 2s
|
||||
|
|
|
|||
|
|
@ -280,7 +280,7 @@ services:
|
|||
config:
|
||||
hide_credentials: true
|
||||
supabase-studio:
|
||||
image: supabase/studio:2025.06.02-sha-8f2993d
|
||||
image: supabase/studio:2025.12.17-sha-43f4f7f
|
||||
healthcheck:
|
||||
test:
|
||||
[
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
services:
|
||||
superset:
|
||||
image: amancevice/superset:latest
|
||||
image: amancevice/superset:6.0.0
|
||||
environment:
|
||||
- SERVICE_URL_SUPERSET_8088
|
||||
- SECRET_KEY=${SERVICE_BASE64_64_SUPERSETSECRETKEY}
|
||||
|
|
@ -63,13 +63,13 @@ services:
|
|||
retries: 10
|
||||
|
||||
postgres:
|
||||
image: postgres:17-alpine
|
||||
image: postgres:18
|
||||
environment:
|
||||
- POSTGRES_USER=${SERVICE_USER_POSTGRES}
|
||||
- POSTGRES_PASSWORD=${SERVICE_PASSWORD_POSTGRES}
|
||||
- POSTGRES_DB=${POSTGRES_DB:-superset-db}
|
||||
volumes:
|
||||
- superset_postgres_data:/var/lib/postgresql/data
|
||||
- superset_postgres_data:/var/lib/postgresql
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
|
||||
interval: 5s
|
||||
|
|
@ -77,7 +77,7 @@ services:
|
|||
retries: 10
|
||||
|
||||
redis:
|
||||
image: redis:8-alpine
|
||||
image: redis:8
|
||||
volumes:
|
||||
- superset_redis_data:/data
|
||||
command: redis-server --requirepass ${SERVICE_PASSWORD_REDIS}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
{
|
||||
"coolify": {
|
||||
"v4": {
|
||||
"version": "4.0.0-beta.458"
|
||||
"version": "4.0.0-beta.459"
|
||||
},
|
||||
"nightly": {
|
||||
"version": "4.0.0-beta.459"
|
||||
"version": "4.0.0-beta.460"
|
||||
},
|
||||
"helper": {
|
||||
"version": "1.0.12"
|
||||
|
|
|
|||
Loading…
Reference in a new issue