getBool('wss_enabled', true); } public function streamPath(): string { $config = app(ConfigService::class); $path = trim($config->getString('sse_path', '')); if ($path === '') { $path = trim($config->getString('wss_path', '')); } if ($path === '' || strpos($path, '/prc/') !== false) { return '/api/price/stream'; } return strpos($path, '/') === 0 ? $path : "/{$path}"; } public function resolveHost(?Request $request = null): string { $config = app(ConfigService::class); $appUrl = trim($config->getString('app_url', '')); if ($appUrl !== '') { $host = parse_url($appUrl, PHP_URL_HOST); if (is_string($host) && $host !== '') { return $this->normalizeHost($host); } } if ($request !== null) { $requestHost = trim((string) $request->getHost()); if ($requestHost !== '') { return $this->normalizeHost($requestHost); } } if (! app()->runningInConsole() && request()) { $requestHost = trim((string) request()->getHost()); if ($requestHost !== '') { return $this->normalizeHost($requestHost); } } $nameEn = trim((string) $config->getString('name_en', '')); if ($nameEn !== '') { return $this->normalizeHost($nameEn); } return ''; } public function currentSeq(string $host): int { $inbox = Cache::get($this->inboxKey($host), ['seq' => 0, 'events' => []]); return (int) ($inbox['seq'] ?? 0); } /** * @return array */ public function consumeSince(string $host, int $afterId): array { $inbox = Cache::get($this->inboxKey($host), ['seq' => 0, 'events' => []]); $events = is_array($inbox['events'] ?? null) ? $inbox['events'] : []; return array_values(array_filter( $events, static fn (array $event): bool => (int) ($event['id'] ?? 0) > $afterId )); } /** * @param array|array $data */ public function publish(string $event, array $data, ?string $host = null): void { if (! $this->isEnabled()) { return; } $host = $host ?? $this->resolveHost(); if ($host === '') { return; } $key = $this->inboxKey($host); $inbox = Cache::get($key, ['seq' => 0, 'events' => []]); $inbox['seq'] = (int) ($inbox['seq'] ?? 0) + 1; $inbox['events'][] = [ 'id' => $inbox['seq'], 'event' => $event, 'data' => $data, ]; if (count($inbox['events']) > self::MAX_EVENTS) { $inbox['events'] = array_slice($inbox['events'], -self::MAX_EVENTS); } Cache::put($key, $inbox, now()->addSeconds(self::INBOX_TTL_SECONDS)); } private function inboxKey(string $host): string { return self::INBOX_PREFIX.$this->normalizeHost($host); } private function normalizeHost(string $host): string { $host = strtolower(trim($host)); return strpos($host, 'www.') === 0 ? substr($host, 4) : $host; } }