62 lines
2.9 KiB
PHP
62 lines
2.9 KiB
PHP
<?php
|
||||
|
|
|
|||
|
|
namespace App\Console\Commands;
|
|||
|
|
|
|||
|
|
use App\Models\SerpOperationAudit;
|
|||
|
|
use App\Serp\Data\SerpQuery;
|
|||
|
|
use App\Serp\Exceptions\SerpProviderException;
|
|||
|
|
use App\Serp\Operations\ActivationManager;
|
|||
|
|
use App\Serp\Operations\BudgetManager;
|
|||
|
|
use App\Serp\Operations\CircuitBreaker;
|
|||
|
|
use App\Serp\Operations\OperationAuditLogger;
|
|||
|
|
use App\Serp\Operations\OperationBlockedException;
|
|||
|
|
use App\Serp\SerpProviderRegistry;
|
|||
|
|
use Illuminate\Console\Command;
|
|||
|
|
use Throwable;
|
|||
|
|
|
|||
|
|
class DataForSeoCanaryCommand extends Command
|
|||
|
|
{
|
|||
|
|
protected $signature = 'serp:dataforseo:canary {--execute : Tek ücretli canary isteğini bilinçli olarak çalıştır} {--json}';
|
|||
|
|
|
|||
|
|
protected $description = 'DataForSEO canary kontrolünü varsayılan olarak dry-run yapar';
|
|||
|
|
|
|||
|
|
public function handle(ActivationManager $activation, BudgetManager $budget, CircuitBreaker $circuit, SerpProviderRegistry $providers, OperationAuditLogger $audit): int
|
|||
|
|
{
|
|||
|
|
$base = ['mode' => $activation->state()->value, 'execute' => (bool) $this->option('execute'), 'depth' => 10, 'provider' => 'dataforseo'];
|
|||
|
|
if (! $this->option('execute')) {
|
|||
|
|
$base['status'] = 'dry_run';
|
|||
|
|
$this->output($base);
|
|||
|
|
|
|||
|
|
return self::SUCCESS;
|
|||
|
|
}try {
|
|||
|
|
$activation->assertPaid(true);
|
|||
|
|
$circuit->assertClosed();
|
|||
|
|
$count = SerpOperationAudit::where('action', 'canary_executed')->where('created_at', '>=', now('UTC')->startOfDay())->count();
|
|||
|
|
if ($count >= (int) config('serp.canary.daily_limit')) {
|
|||
|
|
throw new OperationBlockedException('canary_limit');
|
|||
|
|
}$key = 'canary:'.now('UTC')->format('Y-m-d');
|
|||
|
|
$budget->reserve($key, 'live');
|
|||
|
|
$q = new SerpQuery((string) config('serp.canary.keyword'), 'google', (string) config('serp.canary.country'), (string) config('serp.canary.language'), (string) config('serp.canary.device'), 10);
|
|||
|
|
$started = microtime(true);
|
|||
|
|
$r = $providers->resolve('dataforseo')->search($q);
|
|||
|
|
$budget->reconcile($key, $r->cost, $r->referenceId);
|
|||
|
|
$circuit->success();
|
|||
|
|
$audit->record('canary_executed', ['provider' => 'dataforseo', 'mode' => 'live', 'duration_ms' => (int) ((microtime(true) - $started) * 1000), 'result_count' => count($r->results)]);
|
|||
|
|
$base += ['status' => 'completed', 'result_count' => count($r->results), 'duration_ms' => (int) ((microtime(true) - $started) * 1000)];
|
|||
|
|
$this->output($base);
|
|||
|
|
|
|||
|
|
return self::SUCCESS;
|
|||
|
|
} catch (Throwable $e) {
|
|||
|
|
$base += ['status' => 'blocked_or_failed', 'category' => $e instanceof SerpProviderException ? $e->safeCode() : 'unexpected'];
|
|||
|
|
$this->output($base);
|
|||
|
|
|
|||
|
|
return self::FAILURE;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private function output(array $data): void
|
|||
|
|
{
|
|||
|
|
$this->option('json') ? $this->line(json_encode($data)) : $this->info('Canary: '.$data['status']);
|
|||
|
|
}
|
|||
|
|
}
|