Files
clube67_newwhats.local/clube67/newwhats.local/frontend/services/socketService.ts
T
VPS 4 Deploy Agent 0dc5eefa06
continuous-integration/webhook Falha no deploy de clube67_newwhats.local (VPS 4)
chore(ops): restore missing root files in newwhats.clube67.com
2026-05-18 03:27:25 +02:00

141 lines
4.1 KiB
TypeScript

import { io, Socket } from 'socket.io-client'
const SOCKET_URL = (() => {
const url = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8008'
try {
return new URL(url).origin
} catch {
return url
}
})()
type Listener = (data: any) => void
const HEARTBEAT_INTERVAL_MS = 30_000
class SocketService {
private socket: Socket | null = null
private token: string | null = null
private listeners = new Map<string, Set<Listener>>()
private heartbeatTimer: ReturnType<typeof setInterval> | null = null
// ── Conexão ────────────────────────────────────────────────────────────────
connect(token?: string) {
if (token) this.token = token
if (this.socket?.connected) return
this.socket = io(SOCKET_URL, {
auth: { token: this.token },
transports: ['websocket', 'polling'],
reconnectionAttempts: 10,
reconnectionDelay: 2000,
})
this.socket.on('connect', () => {
this._emit('connected', { id: this.socket?.id })
this._startHeartbeat()
})
this.socket.on('disconnect', (reason) => {
this._stopHeartbeat()
this._emit('disconnected', { reason })
})
this.socket.on('connect_error', (err) => {
console.warn('[Socket] connect_error:', err.message)
})
this.socket.onAny((event, data) => {
console.log('[Socket Event Received]', event, data)
this._emit(event, data)
if (event !== '*') this._emit('*', { event, data })
})
}
disconnect() {
this._stopHeartbeat()
this.socket?.disconnect()
this.socket = null
this.token = null
}
// ── Emit para o servidor ───────────────────────────────────────────────────
emit(event: string, ...args: any[]) {
this.socket?.emit(event, ...args)
}
joinInstance(instanceId: string) {
if (this.socket?.connected) {
this.socket.emit('join:instance', instanceId)
} else {
// Aguarda conexão e então entra na sala
const onConnect = () => {
this.socket?.emit('join:instance', instanceId)
this.off('connected', onConnect)
}
this.on('connected', onConnect)
}
}
joinChat(chatId: string) {
if (this.socket?.connected) {
this.socket.emit('join:chat', chatId)
} else {
const onConnect = () => {
this.socket?.emit('join:chat', chatId)
this.off('connected', onConnect)
}
this.on('connected', onConnect)
}
}
leaveChat(chatId: string) {
this.socket?.emit('leave:chat', chatId)
}
// ── Listeners ─────────────────────────────────────────────────────────────
on(event: string, cb: Listener) {
if (!this.listeners.has(event)) this.listeners.set(event, new Set())
this.listeners.get(event)!.add(cb)
}
off(event: string, cb: Listener) {
this.listeners.get(event)?.delete(cb)
}
// ── Estado ────────────────────────────────────────────────────────────────
get connected() {
return this.socket?.connected ?? false
}
getSocket() {
return this.socket
}
// ── Privado ───────────────────────────────────────────────────────────────
private _startHeartbeat() {
this._stopHeartbeat()
this.heartbeatTimer = setInterval(() => {
if (this.socket?.connected) this.socket.emit('user:heartbeat')
}, HEARTBEAT_INTERVAL_MS)
}
private _stopHeartbeat() {
if (this.heartbeatTimer) {
clearInterval(this.heartbeatTimer)
this.heartbeatTimer = null
}
}
private _emit(event: string, data: any) {
this.listeners.get(event)?.forEach((cb) => cb(data))
}
}
export const socketService = new SocketService()