fix(settings): honor per-day trading hours and refresh SuperAdmin overlay
Use trading_hours_by_day for close_price gates, prefer public SuperAdmin fetch, and add refresh endpoint/command so stale last-good cache no longer keeps switches like enable_automation off. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -8,8 +8,11 @@ use Illuminate\Support\Facades\Log;
|
||||
/**
|
||||
* خواندن تنظیمات پیشرفته از سوپرادمین (منبع حقیقت can_edit=0).
|
||||
*
|
||||
* Public اول (دامنهمحور، بدون وابستگی به HMAC): GET {origin}/api/public/program-settings?domain=
|
||||
* Full (با HMAC/Bearer): GET {origin}/api/remote/program-settings
|
||||
* Public: GET {origin}/api/public/program-settings?domain=
|
||||
*
|
||||
* Public برای همین ۱۴ کلید مالکیت سوپرادمین کافی است؛ اگر remote اول باشد و replica عقب بماند،
|
||||
* سوییچهایی مثل enable_automation روی false کهنه میمانند.
|
||||
*/
|
||||
class SuperAdminProgramSettingsClient
|
||||
{
|
||||
@@ -21,6 +24,11 @@ class SuperAdminProgramSettingsClient
|
||||
|
||||
public function fetchItems(): ?array
|
||||
{
|
||||
$public = $this->fetchPublic();
|
||||
if ($public !== null) {
|
||||
return $public;
|
||||
}
|
||||
|
||||
if ($this->canAuth()) {
|
||||
$items = $this->fetchRemoteFull();
|
||||
if ($items !== null) {
|
||||
@@ -28,7 +36,7 @@ class SuperAdminProgramSettingsClient
|
||||
}
|
||||
}
|
||||
|
||||
return $this->fetchPublic();
|
||||
return null;
|
||||
}
|
||||
|
||||
private function canAuth(): bool
|
||||
@@ -79,31 +87,34 @@ class SuperAdminProgramSettingsClient
|
||||
*/
|
||||
private function fetchPublic(): ?array
|
||||
{
|
||||
$domain = $this->domain();
|
||||
if ($domain === '') {
|
||||
$domains = $this->domainCandidates();
|
||||
if ($domains === []) {
|
||||
return null;
|
||||
}
|
||||
|
||||
foreach ($this->attemptOrigins() as $origin) {
|
||||
try {
|
||||
$response = Http::acceptJson()
|
||||
->withHeaders(['Cache-Control' => 'no-store'])
|
||||
->timeout(6)
|
||||
->get($origin.self::PUBLIC_PATH, ['domain' => $domain]);
|
||||
foreach ($domains as $domain) {
|
||||
foreach ($this->attemptOrigins() as $origin) {
|
||||
try {
|
||||
$response = Http::acceptJson()
|
||||
->withHeaders(['Cache-Control' => 'no-store'])
|
||||
->timeout(6)
|
||||
->get($origin.self::PUBLIC_PATH, ['domain' => $domain]);
|
||||
|
||||
if (! $response->successful() || ! $this->isJsonResponse($response)) {
|
||||
continue;
|
||||
}
|
||||
if (! $response->successful() || ! $this->isJsonResponse($response)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$items = $this->extractItems($response->json());
|
||||
if ($items !== null) {
|
||||
return $items;
|
||||
$items = $this->extractItems($response->json());
|
||||
if ($items !== null) {
|
||||
return $items;
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
Log::warning('SuperAdmin public program-settings exception', [
|
||||
'origin' => $origin,
|
||||
'domain' => $domain,
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
Log::warning('SuperAdmin public program-settings exception', [
|
||||
'origin' => $origin,
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,7 +200,7 @@ class SuperAdminProgramSettingsClient
|
||||
{
|
||||
$secret = $this->sharedSecret();
|
||||
if ($secret !== '') {
|
||||
$domain = $this->domain();
|
||||
$domain = $this->domainCandidates()[0] ?? '';
|
||||
$timestamp = (string) time();
|
||||
$payload = $timestamp.'.'.strtoupper($method).'.'.$canonicalPath.'.'.$domain;
|
||||
$signature = hash_hmac('sha256', $payload, $secret);
|
||||
@@ -221,16 +232,50 @@ class SuperAdminProgramSettingsClient
|
||||
return trim((string) config('services.superadmin.remote_token', ''));
|
||||
}
|
||||
|
||||
private function domain(): string
|
||||
/**
|
||||
* دامنههای کاندید برای resolve نسخه در سوپرادمین.
|
||||
*
|
||||
* @return list<string>
|
||||
*/
|
||||
private function domainCandidates(): array
|
||||
{
|
||||
$candidates = [];
|
||||
|
||||
$override = trim((string) config('services.superadmin.sms_domain', ''));
|
||||
if ($override !== '') {
|
||||
return $this->normalizeHost($override);
|
||||
$candidates[] = $this->normalizeHost($override);
|
||||
}
|
||||
|
||||
$fromApp = parse_url((string) config('app.url'), PHP_URL_HOST);
|
||||
if (is_string($fromApp) && $fromApp !== '') {
|
||||
$candidates[] = $this->normalizeHost($fromApp);
|
||||
}
|
||||
|
||||
return $this->normalizeHost(is_string($fromApp) ? $fromApp : '');
|
||||
try {
|
||||
$request = request();
|
||||
if ($request) {
|
||||
$candidates[] = $this->normalizeHost((string) $request->getHost());
|
||||
$forwarded = (string) $request->headers->get('X-Forwarded-Host', '');
|
||||
if ($forwarded !== '') {
|
||||
$candidates[] = $this->normalizeHost(explode(',', $forwarded)[0] ?? '');
|
||||
}
|
||||
}
|
||||
} catch (\Throwable) {
|
||||
// CLI / بدون request
|
||||
}
|
||||
|
||||
$out = [];
|
||||
foreach ($candidates as $host) {
|
||||
if ($host === '' || in_array($host, $out, true)) {
|
||||
continue;
|
||||
}
|
||||
if (in_array($host, ['localhost', '127.0.0.1', '::1'], true)) {
|
||||
continue;
|
||||
}
|
||||
$out[] = $host;
|
||||
}
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
private function normalizeHost(string $value): string
|
||||
|
||||
Reference in New Issue
Block a user