first commit

This commit is contained in:
rayd1o
2026-03-05 11:46:58 +08:00
commit e7033775d8
20657 changed files with 1988940 additions and 0 deletions

View File

@@ -0,0 +1,151 @@
import { useEffect, useRef, useState, useCallback } from 'react'
import { useAuthStore } from '../stores/auth'
const WS_URL = (import.meta as any).env?.VITE_WS_URL || 'ws://localhost:8000/ws'
interface WebSocketMessage {
type: string
channel?: string
timestamp?: string
data?: Record<string, unknown>
payload?: Record<string, unknown>
}
interface UseWebSocketOptions {
autoConnect?: boolean
autoSubscribe?: string[]
heartbeatInterval?: number
onMessage?: (message: WebSocketMessage) => void
onConnect?: () => void
onDisconnect?: () => void
onError?: (error: Error) => void
}
interface UseWebSocketReturn {
connected: boolean
lastMessage: WebSocketMessage | null
sendMessage: (message: Record<string, unknown>) => void
subscribe: (channels: string[]) => void
connect: () => void
disconnect: () => void
}
export function useWebSocket(options: UseWebSocketOptions = {}): UseWebSocketReturn {
const {
autoConnect = true,
autoSubscribe = [],
onMessage,
onConnect,
onDisconnect,
onError,
} = options
const { token } = useAuthStore()
console.log('[WebSocket] Token present:', !!token, token ? token.substring(0, 20) + '...' : 'none')
console.log('[WebSocket] autoConnect:', autoConnect)
const wsRef = useRef<WebSocket | null>(null)
const [connected, setConnected] = useState(false)
const [lastMessage, setLastMessage] = useState<WebSocketMessage | null>(null)
const reconnectTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null)
const heartbeatTimerRef = useRef<ReturnType<typeof setInterval> | null>(null)
const connect = useCallback(() => {
if (!token) {
console.log('[WebSocket] No token, skipping connect')
return
}
const wsUrl = `${WS_URL}?token=${token}`
console.log('[WebSocket] Connecting to:', wsUrl)
const ws = new WebSocket(wsUrl)
ws.onopen = () => {
console.log('[WebSocket] Connected!')
setConnected(true)
if (autoSubscribe.length > 0) {
ws.send(JSON.stringify({ type: 'subscribe', data: { channels: autoSubscribe } }))
}
onConnect?.()
}
ws.onmessage = (event) => {
console.log('[WebSocket] Received:', event.data)
try {
const message: WebSocketMessage = JSON.parse(event.data)
setLastMessage(message)
onMessage?.(message)
} catch {
console.error('Failed to parse WebSocket message')
}
}
ws.onclose = (event) => {
console.log('[WebSocket] Disconnected:', event.code, event.reason)
setConnected(false)
if (heartbeatTimerRef.current) {
clearInterval(heartbeatTimerRef.current)
heartbeatTimerRef.current = null
}
onDisconnect?.()
if (autoConnect && token) {
reconnectTimeoutRef.current = setTimeout(() => {
console.log('[WebSocket] Reconnecting...')
connect()
}, 3000)
}
}
ws.onerror = (error) => {
console.error('[WebSocket] Error:', error)
onError?.(new Error('WebSocket error'))
}
wsRef.current = ws
}, [token, autoConnect, autoSubscribe, onConnect, onDisconnect, onError])
const disconnect = useCallback(() => {
if (reconnectTimeoutRef.current) {
clearTimeout(reconnectTimeoutRef.current)
reconnectTimeoutRef.current = null
}
if (heartbeatTimerRef.current) {
clearInterval(heartbeatTimerRef.current)
heartbeatTimerRef.current = null
}
wsRef.current?.close()
wsRef.current = null
setConnected(false)
}, [])
const sendMessage = useCallback((message: Record<string, unknown>) => {
wsRef.current?.send(JSON.stringify(message))
}, [])
const subscribe = useCallback((channels: string[]) => {
wsRef.current?.send(JSON.stringify({
type: 'subscribe',
data: { channels }
}))
}, [])
useEffect(() => {
console.log('[WebSocket] useEffect triggered, autoConnect:', autoConnect, 'token:', !!token)
if (autoConnect && token) {
connect()
}
return () => {
disconnect()
}
}, [autoConnect, token, connect, disconnect])
return {
connected,
lastMessage,
sendMessage,
subscribe,
connect,
disconnect,
}
}