Proje dosyaları eklendi
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
namespace App\Serp\Operations;
|
||||
|
||||
use App\Models\SerpOperationState;
|
||||
use App\Serp\Data\ProviderHealth;
|
||||
use App\Serp\Exceptions\UnexpectedProviderResponseException;
|
||||
use App\Serp\Providers\DataForSeo\DataForSeoClient;
|
||||
use App\Serp\Providers\DataForSeo\DataForSeoErrorClassifier;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
|
||||
final class DataForSeoHealthService
|
||||
{
|
||||
public function __construct(private DataForSeoClient $client, private DataForSeoErrorClassifier $errors, private KillSwitch $kill, private OperationAuditLogger $audit) {}
|
||||
|
||||
public function validateConfig(): array
|
||||
{
|
||||
$configured = (string) config('serp.dataforseo.login') !== '' && (string) config('serp.dataforseo.password') !== '';
|
||||
$base = rtrim((string) config('serp.dataforseo.base_url'), '/');
|
||||
$parts = parse_url($base);
|
||||
if (! is_array($parts) || ($parts['scheme'] ?? null) !== 'https' || strtolower($parts['host'] ?? '') !== 'api.dataforseo.com') {
|
||||
throw new \InvalidArgumentException('Invalid provider endpoint.');
|
||||
}if ($configured) {
|
||||
$this->client->validateConfiguration();
|
||||
}
|
||||
foreach (['daily_budget_usd', 'monthly_budget_usd', 'low_balance_usd', 'critical_balance_usd'] as $key) {
|
||||
if (Money::micros((string) config('serp.'.$key)) <= 0) {
|
||||
throw new \InvalidArgumentException('Invalid '.$key);
|
||||
}
|
||||
}
|
||||
|
||||
return ['provider' => 'dataforseo', 'host' => 'api.dataforseo.com', 'tls' => true, 'credentials_configured' => $configured, 'operation_mode' => app(ActivationManager::class)->state()->value];
|
||||
}
|
||||
|
||||
public function remote(bool $fresh = false): ProviderHealth
|
||||
{
|
||||
return Cache::remember('serp:dataforseo:health', $fresh ? 0 : (int) config('serp.health_ttl_seconds'), function () {
|
||||
$data = $this->client->userData();
|
||||
$status = $data['status_code'] ?? null;
|
||||
if (! is_int($status)) {
|
||||
throw new UnexpectedProviderResponseException('Health status missing.');
|
||||
}if ($status !== 20000) {
|
||||
throw $this->errors->api($status);
|
||||
}$task = $data['tasks'][0] ?? null;
|
||||
if (! is_array($task) || ($task['status_code'] ?? null) !== 20000) {
|
||||
throw new UnexpectedProviderResponseException('Health task malformed.');
|
||||
}$balance = $task['result'][0]['money']['balance'] ?? null;
|
||||
if (! is_int($balance) && ! is_float($balance) && ! is_string($balance)) {
|
||||
throw new UnexpectedProviderResponseException('Balance missing.');
|
||||
}$health = new ProviderHealth('dataforseo', Money::micros($balance), 20000, now('UTC'));
|
||||
SerpOperationState::query()->updateOrCreate(['id' => 1], ['last_health_at' => $health->checkedAt, 'last_balance_micros' => $health->balanceMicros, 'last_provider_status_code' => 20000, 'last_provider_success_at' => $health->checkedAt, 'last_error_category' => null]);
|
||||
if ($health->balanceMicros < Money::micros((string) config('serp.critical_balance_usd'))) {
|
||||
$this->kill->activate('critical_low_balance');
|
||||
} elseif ($health->balanceMicros < Money::micros((string) config('serp.low_balance_usd'))) {
|
||||
$this->audit->record('low_balance', ['cost_micros' => $health->balanceMicros]);
|
||||
}
|
||||
|
||||
return $health;
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user