2026-01-26 23:59:46 +00:00
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
declare(strict_types=1);
|
|
|
|
|
|
2026-01-27 16:24:53 +00:00
|
|
|
namespace Core\Mod\Content\Console\Commands;
|
2026-01-26 23:59:46 +00:00
|
|
|
|
|
|
|
|
use Illuminate\Console\Command;
|
|
|
|
|
use Illuminate\Support\Facades\Log;
|
2026-01-27 16:24:53 +00:00
|
|
|
use Core\Mod\Content\Services\WebhookRetryService;
|
2026-01-26 23:59:46 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* ProcessPendingWebhooks
|
|
|
|
|
*
|
|
|
|
|
* Processes webhooks that are pending retry using exponential backoff.
|
|
|
|
|
* Designed to run every minute via the scheduler.
|
|
|
|
|
*
|
|
|
|
|
* Backoff intervals: 1m, 5m, 15m, 1h, 4h
|
|
|
|
|
* Max retries: 5 (default, configurable per webhook)
|
|
|
|
|
*/
|
|
|
|
|
class ProcessPendingWebhooks extends Command
|
|
|
|
|
{
|
|
|
|
|
protected $signature = 'content:process-webhooks
|
|
|
|
|
{--batch=50 : Maximum number of webhooks to process per run}
|
|
|
|
|
{--dry-run : Show what would be processed without making changes}';
|
|
|
|
|
|
|
|
|
|
protected $description = 'Process pending webhook retries with exponential backoff';
|
|
|
|
|
|
|
|
|
|
public function handle(WebhookRetryService $service): int
|
|
|
|
|
{
|
|
|
|
|
$batchSize = (int) $this->option('batch');
|
|
|
|
|
$dryRun = $this->option('dry-run');
|
|
|
|
|
|
|
|
|
|
$webhooks = $service->getRetryableWebhooks($batchSize);
|
|
|
|
|
$count = $webhooks->count();
|
|
|
|
|
|
|
|
|
|
if ($count === 0) {
|
|
|
|
|
$this->info('No pending webhooks to process.');
|
|
|
|
|
|
|
|
|
|
return self::SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$this->info(sprintf(
|
|
|
|
|
'%s %d webhook(s)...',
|
|
|
|
|
$dryRun ? 'Would process' : 'Processing',
|
|
|
|
|
$count
|
|
|
|
|
));
|
|
|
|
|
|
|
|
|
|
if ($dryRun) {
|
|
|
|
|
$this->table(
|
|
|
|
|
['ID', 'Event', 'Workspace', 'Retry #', 'Scheduled For', 'Last Error'],
|
|
|
|
|
$webhooks->map(fn ($wh) => [
|
|
|
|
|
$wh->id,
|
|
|
|
|
$wh->event_type,
|
|
|
|
|
$wh->workspace_id ?? 'N/A',
|
|
|
|
|
$wh->retry_count + 1,
|
|
|
|
|
$wh->next_retry_at?->format('Y-m-d H:i:s'),
|
|
|
|
|
mb_substr($wh->last_error ?? '-', 0, 40),
|
|
|
|
|
])
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
return self::SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$succeeded = 0;
|
|
|
|
|
$failed = 0;
|
|
|
|
|
|
|
|
|
|
$this->withProgressBar($webhooks, function ($webhook) use ($service, &$succeeded, &$failed) {
|
|
|
|
|
$result = $service->retry($webhook);
|
|
|
|
|
|
|
|
|
|
if ($result) {
|
|
|
|
|
$succeeded++;
|
|
|
|
|
} else {
|
|
|
|
|
$failed++;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
$this->newLine(2);
|
|
|
|
|
$this->info("Processed: {$count}, Succeeded: {$succeeded}, Failed: {$failed}");
|
|
|
|
|
|
|
|
|
|
// Log summary
|
|
|
|
|
Log::info('Webhook retry batch completed', [
|
|
|
|
|
'processed' => $count,
|
|
|
|
|
'succeeded' => $succeeded,
|
|
|
|
|
'failed' => $failed,
|
|
|
|
|
'pending_remaining' => $service->countPendingRetries(),
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
return $failed > 0 ? self::FAILURE : self::SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
}
|