Files
web-antrean/composables/useWebSocket.ts
2026-01-12 11:21:54 +07:00

168 lines
4.7 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// composables/useWebSocket.ts
import { ref, computed, onUnmounted } from 'vue'
export interface WebSocketConfig {
url: string
clientId: string
onMessage?: (data: any) => void
onOpen?: () => void
onClose?: () => void
onError?: (error: Event) => void
reconnectInterval?: number
maxReconnectAttempts?: number
}
export interface WebSocketMessage {
to_client: string
data: any
}
export const useWebSocket = (config: WebSocketConfig) => {
const ws = ref<WebSocket | null>(null)
const isConnected = ref(false)
const reconnectAttempts = ref(0)
const reconnectTimer = ref<NodeJS.Timeout | null>(null)
const wsUrl = computed(() => {
const url = new URL(config.url)
url.searchParams.set('client_id', config.clientId)
return url.toString()
})
const connect = () => {
try {
// Close existing connection if any
if (ws.value && ws.value.readyState !== WebSocket.CLOSED) {
ws.value.close()
}
ws.value = new WebSocket(wsUrl.value)
ws.value.onopen = () => {
console.log('WebSocket connected:', config.clientId)
isConnected.value = true
reconnectAttempts.value = 0
config.onOpen?.()
}
ws.value.onmessage = (event) => {
try {
const data = JSON.parse(event.data)
config.onMessage?.(data)
} catch (error) {
console.error('Error parsing WebSocket message:', error)
}
}
ws.value.onclose = () => {
console.log('WebSocket closed:', config.clientId)
isConnected.value = false
config.onClose?.()
// Attempt to reconnect
if (reconnectAttempts.value < (config.maxReconnectAttempts || 5)) {
reconnectAttempts.value++
const interval = config.reconnectInterval || 3000
reconnectTimer.value = setTimeout(() => {
console.log(`Reconnecting... Attempt ${reconnectAttempts.value}`)
connect()
}, interval)
} else {
console.error('Max reconnection attempts reached')
}
}
ws.value.onerror = (error) => {
console.error('WebSocket error:', error)
config.onError?.(error)
}
} catch (error) {
console.error('Error creating WebSocket:', error)
config.onError?.(error as Event)
}
}
const disconnect = () => {
if (reconnectTimer.value) {
clearTimeout(reconnectTimer.value)
reconnectTimer.value = null
}
if (ws.value) {
ws.value.close()
ws.value = null
}
isConnected.value = false
}
const sendMessage = (message: any) => {
if (ws.value && ws.value.readyState === WebSocket.OPEN) {
ws.value.send(JSON.stringify(message))
return true
}
console.warn('WebSocket is not connected')
return false
}
// Send data via POST to WebSocket server
const sendViaPost = async (message: WebSocketMessage) => {
try {
// Extract base URL from WebSocket URL (convert ws:// to http:// or wss:// to https://)
let baseUrl = config.url
if (baseUrl.startsWith('ws://')) {
baseUrl = baseUrl.replace('ws://', 'http://')
} else if (baseUrl.startsWith('wss://')) {
baseUrl = baseUrl.replace('wss://', 'https://')
}
// Remove query parameters and ensure we have the correct POST endpoint
const urlObj = new URL(baseUrl)
const postUrl = `${urlObj.protocol}//${urlObj.host}${urlObj.pathname}`
console.log('📡 POST URL:', postUrl)
console.log('📦 POST Body:', JSON.stringify(message, null, 2))
const response = await fetch(postUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(message),
})
console.log('📥 POST Response status:', response.status, response.statusText)
if (!response.ok) {
const errorText = await response.text().catch(() => 'Unknown error')
console.error('❌ POST Error response:', errorText)
throw new Error(`HTTP error! status: ${response.status}, message: ${errorText}`)
}
const result = await response.json().catch(() => {
console.log(' Response is not JSON, assuming success')
return { success: true }
})
console.log('✅ POST Response data:', result)
return result
} catch (error) {
console.error('❌ Error sending message via POST:', error)
throw error
}
}
onUnmounted(() => {
disconnect()
})
return {
ws: computed(() => ws.value),
isConnected: computed(() => isConnected.value),
connect,
disconnect,
sendMessage,
sendViaPost,
reconnectAttempts: computed(() => reconnectAttempts.value),
}
}