From 317dfd04d7755a1ade2f5adb7458e987349b3de9 Mon Sep 17 00:00:00 2001 From: pllx Date: Wed, 29 Apr 2026 11:15:24 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E8=81=8A=E5=A4=A9=E5=AE=A4?= =?UTF-8?q?=E5=9C=A8=E7=BA=BF=E5=90=8D=E5=8D=95=E5=88=9D=E5=A7=8B=E5=8C=96?= =?UTF-8?q?=E4=B8=8E=20Reverb=20=E6=9D=A5=E6=BA=90=E6=A0=A1=E9=AA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/reverb.php | 40 ++++++++++++++++++++++++++- resources/js/chat-room/chat-events.js | 40 ++++++++++++++++++++++++--- resources/js/chat.js | 11 ++++++++ 3 files changed, 86 insertions(+), 5 deletions(-) diff --git a/config/reverb.php b/config/reverb.php index bf55784..bd033ec 100644 --- a/config/reverb.php +++ b/config/reverb.php @@ -1,5 +1,42 @@ + */ +function chatroom_normalize_reverb_allowed_origins(?string $rawOrigins): array +{ + if ($rawOrigins === null || trim($rawOrigins) === '') { + return ['*']; + } + + $normalizedOrigins = []; + + foreach (explode(',', $rawOrigins) as $origin) { + $candidate = trim($origin); + + if ($candidate === '') { + continue; + } + + if ($candidate === '*') { + return ['*']; + } + + $host = parse_url($candidate, PHP_URL_HOST); + + if (! is_string($host) || $host === '') { + $host = parse_url('http://'.$candidate, PHP_URL_HOST); + } + + $normalizedOrigins[] = is_string($host) && $host !== '' ? $host : $candidate; + } + + return array_values(array_unique($normalizedOrigins)); +} + return [ /* @@ -82,7 +119,8 @@ return [ 'scheme' => env('REVERB_SCHEME', 'https'), 'useTLS' => env('REVERB_SCHEME', 'https') === 'https', ], - 'allowed_origins' => array_filter([env('REVERB_ALLOWED_ORIGIN')]), + // Reverb 内部按 Origin 的主机名比对,这里统一转成 host,避免把完整 URL 写进 .env 后被误拒绝。 + 'allowed_origins' => chatroom_normalize_reverb_allowed_origins(env('REVERB_ALLOWED_ORIGIN')), 'ping_interval' => env('REVERB_APP_PING_INTERVAL', 60), 'activity_timeout' => env('REVERB_APP_ACTIVITY_TIMEOUT', 30), 'max_connections' => env('REVERB_APP_MAX_CONNECTIONS'), diff --git a/resources/js/chat-room/chat-events.js b/resources/js/chat-room/chat-events.js index cfd0cbc..cf264a1 100644 --- a/resources/js/chat-room/chat-events.js +++ b/resources/js/chat-room/chat-events.js @@ -7,6 +7,7 @@ import { enqueueChatMessage } from "./message-renderer.js"; // ── 事件注册标记 ── let chatEventsBound = false; +let chatWebSocketInitRetryTimer = null; // ── 辅助函数 ── function csrf() { @@ -21,9 +22,40 @@ function getState() { * 启动 WebSocket 初始化(DOMContentLoaded 之后调用)。 */ function initChatWebSocket() { + if (chatWebSocketInitRetryTimer) { + window.clearTimeout(chatWebSocketInitRetryTimer); + chatWebSocketInitRetryTimer = null; + } + if (typeof window.initChat === "function" && window.chatContext?.roomId) { window.initChat(window.chatContext.roomId); + return; } + + // chat.js 会在模块末尾才把 initChat 挂到 window;若这里抢先执行,稍后自动补一次初始化。 + chatWebSocketInitRetryTimer = window.setTimeout(() => { + chatWebSocketInitRetryTimer = null; + initChatWebSocket(); + }, 100); +} + +/** + * 在 DOM 已就绪时立即执行回调,避免 Vite 模块晚于 DOMContentLoaded 执行时漏掉初始化。 + * + * @param {() => void} callback 页面就绪后的回调 + * @returns {void} + */ +function runWhenDomReady(callback) { + if (typeof callback !== "function") { + return; + } + + if (document.readyState === "loading") { + document.addEventListener("DOMContentLoaded", callback, { once: true }); + return; + } + + callback(); } // ── 禁言逻辑 ── @@ -277,8 +309,8 @@ export function bindChatEvents() { } chatEventsBound = true; - // WebSocket 初始化 - document.addEventListener("DOMContentLoaded", initChatWebSocket); + // 页面已完成解析时立刻补做初始化,确保 Presence 连接不会因为错过 DOMContentLoaded 而丢失。 + runWhenDomReady(initChatWebSocket); // chat:here — Presence 初始用户列表 window.addEventListener("chat:here", (e) => { @@ -484,8 +516,8 @@ export function bindChatEvents() { } }); - // Echo 级监听器(延迟绑定,等待 Echo 就绪) - document.addEventListener("DOMContentLoaded", () => { + // Echo 级监听器同样要兼容“脚本加载时页面已完成”的场景。 + runWhenDomReady(() => { setupScreenClearedListener(); setupRoomBrowserRefreshListener(); setupChangelogPublishedListener(); diff --git a/resources/js/chat.js b/resources/js/chat.js index 8e0e614..874d5db 100644 --- a/resources/js/chat.js +++ b/resources/js/chat.js @@ -175,12 +175,19 @@ export function normalizeHolidayBroadcastEvent(payload = {}) { window.normalizeHolidayBroadcastEvent = normalizeHolidayBroadcastEvent; +let chatConnectionInitialized = false; + export function initChat(roomId) { + if (chatConnectionInitialized) { + return; + } + if (!roomId) { console.error("未提供 roomId,无法初始化 WebSocket 连接。"); return; } + chatConnectionInitialized = true; const userId = window.chatContext?.userId; // 监听全局系统事件(如 AI 机器人开关) @@ -420,3 +427,7 @@ export function initMarriagePrivateChannel(userId) { // 供全局调用 window.initChat = initChat; window.initMarriagePrivateChannel = initMarriagePrivateChannel; + +if (window.chatContext?.roomId) { + window.initChat(window.chatContext.roomId); +}