diff --git a/backend/server-prod.js b/backend/server-prod.js index 0e6b46f..8374033 100644 --- a/backend/server-prod.js +++ b/backend/server-prod.js @@ -21,6 +21,9 @@ const client = new MongoClient(uri); let db; let amigosCollection; +// Array para almacenar las conexiones SSE +let sseClients = []; + // Connect to MongoDB async function connectDB() { try { @@ -49,6 +52,40 @@ app.get('/api/amigos', async (req, res) => { } }); +// SSE endpoint para cambios en tiempo real +app.get('/api/stream', (req, res) => { + // Configurar headers para SSE + res.writeHead(200, { + 'Content-Type': 'text/event-stream', + 'Cache-Control': 'no-cache', + 'Connection': 'keep-alive', + 'Access-Control-Allow-Origin': '*', + 'Access-Control-Allow-Headers': 'Cache-Control' + }); + + // Agregar cliente a la lista + sseClients.push(res); + + // Manejar desconexión del cliente + req.on('close', () => { + sseClients = sseClients.filter(client => client !== res); + }); + + // Enviar ping inicial + res.write(`data: ${JSON.stringify({ type: 'connected', message: 'SSE connected' })}\n\n`); +}); + +// Función para notificar a todos los clientes SSE +function notifyClients(data) { + sseClients.forEach(client => { + try { + client.write(`data: ${JSON.stringify(data)}\n\n`); + } catch (error) { + console.error('Error sending SSE data:', error); + } + }); +} + // Add new amigo app.post('/api/amigos', async (req, res) => { try { @@ -62,6 +99,14 @@ app.post('/api/amigos', async (req, res) => { } const result = await amigosCollection.insertOne({ nombre }); + const newAmigo = { _id: result.insertedId, nombre }; + + // Notificar a todos los clientes SSE + notifyClients({ + type: 'amigo_added', + amigo: newAmigo + }); + res.status(201).json({ message: 'Amigo agregado exitosamente', id: result.insertedId, diff --git a/backend/server.js b/backend/server.js index 1371cd9..c48dc94 100644 --- a/backend/server.js +++ b/backend/server.js @@ -16,6 +16,9 @@ const client = new MongoClient(uri); let db; let amigosCollection; +// Array para almacenar las conexiones SSE +let sseClients = []; + // Connect to MongoDB async function connectDB() { try { @@ -48,6 +51,40 @@ app.get('/api/vistiantes_hoy', async (req, res) => { } }); +// SSE endpoint para cambios en tiempo real +app.get('/api/stream', (req, res) => { + // Configurar headers para SSE + res.writeHead(200, { + 'Content-Type': 'text/event-stream', + 'Cache-Control': 'no-cache', + 'Connection': 'keep-alive', + 'Access-Control-Allow-Origin': '*', + 'Access-Control-Allow-Headers': 'Cache-Control' + }); + + // Agregar cliente a la lista + sseClients.push(res); + + // Manejar desconexión del cliente + req.on('close', () => { + sseClients = sseClients.filter(client => client !== res); + }); + + // Enviar ping inicial + res.write(`data: ${JSON.stringify({ type: 'connected', message: 'SSE connected' })}\n\n`); +}); + +// Función para notificar a todos los clientes SSE +function notifyClients(data) { + sseClients.forEach(client => { + try { + client.write(`data: ${JSON.stringify(data)}\n\n`); + } catch (error) { + console.error('Error sending SSE data:', error); + } + }); +} + // Add new amigo app.post('/api/amigos', async (req, res) => { try { @@ -57,6 +94,14 @@ app.post('/api/amigos', async (req, res) => { } const result = await amigosCollection.insertOne({ nombre }); + const newAmigo = { _id: result.insertedId, nombre }; + + // Notificar a todos los clientes SSE + notifyClients({ + type: 'amigo_added', + amigo: newAmigo + }); + res.status(201).json({ message: 'Amigo agregado exitosamente', id: result.insertedId, diff --git a/src/App.jsx b/src/App.jsx index 9e3f083..39d6c15 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,4 +1,4 @@ -import { useState, useEffect } from 'react' +import { useState, useEffect, useRef } from 'react' import './App.css' // Configure API base URL via Vite env, default to same-origin @@ -9,11 +9,34 @@ function App() { const [amigos, setAmigos] = useState([]) const [loading, setLoading] = useState(false) const [message, setMessage] = useState('') + const inputRef = useRef(null) useEffect(() => { fetchAmigos() + // Conectar a SSE para cambios en tiempo real + connectSSE() }, []) + const connectSSE = () => { + const eventSource = new EventSource(`${API_BASE}/api/stream`) + + eventSource.onmessage = (event) => { + const data = JSON.parse(event.data) + if (data.type === 'amigo_added') { + setAmigos(prevAmigos => [...prevAmigos, data.amigo]) + } + } + + eventSource.onerror = (error) => { + console.error('SSE error:', error) + eventSource.close() + // Reconectar después de 3 segundos + setTimeout(connectSSE, 3000) + } + + return () => eventSource.close() + } + const fetchAmigos = async () => { try { const response = await fetch(`${API_BASE}/api/amigos`) @@ -49,7 +72,9 @@ function App() { if (response.ok) { setMessage(`¡${data.nombre} agregado exitosamente!`) setNombre('') - fetchAmigos() + // Ya no necesitamos fetchAmigos() porque SSE actualizará automáticamente + // Mantener focus en el input + inputRef.current?.focus() } else { setMessage(data.error || 'Error al agregar amigo') } @@ -69,10 +94,16 @@ function App() {
setNombre(e.target.value)} + onKeyDown={(e) => { + if (e.key === 'Enter' && !loading) { + handleSubmit(e) + } + }} placeholder="Ingresa el nombre" disabled={loading} />