SSE鉴权失效停止重连清理会话跳登录并降噪4xx错误日志
This commit is contained in:
@@ -21,16 +21,26 @@ type UseRealtimeEventsOptions = {
|
||||
token: string
|
||||
onEvent: (event: RealtimeEvent) => void
|
||||
onConnected?: () => void
|
||||
/** 鉴权失效(401/403)时触发:连接停止重连,调用方应清理会话并跳转登录页。 */
|
||||
onAuthExpired?: () => void
|
||||
}
|
||||
|
||||
const RECONNECT_DELAY_MS = 3_000
|
||||
|
||||
/** 通过 fetch 保持带 Bearer 鉴权的 SSE 连接,避免把登录 token 暴露到 URL。 */
|
||||
export function useRealtimeEvents({ url, token, onEvent, onConnected }: UseRealtimeEventsOptions) {
|
||||
export function useRealtimeEvents({
|
||||
url,
|
||||
token,
|
||||
onEvent,
|
||||
onConnected,
|
||||
onAuthExpired,
|
||||
}: UseRealtimeEventsOptions) {
|
||||
const onEventRef = useRef(onEvent)
|
||||
const onConnectedRef = useRef(onConnected)
|
||||
const onAuthExpiredRef = useRef(onAuthExpired)
|
||||
onEventRef.current = onEvent
|
||||
onConnectedRef.current = onConnected
|
||||
onAuthExpiredRef.current = onAuthExpired
|
||||
|
||||
useEffect(() => {
|
||||
if (!token) return
|
||||
@@ -50,6 +60,7 @@ export function useRealtimeEvents({ url, token, onEvent, onConnected }: UseRealt
|
||||
Authorization: `Bearer ${token}`,
|
||||
}
|
||||
if (lastEventId > 0) headers['Last-Event-ID'] = String(lastEventId)
|
||||
let authExpired = false
|
||||
void fetch(url, {
|
||||
method: 'GET',
|
||||
headers,
|
||||
@@ -57,6 +68,12 @@ export function useRealtimeEvents({ url, token, onEvent, onConnected }: UseRealt
|
||||
signal: abortController.signal,
|
||||
})
|
||||
.then(async (response) => {
|
||||
if (response.status === 401 || response.status === 403) {
|
||||
// 会话已失效:重连只会无限 401,交给调用方清理会话跳登录。
|
||||
authExpired = true
|
||||
onAuthExpiredRef.current?.()
|
||||
return
|
||||
}
|
||||
if (!response.ok || !response.body) {
|
||||
throw new Error(`realtime_stream_${response.status}`)
|
||||
}
|
||||
@@ -73,7 +90,7 @@ export function useRealtimeEvents({ url, token, onEvent, onConnected }: UseRealt
|
||||
})
|
||||
.catch(() => undefined)
|
||||
.finally(() => {
|
||||
if (stopped || document.visibilityState !== 'visible') return
|
||||
if (authExpired || stopped || document.visibilityState !== 'visible') return
|
||||
reconnectTimer = window.setTimeout(connect, RECONNECT_DELAY_MS)
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user