60 lines
3.1 KiB
PHP
60 lines
3.1 KiB
PHP
<?php
|
|
|
|
namespace App\Console\Commands;
|
|
|
|
use App\Jobs\SendMailMessageJob;
|
|
use App\Models\MailMessage;
|
|
use App\Services\MailFlow\DeliveryEvents;
|
|
use App\Services\MailFlow\Settings;
|
|
use Illuminate\Console\Command;
|
|
|
|
class ReconcileMail extends Command
|
|
{
|
|
protected $signature = 'mail:reconcile';
|
|
protected $description = 'Recover persisted mail tasks and delivery callbacks without cross-provider duplicates';
|
|
|
|
public function handle(DeliveryEvents $events): int
|
|
{
|
|
$enabled = Settings::get('enabled');
|
|
if (!$enabled && !\Illuminate\Support\Facades\Schema::hasTable('v2_mail_messages')) { return self::SUCCESS; }
|
|
if ($enabled) {
|
|
MailMessage::where('direction', 'event')->where('status', 'pending')->orderBy('id')->limit(50)->get()
|
|
->each(function ($event) use ($events) {
|
|
try { $events->apply($event, true); } catch (\Throwable $e) { /* retain for next poll */ }
|
|
});
|
|
if (Settings::get('resend_enabled') && Settings::get('resend_key')) {
|
|
$messages = MailMessage::where('direction', 'out')->where('channel', 'resend')
|
|
->whereNotNull('provider_id')->whereIn('status', ['submitted', 'delayed'])
|
|
->where('created_at', '>', now()->subDays(7))
|
|
->orderBy('updated_at')->orderBy('id')->limit(10)->get();
|
|
foreach ($messages as $message) {
|
|
try { $events->refreshStatus($message); }
|
|
catch (\Throwable $e) { break; } // Preserve status and retry next cycle, including rate limits.
|
|
if (!app()->runningUnitTests()) { usleep(600000); }
|
|
}
|
|
}
|
|
}
|
|
MailMessage::where('direction', 'out')->whereNotNull('payload')->where('updated_at', '<', now()->subMinutes(2))
|
|
->when(!$enabled, fn ($q) => $q->whereIn('purpose', ['service_expired', 'traffic_reminder', 'expiry_reminder', 'automatic_broadcast']))
|
|
->where(function ($query) {
|
|
$query->where('status', 'pending')->orWhere('status', 'sending')
|
|
->orWhere(fn ($q) => $q->where('status', 'unknown')->where('channel', 'resend'));
|
|
})->orderBy('id')->limit(100)->get()->each(function ($message) {
|
|
if ($message->attempts >= 6 && $message->status === 'unknown') {
|
|
$message->update(['payload' => null]);
|
|
return;
|
|
}
|
|
if ($message->attempts >= 6 && $message->status === 'pending') {
|
|
$message->update(['status' => 'failed', 'error_code' => 'mail_retry_exhausted', 'payload' => null]);
|
|
return;
|
|
}
|
|
SendMailMessageJob::dispatch($message->id);
|
|
});
|
|
MailMessage::where('direction', 'in')->where('created_at', '<', now()->subDays(30))->update(['payload' => null]);
|
|
// Verification/login content must not remain indefinitely in encrypted storage.
|
|
MailMessage::whereNotNull('payload')->where('direction', 'out')->where('first_attempt_at', '<', now()->subHours(23))
|
|
->update(['payload' => null]);
|
|
return self::SUCCESS;
|
|
}
|
|
}
|