WebSockets

Socket.IO com rooms, broadcast, notificacoes e setup por framework.

O PlazerCLI configura Socket.IO para comunicacao em tempo real. O setup inclui autenticacao por JWT no handshake, rooms por usuario/organizacao e broadcast de eventos.

Setup NestJS

No NestJS, o PlazerCLI gera um Gateway completo:

// apps/api/src/websockets/events.gateway.ts
import {
  WebSocketGateway,
  WebSocketServer,
  SubscribeMessage,
  OnGatewayConnection,
  OnGatewayDisconnect,
} from '@nestjs/websockets';
import { Server, Socket } from 'socket.io';
import { JwtService } from '@nestjs/jwt';

@WebSocketGateway({
  cors: { origin: process.env.FRONTEND_URL || 'http://localhost:3000' },
  namespace: '/events',
})
export class EventsGateway implements OnGatewayConnection, OnGatewayDisconnect {
  @WebSocketServer()
  server: Server;

  constructor(private jwtService: JwtService) {}

  async handleConnection(client: Socket) {
    try {
      const token = client.handshake.auth.token;
      const payload = this.jwtService.verify(token);
      client.data.userId = payload.sub;

      // Entrar na room pessoal
      await client.join(`user:${payload.sub}`);

      // Se tiver organizacao, entrar na room da org
      if (payload.orgId) {
        await client.join(`org:${payload.orgId}`);
      }
    } catch {
      client.disconnect();
    }
  }

  handleDisconnect(client: Socket) {
    console.log(`Client disconnected: ${client.id}`);
  }

  @SubscribeMessage('message')
  handleMessage(client: Socket, payload: { room: string; data: any }) {
    this.server.to(payload.room).emit('message', {
      userId: client.data.userId,
      data: payload.data,
      timestamp: new Date().toISOString(),
    });
  }

  // Metodo utilitario para enviar notificacoes
  sendNotification(userId: string, notification: any) {
    this.server.to(`user:${userId}`).emit('notification', notification);
  }
}

Setup Express / Fastify

// apps/api/src/websockets/socket.ts
import { Server } from 'socket.io';
import jwt from 'jsonwebtoken';

export function setupWebSockets(httpServer: any) {
  const io = new Server(httpServer, {
    cors: { origin: process.env.FRONTEND_URL || 'http://localhost:3000' },
  });

  // Middleware de autenticacao
  io.use((socket, next) => {
    const token = socket.handshake.auth.token;
    try {
      const payload = jwt.verify(token, process.env.JWT_SECRET!) as any;
      socket.data.userId = payload.sub;
      socket.data.orgId = payload.orgId;
      next();
    } catch {
      next(new Error('Authentication error'));
    }
  });

  io.on('connection', (socket) => {
    // Rooms automaticas
    socket.join(`user:${socket.data.userId}`);
    if (socket.data.orgId) {
      socket.join(`org:${socket.data.orgId}`);
    }

    socket.on('message', (payload) => {
      io.to(payload.room).emit('message', {
        userId: socket.data.userId,
        data: payload.data,
        timestamp: new Date().toISOString(),
      });
    });
  });

  return io;
}

// No server principal:
import { createServer } from 'http';
const httpServer = createServer(app);
const io = setupWebSockets(httpServer);
httpServer.listen(3001);

Cliente (Frontend)

// apps/web/src/lib/socket.ts
import { io, Socket } from 'socket.io-client';

let socket: Socket | null = null;

export function connectSocket(token: string): Socket {
  if (socket?.connected) return socket;

  socket = io(process.env.NEXT_PUBLIC_API_URL + '/events', {
    auth: { token },
    reconnection: true,
    reconnectionAttempts: 5,
    reconnectionDelay: 1000,
  });

  socket.on('connect', () => console.log('Socket connected'));
  socket.on('disconnect', () => console.log('Socket disconnected'));

  return socket;
}

export function getSocket(): Socket | null {
  return socket;
}

// Uso em um componente React:
import { useEffect, useState } from 'react';
import { connectSocket, getSocket } from '@/lib/socket';

export function useNotifications(token: string) {
  const [notifications, setNotifications] = useState([]);

  useEffect(() => {
    const socket = connectSocket(token);
    socket.on('notification', (data) => {
      setNotifications((prev) => [data, ...prev]);
    });
    return () => { socket.off('notification'); };
  }, [token]);

  return notifications;
}