From e9aecc6bd2060fa580e5ce5e023db1a48d3cf69c Mon Sep 17 00:00:00 2001 From: Lizandro Guarnizo <77708265+lizandrogd@users.noreply.github.com> Date: Wed, 28 Jan 2026 02:44:28 -0500 Subject: [PATCH] up --- Dockerfile | 8 ++- Dockerfile.worker | 8 ++- api/sse_events.php | 155 +++++++++++++++++++++++++--------------- docker-compose.dev.yml | 20 +++--- docker-compose.prod.yml | 4 ++ health.php | 5 +- 6 files changed, 130 insertions(+), 70 deletions(-) diff --git a/Dockerfile b/Dockerfile index 9124f2b..3a95f83 100644 --- a/Dockerfile +++ b/Dockerfile @@ -49,6 +49,9 @@ LABEL maintainer="U-Site.app" LABEL description="WhatsApp Bot Manager with Queue Processing" LABEL version="2.0" +# Configurar zona horaria Colombia +ENV TZ=America/Bogota + # Instalar dependencias runtime RUN apk add --no-cache \ bash \ @@ -64,7 +67,10 @@ RUN apk add --no-cache \ mysql-client \ redis \ ffmpeg \ - openssl + openssl \ + tzdata \ + && cp /usr/share/zoneinfo/America/Bogota /etc/localtime \ + && echo "America/Bogota" > /etc/timezone # Copiar extensiones PHP desde builder COPY --from=builder /usr/local/lib/php/extensions/ /usr/local/lib/php/extensions/ diff --git a/Dockerfile.worker b/Dockerfile.worker index 6948272..929cb3e 100644 --- a/Dockerfile.worker +++ b/Dockerfile.worker @@ -1,6 +1,9 @@ # Dockerfile para workers dedicados (sin Nginx) FROM php:8.2-cli-alpine +# Configurar zona horaria Colombia +ENV TZ=America/Bogota + # Instalar dependencias RUN apk add --no-cache \ bash \ @@ -13,7 +16,10 @@ RUN apk add --no-cache \ oniguruma-dev \ mysql-client \ redis \ - zlib-dev + zlib-dev \ + tzdata \ + && cp /usr/share/zoneinfo/America/Bogota /etc/localtime \ + && echo "America/Bogota" > /etc/timezone # Instalar extensiones PHP RUN docker-php-ext-install -j$(nproc) \ diff --git a/api/sse_events.php b/api/sse_events.php index 699d0eb..6897f53 100644 --- a/api/sse_events.php +++ b/api/sse_events.php @@ -125,67 +125,104 @@ try { // Revisar nuevos mensajes en BD cada 3 segundos if (time() - $lastCheck >= 3) { - // 1. Verificar mensajes nuevos individuales (últimos 10 segundos) - $recentMessages = $db->fetchAll( - "SELECT c.*, u.phone_number, u.name as user_name - FROM conversations c - INNER JOIN users u ON c.user_id = u.id - WHERE c.created_at >= DATE_SUB(NOW(), INTERVAL 10 SECOND) - AND c.direction = 'incoming' - ORDER BY c.created_at DESC - LIMIT 10" - ); + // Mantener track de mensajes ya enviados para evitar duplicados + static $lastMessageId = 0; + static $lastConversationCheck = null; - foreach ($recentMessages as $msg) { - sendSSEEvent('new_message', [ - 'message_id' => $msg['id'], - 'user_id' => $msg['user_id'], - 'phone_number' => $msg['phone_number'], - 'user_name' => $msg['user_name'], - 'content' => $msg['message_content'], - 'message_type' => $msg['message_type'] ?? 'text', - 'direction' => $msg['direction'], - 'created_at' => $msg['created_at'], - 'timestamp' => time() - ]); - } - - // 2. Verificar si hay nuevas conversaciones desde la última revisión - $recentConversations = $db->fetchAll( - "SELECT DISTINCT c.user_id, u.phone_number, u.name, - MAX(c.created_at) as last_message_time, - COUNT(*) as message_count - FROM conversations c - INNER JOIN users u ON c.user_id = u.id - WHERE c.created_at >= DATE_SUB(NOW(), INTERVAL 10 SECOND) - GROUP BY c.user_id - ORDER BY last_message_time DESC - LIMIT 5" - ); - - foreach ($recentConversations as $conv) { - sendSSEEvent('new_conversation', [ - 'user_id' => $conv['user_id'], - 'phone_number' => $conv['phone_number'], - 'name' => $conv['name'], - 'message_count' => $conv['message_count'], - 'timestamp' => $conv['last_message_time'] - ]); - } - - // Verificar notificaciones no leídas - try { - $notifications = $db->fetchAll( - "SELECT id, user_id, type, message, data, is_read, created_at - FROM notifications - WHERE is_read = 0 - ORDER BY created_at DESC - LIMIT 10" + if ($lastConversationCheck === null) { + // Primera vez: obtener el ID más alto y timestamp actual de la BD, sin enviar nada + $maxMsg = $db->fetch("SELECT MAX(id) as max_id FROM conversations"); + $lastMessageId = $maxMsg['max_id'] ?? 0; + // Usar el timestamp de la BD para evitar problemas de zona horaria + $dbTime = $db->fetch("SELECT NOW() as now"); + $lastConversationCheck = $dbTime['now'] ?? date('Y-m-d H:i:s'); + } else { + // 1. Verificar mensajes NUEVOS (solo los que tienen ID mayor al último visto) + $recentMessages = $db->fetchAll( + "SELECT c.*, u.phone_number, u.name as user_name + FROM conversations c + INNER JOIN users u ON c.user_id = u.id + WHERE c.id > ? + AND c.direction = 'incoming' + ORDER BY c.id ASC + LIMIT 10", + [$lastMessageId] ); - - if ($notifications && count($notifications) > 0) { - foreach ($notifications as $notification) { - sendSSEEvent('notification', $notification); + + foreach ($recentMessages as $msg) { + sendSSEEvent('new_message', [ + 'message_id' => $msg['id'], + 'user_id' => $msg['user_id'], + 'phone_number' => $msg['phone_number'], + 'user_name' => $msg['user_name'], + 'content' => $msg['message_content'], + 'message_type' => $msg['message_type'] ?? 'text', + 'direction' => $msg['direction'], + 'created_at' => $msg['created_at'], + 'timestamp' => time() + ]); + // Actualizar último ID visto + if ($msg['id'] > $lastMessageId) { + $lastMessageId = $msg['id']; + } + } + + // 2. Verificar si hay nuevas conversaciones desde la última revisión + $recentConversations = $db->fetchAll( + "SELECT DISTINCT c.user_id, u.phone_number, u.name, + MAX(c.created_at) as last_message_time, + COUNT(*) as message_count + FROM conversations c + INNER JOIN users u ON c.user_id = u.id + WHERE c.created_at > ? + GROUP BY c.user_id + ORDER BY last_message_time DESC + LIMIT 5", + [$lastConversationCheck] + ); + + foreach ($recentConversations as $conv) { + sendSSEEvent('new_conversation', [ + 'user_id' => $conv['user_id'], + 'phone_number' => $conv['phone_number'], + 'name' => $conv['name'], + 'message_count' => $conv['message_count'], + 'timestamp' => $conv['last_message_time'] + ]); + // Actualizar timestamp + if ($conv['last_message_time'] > $lastConversationCheck) { + $lastConversationCheck = $conv['last_message_time']; + } + } + } // Fin del else (primera inicialización vs verificaciones posteriores) + + // Verificar notificaciones NUEVAS (solo las creadas desde la última verificación) + // NO enviar notificaciones guardadas - solo las que llegan en tiempo real + try { + static $lastNotificationCheck = null; + if ($lastNotificationCheck === null) { + // Primera vez: usar el timestamp de la BD, NO enviar nada + $dbTimeNotif = $db->fetch("SELECT NOW() as now"); + $lastNotificationCheck = $dbTimeNotif['now'] ?? date('Y-m-d H:i:s'); + } else { + // Verificaciones posteriores: solo notificaciones NUEVAS + $notifications = $db->fetchAll( + "SELECT id, user_id, type, message, data, is_read, created_at + FROM notifications + WHERE created_at > ? + ORDER BY created_at ASC + LIMIT 10", + [$lastNotificationCheck] + ); + + if ($notifications && count($notifications) > 0) { + foreach ($notifications as $notification) { + sendSSEEvent('notification', $notification); + // Actualizar timestamp para evitar duplicados + if ($notification['created_at'] > $lastNotificationCheck) { + $lastNotificationCheck = $notification['created_at']; + } + } } } } catch (Exception $e) { diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index cb27d31..1607534 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -2,26 +2,27 @@ # Uso: docker-compose -f docker-compose.dev.yml up -d services: - # MySQL Database + # MariaDB Database (compatible con producción) mysql: - image: mysql:8.0 + image: mariadb:10.11 container_name: whatsapp-dev-mysql restart: unless-stopped ports: - "3306:3306" environment: - MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD:-root_password_2026} - MYSQL_DATABASE: ${DB_NAME:-usite_whatsapp_bot} - MYSQL_USER: ${DB_USER:-usite_whatsapp_user} - MYSQL_PASSWORD: ${DB_PASS:-6q!Vio0fn@PFmsLyHjH5} + MARIADB_ROOT_PASSWORD: ${DB_ROOT_PASSWORD:-root_password_2026} + MARIADB_DATABASE: ${DB_NAME:-usite_whatsapp_bot} + MARIADB_USER: ${DB_USER:-usite_whatsapp_user} + MARIADB_PASSWORD: ${DB_PASS:-6q!Vio0fn@PFmsLyHjH5} + TZ: America/Bogota volumes: - mysql-dev-data:/var/lib/mysql - ./database:/docker-entrypoint-initdb.d:ro - command: --default-authentication-plugin=mysql_native_password --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci + command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci --default-time-zone=America/Bogota networks: - whatsapp-dev-network healthcheck: - test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p${DB_ROOT_PASSWORD:-root_password_2026}"] + test: ["CMD", "mariadb-admin", "ping", "-h", "localhost", "-u", "root", "-p${DB_ROOT_PASSWORD:-root_password_2026}"] interval: 10s timeout: 5s retries: 5 @@ -50,6 +51,9 @@ services: - ssl-certs:/etc/nginx/ssl - php-socket:/run/php environment: + # Zona horaria Colombia + - TZ=America/Bogota + # Entorno de desarrollo - APP_ENV=development - APP_DEBUG=true diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index e67b49a..caf9572 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -21,6 +21,9 @@ services: - /etc/letsencrypt:/etc/letsencrypt:ro - ssl-certs:/etc/nginx/ssl environment: + # Zona horaria Colombia + - TZ=America/Bogota + # Entorno - APP_ENV=production - APP_DEBUG=false @@ -95,6 +98,7 @@ services: volumes: - ./logs:/var/www/html/logs environment: + - TZ=America/Bogota - APP_ENV=production - APP_DEBUG=false - DB_HOST=${DB_HOST:-77.37.126.126} diff --git a/health.php b/health.php index 316910f..402ece8 100644 --- a/health.php +++ b/health.php @@ -4,7 +4,10 @@ * Verifica que todos los servicios estén funcionando */ -require_once __DIR__ . '/vendor/autoload.php'; +// Cargar autoload solo si existe (en desarrollo podría no estar) +if (file_exists(__DIR__ . '/vendor/autoload.php')) { + require_once __DIR__ . '/vendor/autoload.php'; +} header('Content-Type: application/json');