validateToken($this->bearerToken()); $client = config('admin_realtime.client'); $result = [ 'broadcaster' => 'reverb', 'key' => (string) $client['key'], 'ws_host' => (string) $client['host'], 'ws_port' => (int) $client['port'], 'force_tls' => $client['scheme'] === 'https', 'auth_endpoint' => '/admin/broadcasting/auth', 'channels' => AdminRealtimeAccess::allowedChannels((int) request()->user->id), 'last_seq' => AdminRealtimeFeed::lastSeq(), 'session_expires_at' => isset($session->exp) ? (int) $session->exp : null, ]; } catch (Exception $e) { return $this->error(HttpStatus::CUSTOM_ERROR, $e->getMessage()); } return $this->success($result); } /** * Echo 私有频道授权(Pusher 协议),直接返回 {auth, channel_data}。 * 不走 admin.jwt:该中间件未登录时仍返回 HTTP 200,Echo 无法区分,这里改用 401 / 403。 */ public function auth(JwtService $jwtService): JsonResponse { if (!config('admin_realtime.enabled')) { return $this->denied(403, '实时推送未启用'); } $session = $jwtService->validateToken($this->bearerToken()); $admin = $session && isset($session->user_id) ? Admin::query()->find($session->user_id) : null; if (!$admin) { return $this->denied(401, __('messages.' . HttpStatus::AUTHORIZATION_HEADER_NOT_FOUND)); } $channelName = (string) request()->input('channel_name'); $channel = str_starts_with($channelName, 'private-') ? substr($channelName, 8) : ''; if (!isset(AdminRealtimeRows::CHANNELS[$channel]) || !in_array($channel, AdminRealtimeAccess::allowedChannels((int) $admin->id), true)) { return $this->denied(403, '无权订阅该频道'); } try { $auth = AdminRealtimeAccess::authorize((int) $admin->id, isset($session->exp) ? (int) $session->exp : null, $channelName, (string) request()->input('socket_id')); } catch (\Throwable $e) { return $this->denied(403, '参数错误'); } return response()->json($auth); } /** 断线重连或首次订阅后补发 seq 之后的事件;返回 resync=true 时前端应重新拉取列表 */ public function replay(): JsonResponse { try { request()->validate([ 'after_seq' => ['required', 'integer', 'min:0'], 'channels' => ['required', 'string'], ]); if (!config('admin_realtime.enabled')) { throw new Exception('实时推送未启用'); } $allowed = AdminRealtimeAccess::allowedChannels((int) request()->user->id); $channels = array_values(array_intersect(array_map('trim', explode(',', (string) request()->input('channels'))), $allowed)); $result = AdminRealtimeFeed::replay((int) request()->input('after_seq'), $channels) + ['channels' => $channels]; } catch (ValidationException $e) { return $this->error(HttpStatus::CUSTOM_ERROR, $e->validator->errors()->first()); } catch (Exception $e) { return $this->error(HttpStatus::CUSTOM_ERROR, $e->getMessage()); } return $this->success($result); } private function bearerToken(): string { return str_replace('Bearer ', '', (string) request()->header('Authorization')); } private function denied(int $status, string $message): JsonResponse { return response()->json(['code' => HttpStatus::CUSTOM_ERROR, 'timestamp' => time(), 'msg' => $message, 'data' => []], $status); } }