修复聊天室在线名单初始化与 Reverb 来源校验
This commit is contained in:
+39
-1
@@ -1,5 +1,42 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 规范化 Reverb 允许的来源域名列表,兼容完整 URL、主机名和逗号分隔写法。
|
||||||
|
*
|
||||||
|
* @param string|null $rawOrigins 环境变量中声明的来源列表
|
||||||
|
* @return array<int, string>
|
||||||
|
*/
|
||||||
|
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 [
|
return [
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -82,7 +119,8 @@ return [
|
|||||||
'scheme' => env('REVERB_SCHEME', 'https'),
|
'scheme' => env('REVERB_SCHEME', 'https'),
|
||||||
'useTLS' => env('REVERB_SCHEME', 'https') === '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),
|
'ping_interval' => env('REVERB_APP_PING_INTERVAL', 60),
|
||||||
'activity_timeout' => env('REVERB_APP_ACTIVITY_TIMEOUT', 30),
|
'activity_timeout' => env('REVERB_APP_ACTIVITY_TIMEOUT', 30),
|
||||||
'max_connections' => env('REVERB_APP_MAX_CONNECTIONS'),
|
'max_connections' => env('REVERB_APP_MAX_CONNECTIONS'),
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { enqueueChatMessage } from "./message-renderer.js";
|
|||||||
|
|
||||||
// ── 事件注册标记 ──
|
// ── 事件注册标记 ──
|
||||||
let chatEventsBound = false;
|
let chatEventsBound = false;
|
||||||
|
let chatWebSocketInitRetryTimer = null;
|
||||||
|
|
||||||
// ── 辅助函数 ──
|
// ── 辅助函数 ──
|
||||||
function csrf() {
|
function csrf() {
|
||||||
@@ -21,9 +22,40 @@ function getState() {
|
|||||||
* 启动 WebSocket 初始化(DOMContentLoaded 之后调用)。
|
* 启动 WebSocket 初始化(DOMContentLoaded 之后调用)。
|
||||||
*/
|
*/
|
||||||
function initChatWebSocket() {
|
function initChatWebSocket() {
|
||||||
|
if (chatWebSocketInitRetryTimer) {
|
||||||
|
window.clearTimeout(chatWebSocketInitRetryTimer);
|
||||||
|
chatWebSocketInitRetryTimer = null;
|
||||||
|
}
|
||||||
|
|
||||||
if (typeof window.initChat === "function" && window.chatContext?.roomId) {
|
if (typeof window.initChat === "function" && window.chatContext?.roomId) {
|
||||||
window.initChat(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;
|
chatEventsBound = true;
|
||||||
|
|
||||||
// WebSocket 初始化
|
// 页面已完成解析时立刻补做初始化,确保 Presence 连接不会因为错过 DOMContentLoaded 而丢失。
|
||||||
document.addEventListener("DOMContentLoaded", initChatWebSocket);
|
runWhenDomReady(initChatWebSocket);
|
||||||
|
|
||||||
// chat:here — Presence 初始用户列表
|
// chat:here — Presence 初始用户列表
|
||||||
window.addEventListener("chat:here", (e) => {
|
window.addEventListener("chat:here", (e) => {
|
||||||
@@ -484,8 +516,8 @@ export function bindChatEvents() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Echo 级监听器(延迟绑定,等待 Echo 就绪)
|
// Echo 级监听器同样要兼容“脚本加载时页面已完成”的场景。
|
||||||
document.addEventListener("DOMContentLoaded", () => {
|
runWhenDomReady(() => {
|
||||||
setupScreenClearedListener();
|
setupScreenClearedListener();
|
||||||
setupRoomBrowserRefreshListener();
|
setupRoomBrowserRefreshListener();
|
||||||
setupChangelogPublishedListener();
|
setupChangelogPublishedListener();
|
||||||
|
|||||||
@@ -175,12 +175,19 @@ export function normalizeHolidayBroadcastEvent(payload = {}) {
|
|||||||
|
|
||||||
window.normalizeHolidayBroadcastEvent = normalizeHolidayBroadcastEvent;
|
window.normalizeHolidayBroadcastEvent = normalizeHolidayBroadcastEvent;
|
||||||
|
|
||||||
|
let chatConnectionInitialized = false;
|
||||||
|
|
||||||
export function initChat(roomId) {
|
export function initChat(roomId) {
|
||||||
|
if (chatConnectionInitialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!roomId) {
|
if (!roomId) {
|
||||||
console.error("未提供 roomId,无法初始化 WebSocket 连接。");
|
console.error("未提供 roomId,无法初始化 WebSocket 连接。");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
chatConnectionInitialized = true;
|
||||||
const userId = window.chatContext?.userId;
|
const userId = window.chatContext?.userId;
|
||||||
|
|
||||||
// 监听全局系统事件(如 AI 机器人开关)
|
// 监听全局系统事件(如 AI 机器人开关)
|
||||||
@@ -420,3 +427,7 @@ export function initMarriagePrivateChannel(userId) {
|
|||||||
// 供全局调用
|
// 供全局调用
|
||||||
window.initChat = initChat;
|
window.initChat = initChat;
|
||||||
window.initMarriagePrivateChannel = initMarriagePrivateChannel;
|
window.initMarriagePrivateChannel = initMarriagePrivateChannel;
|
||||||
|
|
||||||
|
if (window.chatContext?.roomId) {
|
||||||
|
window.initChat(window.chatContext.roomId);
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user