// 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(null) const isConnected = ref(false) const reconnectAttempts = ref(0) const reconnectTimer = ref(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), } }