This commit is contained in:
Lizandro Guarnizo
2026-01-28 02:23:36 -05:00
parent 37d6bf1e20
commit 6ebf5c9a22
30 changed files with 2264 additions and 118 deletions
+8 -4
View File
@@ -62,7 +62,9 @@ RUN apk add --no-cache \
libjpeg-turbo \
libpng \
mysql-client \
redis
redis \
ffmpeg \
openssl
# Copiar extensiones PHP desde builder
COPY --from=builder /usr/local/lib/php/extensions/ /usr/local/lib/php/extensions/
@@ -71,9 +73,10 @@ COPY --from=builder /usr/local/etc/php/conf.d/ /usr/local/etc/php/conf.d/
# Copiar Composer desde builder
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
# Crear usuario para la aplicación
# Crear usuario para la aplicación y configurar grupos
RUN addgroup -g 1000 -S www && \
adduser -u 1000 -S www -G www
adduser -u 1000 -S www -G www && \
addgroup www nginx
# Configurar PHP
COPY docker/php/php.ini /usr/local/etc/php/conf.d/custom.ini
@@ -94,6 +97,7 @@ RUN mkdir -p \
/var/www/html/uploads \
/var/log/supervisor \
/run/php \
/etc/nginx/ssl \
&& chown -R www:www /var/www/html \
&& chown -R www:www /var/log/supervisor
@@ -118,7 +122,7 @@ HEALTHCHECK --interval=30s --timeout=5s --start-period=30s --retries=3 \
CMD curl -f http://localhost/health.php || exit 1
# Exponer puertos
EXPOSE 80 9000
EXPOSE 80 443 9000
# Script de inicio
COPY docker/entrypoint.sh /usr/local/bin/entrypoint.sh
+3
View File
@@ -14,6 +14,7 @@ RUN apk add --no-cache \
bash \
mysql-client \
redis \
ffmpeg \
# Dependencias para XDebug y desarrollo
autoconf \
g++ \
@@ -68,6 +69,8 @@ RUN { \
echo 'upload_max_filesize = 100M'; \
echo 'post_max_size = 100M'; \
echo 'date.timezone = America/Bogota'; \
# Habilitar shell_exec para conversión de audio
echo 'disable_functions = '; \
# Desactivar OPcache para hot-reload
echo 'opcache.enable = 0'; \
echo 'opcache.enable_cli = 0'; \
+57
View File
@@ -0,0 +1,57 @@
## ✅ Problema del Error 500 Corregido
### 🐛 Causa del Error:
Al pegar una imagen desde el portapapeles y enviarla, las peticiones `fetch` a los endpoints de API **NO estaban enviando las cookies de sesión**, causando que:
1. `upload_media.php` rechazara la petición → Error 401
2. Nginx devolviera error 500 por fallo en PHP
### 🔧 Solución Aplicada:
Agregué `credentials: 'same-origin'` a ambos fetch en `sendMediaMessage()`:
```javascript
// 1. Upload del archivo
const uploadResponse = await fetch('api/upload_media.php', {
method: 'POST',
credentials: 'same-origin', // ✅ Enviar cookies de sesión
body: formData
});
// 2. Envío del mensaje
const sendResponse = await fetch('api/send_media_message.php', {
method: 'POST',
credentials: 'same-origin', // ✅ Enviar cookies de sesión
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({...})
});
```
### 📋 Cómo Funciona Ahora:
1. **Copias una imagen** (Ctrl+C o Cmd+C)
2. **Haces click en el input de mensaje**
3. **Pegas** (Ctrl+V o Cmd+V)
4. ✅ Aparece el preview de la imagen
5. ✅ Haces click en "Enviar"
6. ✅ Se sube el archivo con autenticación correcta
7. ✅ Se envía por WhatsApp
8. ✅ Aparece en la conversación
### 🧪 Probar Ahora:
1. Recarga la página: http://localhost:8080/conversations.php
2. Abre una conversación
3. Copia una imagen desde cualquier lugar
4. Haz click en el input de texto
5. Presiona Ctrl+V (o Cmd+V en Mac)
6. Debe aparecer el preview sin errores
7. Haz click en enviar
8. Debe enviarse correctamente
### 📝 Nota Técnica:
Por defecto, `fetch()` **NO envía cookies** a menos que especifiques:
- `credentials: 'same-origin'` → Envía cookies al mismo dominio
- `credentials: 'include'` → Envía cookies incluso en CORS
El endpoint `upload_media.php` usa `requireAuthentication()` que verifica la sesión en cookies, por eso fallaba antes.
+4
View File
@@ -41,6 +41,8 @@ try {
lm.content AS last_message,
lm.direction AS direction,
lm.message_type AS message_type,
lm.local_file AS last_local_file,
lm.local_thumb AS last_local_thumb,
lm.created_at AS last_time,
IFNULL(SUM(CASE WHEN c.direction = 'incoming' AND (c.is_read IS NULL OR c.is_read = 0) THEN 1 ELSE 0 END), 0) AS unread_count
FROM users u
@@ -86,6 +88,8 @@ try {
'last_time' => $conv['last_time'] ?? null,
'direction' => $conv['direction'] ?? 'incoming',
'message_type' => $conv['message_type'] ?? 'text',
'last_local_file' => $conv['last_local_file'] ?? null,
'last_local_thumb' => $conv['last_local_thumb'] ?? null,
'unread_count' => intval($conv['unread_count'] ?? 0),
'bot_enabled' => isset($conv['bot_enabled']) ? (bool)$conv['bot_enabled'] : true,
'on_hold' => isset($conv['on_hold']) ? (bool)$conv['on_hold'] : false,
+7 -1
View File
@@ -59,6 +59,9 @@ try {
c.content as content,
c.message_id as message_id,
c.media_url,
c.local_file,
c.filename,
c.mime_type,
u.phone_number as user_phone,
c.direction as direction,
c.message_type as message_type,
@@ -145,6 +148,10 @@ try {
message_text as content,
direction,
message_type,
media_url,
local_file,
filename,
mime_type,
status,
created_at
FROM conversations
@@ -179,7 +186,6 @@ try {
'message_id' => $msg['message_id'] ?? null,
'media_url' => null,
'local_file' => $msg['local_file'] ?? null,
'local_thumb' => $msg['local_thumb'] ?? null,
'media_url_external' => $external,
'filename' => $msg['filename'] ?? null,
'mime_type' => $msg['mime_type'] ?? null,
+39 -1
View File
@@ -355,6 +355,39 @@ try {
// Datos base para insertar
// content: nombre de archivo o caption (nunca JSON)
// media_url: ID de WhatsApp si está disponible, sino la URL original
// local_file: ruta relativa del archivo local si existe
// local_thumb: miniatura (por ahora usar mismo archivo para imágenes/videos)
// Intentar determinar ruta local desde mediaUrl si no se definió antes
if (!isset($localFilePath) && $mediaUrl) {
try {
error_log("send_media_message.php - Intentando extraer local_file desde mediaUrl: " . $mediaUrl);
$parsedUrl = parse_url($mediaUrl);
$path = $parsedUrl['path'] ?? '';
error_log("send_media_message.php - Path extraído: " . $path);
// Si la URL apunta a /uploads/, construir ruta local
if (strpos($path, '/uploads/') === 0) {
$uploadDir = __DIR__ . '/../uploads/';
$filename_from_url = basename($path);
$testPath = $uploadDir . $filename_from_url;
error_log("send_media_message.php - Testeando archivo local: " . $testPath);
if (file_exists($testPath)) {
$localFilePath = $testPath;
error_log("send_media_message.php - ✅ Archivo local encontrado: " . $localFilePath);
} else {
error_log("send_media_message.php - ❌ Archivo local NO existe: " . $testPath);
}
}
} catch (Exception $e) {
error_log("send_media_message.php - Error parseando mediaUrl: " . $e->getMessage());
}
} else {
error_log("send_media_message.php - localFilePath ya definido o mediaUrl vacío. localFilePath=" . (isset($localFilePath) ? $localFilePath : 'NULL') . ", mediaUrl=" . ($mediaUrl ?? 'NULL'));
}
$localFileRelative = (isset($localFilePath) && file_exists($localFilePath)) ? str_replace(__DIR__ . '/../', '', $localFilePath) : null;
error_log("send_media_message.php - local_file relativo para BD: " . ($localFileRelative ?? 'NULL'));
$conversationData = [
'user_id' => $user['id'],
'message_id' => $sentMessageId,
@@ -363,6 +396,9 @@ try {
'content' => $content,
'media_url' => isset($uploaded_media_id) ? $uploaded_media_id : $mediaUrl,
'whatsapp_media_id' => $uploaded_media_id ?? null,
'local_file' => $localFileRelative,
'local_thumb' => ($mediaType === 'image' || $mediaType === 'video') ? $localFileRelative : null,
'filename' => $filename ?? null,
'status' => 'sent',
'created_at' => date('Y-m-d H:i:s')
];
@@ -372,8 +408,10 @@ try {
error_log(" - content: " . $content);
error_log(" - media_url: " . ($conversationData['media_url'] ?? 'null'));
error_log(" - whatsapp_media_id: " . ($conversationData['whatsapp_media_id'] ?? 'null'));
error_log(" - local_file: " . ($conversationData['local_file'] ?? 'null'));
error_log(" - local_thumb: " . ($conversationData['local_thumb'] ?? 'null'));
error_log(" - media_type: " . $mediaType);
smm_log("Saving to DB - content: {$content}, media_url: " . ($conversationData['media_url'] ?? 'null'));
smm_log("Saving to DB - content: {$content}, media_url: " . ($conversationData['media_url'] ?? 'null') . ", local_file: " . ($conversationData['local_file'] ?? 'null') . ", local_thumb: " . ($conversationData['local_thumb'] ?? 'null'));
try {
$db->insert('conversations', $conversationData);
+147
View File
@@ -60,3 +60,150 @@
[2026-01-27 23:37:46] Send media by id response: {"messaging_product":"whatsapp","contacts":[{"input":"573022548060","wa_id":"573022548060"}],"messages":[{"id":"wamid.HBgMNTczMDIyNTQ4MDYwFQIAERgSNUU0NTQ4OURGNjYyQTkwQUI5AA=="}]}
[2026-01-27 23:37:46] WhatsApp response (successful): {"messaging_product":"whatsapp","contacts":[{"input":"573022548060","wa_id":"573022548060"}],"messages":[{"id":"wamid.HBgMNTczMDIyNTQ4MDYwFQIAERgSNUU0NTQ4OURGNjYyQTkwQUI5AA=="}]}
[2026-01-27 23:37:46] Saving to DB - content: survey.xlsx, media_url: 1374639530630158
[2026-01-27 23:57:57] Raw input: {"recipient":"573168950803","media_url":"http://localhost:8080/uploads/media_69799753b67d83.17233125.webm","media_type":"audio","caption":null,"filename":"record_1769576272869.webm"}
[2026-01-27 23:57:57] Input decoded: {"recipient":"573168950803","media_url":"http:\/\/localhost:8080\/uploads\/media_69799753b67d83.17233125.webm","media_type":"audio","caption":null,"filename":"record_1769576272869.webm"}
[2026-01-27 23:58:23] Raw input: {"recipient":"573168950803","media_url":"http://localhost:8080/uploads/media_6979976dc5ecb2.11097928.xlsx","media_type":"document","caption":null,"filename":"survey.xlsx"}
[2026-01-27 23:58:23] Input decoded: {"recipient":"573168950803","media_url":"http:\/\/localhost:8080\/uploads\/media_6979976dc5ecb2.11097928.xlsx","media_type":"document","caption":null,"filename":"survey.xlsx"}
[2026-01-27 23:58:24] Upload result: {"id":"1873321179980667"}
[2026-01-27 23:58:24] Media ID obtained: 1873321179980667
[2026-01-27 23:58:25] Send media by id response: {"messaging_product":"whatsapp","contacts":[{"input":"573168950803","wa_id":"573168950803"}],"messages":[{"id":"wamid.HBgMNTczMTY4OTUwODAzFQIAERgSMEE2QzEyRUE2NTAxRjJDODQwAA=="}]}
[2026-01-27 23:58:25] WhatsApp response (successful): {"messaging_product":"whatsapp","contacts":[{"input":"573168950803","wa_id":"573168950803"}],"messages":[{"id":"wamid.HBgMNTczMTY4OTUwODAzFQIAERgSMEE2QzEyRUE2NTAxRjJDODQwAA=="}]}
[2026-01-27 23:58:25] Saving to DB - content: survey.xlsx, media_url: 1873321179980667
[2026-01-28 00:02:10] Raw input: {"recipient":"573168950803","media_url":"http://localhost:8080/uploads/media_69799850802746.13854941.xlsx","media_type":"document","caption":null,"filename":"survey.xlsx"}
[2026-01-28 00:02:10] Input decoded: {"recipient":"573168950803","media_url":"http:\/\/localhost:8080\/uploads\/media_69799850802746.13854941.xlsx","media_type":"document","caption":null,"filename":"survey.xlsx"}
[2026-01-28 00:02:11] Upload result: {"id":"3100504000137603"}
[2026-01-28 00:02:11] Media ID obtained: 3100504000137603
[2026-01-28 00:02:12] Send media by id response: {"messaging_product":"whatsapp","contacts":[{"input":"573168950803","wa_id":"573168950803"}],"messages":[{"id":"wamid.HBgMNTczMTY4OTUwODAzFQIAERgSQzQ3RDRCQTFERThERjM2QTgxAA=="}]}
[2026-01-28 00:02:12] WhatsApp response (successful): {"messaging_product":"whatsapp","contacts":[{"input":"573168950803","wa_id":"573168950803"}],"messages":[{"id":"wamid.HBgMNTczMTY4OTUwODAzFQIAERgSQzQ3RDRCQTFERThERjM2QTgxAA=="}]}
[2026-01-28 00:02:13] Saving to DB - content: survey.xlsx, media_url: 3100504000137603
[2026-01-28 00:06:46] Raw input: {"recipient":"573168950803","media_url":"http://localhost:8080/uploads/media_69799964926f58.72435045.jpg","media_type":"image","caption":null,"filename":"media-url.jpg"}
[2026-01-28 00:06:46] Input decoded: {"recipient":"573168950803","media_url":"http:\/\/localhost:8080\/uploads\/media_69799964926f58.72435045.jpg","media_type":"image","caption":null,"filename":"media-url.jpg"}
[2026-01-28 00:06:47] Upload result: {"id":"876668735219378"}
[2026-01-28 00:06:47] Media ID obtained: 876668735219378
[2026-01-28 00:06:48] Send media by id response: {"messaging_product":"whatsapp","contacts":[{"input":"573168950803","wa_id":"573168950803"}],"messages":[{"id":"wamid.HBgMNTczMTY4OTUwODAzFQIAERgSQjlGNzZFODBDOTg3REQxRDZDAA=="}]}
[2026-01-28 00:06:48] WhatsApp response (successful): {"messaging_product":"whatsapp","contacts":[{"input":"573168950803","wa_id":"573168950803"}],"messages":[{"id":"wamid.HBgMNTczMTY4OTUwODAzFQIAERgSQjlGNzZFODBDOTg3REQxRDZDAA=="}]}
[2026-01-28 00:06:48] Saving to DB - content: media-url.jpg, media_url: 876668735219378
[2026-01-28 00:08:44] Raw input: {"recipient":"573168950803","media_url":"http://localhost:8080/uploads/media_697999dadb01c6.63157521.webm","media_type":"audio","caption":null,"filename":"record_1769576919723.webm"}
[2026-01-28 00:08:44] Input decoded: {"recipient":"573168950803","media_url":"http:\/\/localhost:8080\/uploads\/media_697999dadb01c6.63157521.webm","media_type":"audio","caption":null,"filename":"record_1769576919723.webm"}
[2026-01-28 00:14:59] Raw input: {"recipient":"573168950803","media_url":"http://localhost:8080/uploads/media_69799b51530038.79115661.webm","media_type":"audio","caption":null,"filename":"record_1769576919723.webm"}
[2026-01-28 00:14:59] Input decoded: {"recipient":"573168950803","media_url":"http:\/\/localhost:8080\/uploads\/media_69799b51530038.79115661.webm","media_type":"audio","caption":null,"filename":"record_1769576919723.webm"}
[2026-01-28 00:15:54] Raw input: {"recipient":"573168950803","media_url":"http://localhost:8080/uploads/media_69799b891e3c11.09600349.webm","media_type":"audio","caption":null,"filename":"record_1769577348725.webm"}
[2026-01-28 00:15:54] Input decoded: {"recipient":"573168950803","media_url":"http:\/\/localhost:8080\/uploads\/media_69799b891e3c11.09600349.webm","media_type":"audio","caption":null,"filename":"record_1769577348725.webm"}
[2026-01-28 00:17:05] Raw input: {"recipient":"573168950803","media_url":"http://localhost:8080/uploads/media_69799bcfb8b031.52674216.webm","media_type":"audio","caption":null,"filename":"record_1769577421621.webm"}
[2026-01-28 00:17:05] Input decoded: {"recipient":"573168950803","media_url":"http:\/\/localhost:8080\/uploads\/media_69799bcfb8b031.52674216.webm","media_type":"audio","caption":null,"filename":"record_1769577421621.webm"}
[2026-01-28 00:20:06] Raw input: {"recipient":"573168950803","media_url":"http://localhost:8080/uploads/media_69799c846ada20.81906873.webm","media_type":"audio","caption":null,"filename":"record_1769577601944.webm"}
[2026-01-28 00:20:06] Input decoded: {"recipient":"573168950803","media_url":"http:\/\/localhost:8080\/uploads\/media_69799c846ada20.81906873.webm","media_type":"audio","caption":null,"filename":"record_1769577601944.webm"}
[2026-01-28 00:21:20] Raw input: {"recipient":"573022548060","media_url":"http://localhost:8080/uploads/media_69799ccf1bb225.76804681.webm","media_type":"audio","caption":null,"filename":"record_1769577675811.webm"}
[2026-01-28 00:21:20] Input decoded: {"recipient":"573022548060","media_url":"http:\/\/localhost:8080\/uploads\/media_69799ccf1bb225.76804681.webm","media_type":"audio","caption":null,"filename":"record_1769577675811.webm"}
[2026-01-28 00:24:06] Raw input: {"recipient":"573168950803","media_url":"http://localhost:8080/uploads/media_69799d735ae469.91287547.ogg","media_type":"audio","caption":null,"filename":"record_1769577841134.webm"}
[2026-01-28 00:24:06] Input decoded: {"recipient":"573168950803","media_url":"http:\/\/localhost:8080\/uploads\/media_69799d735ae469.91287547.ogg","media_type":"audio","caption":null,"filename":"record_1769577841134.webm"}
[2026-01-28 00:24:07] Upload result: {"id":"903170332106418"}
[2026-01-28 00:24:07] Media ID obtained: 903170332106418
[2026-01-28 00:24:09] Send media by id response: {"messaging_product":"whatsapp","contacts":[{"input":"573168950803","wa_id":"573168950803"}],"messages":[{"id":"wamid.HBgMNTczMTY4OTUwODAzFQIAERgSMUU0QTg1RUMwMkE0OTUyMDM2AA=="}]}
[2026-01-28 00:24:09] WhatsApp response (successful): {"messaging_product":"whatsapp","contacts":[{"input":"573168950803","wa_id":"573168950803"}],"messages":[{"id":"wamid.HBgMNTczMTY4OTUwODAzFQIAERgSMUU0QTg1RUMwMkE0OTUyMDM2AA=="}]}
[2026-01-28 00:24:09] Saving to DB - content: record_1769577841134.webm, media_url: 903170332106418
[2026-01-28 00:27:47] Raw input: {"recipient":"573168950803","media_url":"http://localhost:8080/uploads/media_69799e51c56f53.56537824.ogg","media_type":"audio","caption":null,"filename":"record_1769578062431.webm"}
[2026-01-28 00:27:47] Input decoded: {"recipient":"573168950803","media_url":"http:\/\/localhost:8080\/uploads\/media_69799e51c56f53.56537824.ogg","media_type":"audio","caption":null,"filename":"record_1769578062431.webm"}
[2026-01-28 00:27:48] Upload result: {"id":"2590254688014534"}
[2026-01-28 00:27:49] Media ID obtained: 2590254688014534
[2026-01-28 00:27:49] Send media by id response: {"messaging_product":"whatsapp","contacts":[{"input":"573168950803","wa_id":"573168950803"}],"messages":[{"id":"wamid.HBgMNTczMTY4OTUwODAzFQIAERgSNzkwNEYzQTdFQjkwNjQ1NzQ2AA=="}]}
[2026-01-28 00:27:49] WhatsApp response (successful): {"messaging_product":"whatsapp","contacts":[{"input":"573168950803","wa_id":"573168950803"}],"messages":[{"id":"wamid.HBgMNTczMTY4OTUwODAzFQIAERgSNzkwNEYzQTdFQjkwNjQ1NzQ2AA=="}]}
[2026-01-28 00:27:50] Saving to DB - content: record_1769578062431.webm, media_url: 2590254688014534, local_file: uploads/media_69799e51c56f53.56537824.ogg
[2026-01-28 00:28:17] Raw input: {"recipient":"573168950803","media_url":"http://localhost:8080/uploads/media_69799e6fd863b7.25583571.jpg","media_type":"image","caption":null,"filename":"media-url.jpg"}
[2026-01-28 00:28:17] Input decoded: {"recipient":"573168950803","media_url":"http:\/\/localhost:8080\/uploads\/media_69799e6fd863b7.25583571.jpg","media_type":"image","caption":null,"filename":"media-url.jpg"}
[2026-01-28 00:28:19] Upload result: {"id":"1375898793748917"}
[2026-01-28 00:28:19] Media ID obtained: 1375898793748917
[2026-01-28 00:28:20] Send media by id response: {"messaging_product":"whatsapp","contacts":[{"input":"573168950803","wa_id":"573168950803"}],"messages":[{"id":"wamid.HBgMNTczMTY4OTUwODAzFQIAERgSREYxQzNCQUZGMTA3NEVBN0I5AA=="}]}
[2026-01-28 00:28:20] WhatsApp response (successful): {"messaging_product":"whatsapp","contacts":[{"input":"573168950803","wa_id":"573168950803"}],"messages":[{"id":"wamid.HBgMNTczMTY4OTUwODAzFQIAERgSREYxQzNCQUZGMTA3NEVBN0I5AA=="}]}
[2026-01-28 00:28:20] Saving to DB - content: media-url.jpg, media_url: 1375898793748917, local_file: uploads/media_69799e6fd863b7.25583571.jpg
[2026-01-28 00:30:22] Raw input: {"recipient":"573168950803","media_url":"http://localhost:8080/uploads/media_69799eec0f6949.58637457.ogg","media_type":"audio","caption":null,"filename":"record_1769578217833.webm"}
[2026-01-28 00:30:22] Input decoded: {"recipient":"573168950803","media_url":"http:\/\/localhost:8080\/uploads\/media_69799eec0f6949.58637457.ogg","media_type":"audio","caption":null,"filename":"record_1769578217833.webm"}
[2026-01-28 00:30:23] Upload result: {"id":"882480841336174"}
[2026-01-28 00:30:23] Media ID obtained: 882480841336174
[2026-01-28 00:30:24] Send media by id response: {"messaging_product":"whatsapp","contacts":[{"input":"573168950803","wa_id":"573168950803"}],"messages":[{"id":"wamid.HBgMNTczMTY4OTUwODAzFQIAERgSQTJBMDQwMTU1REUxRTBBMjI5AA=="}]}
[2026-01-28 00:30:24] WhatsApp response (successful): {"messaging_product":"whatsapp","contacts":[{"input":"573168950803","wa_id":"573168950803"}],"messages":[{"id":"wamid.HBgMNTczMTY4OTUwODAzFQIAERgSQTJBMDQwMTU1REUxRTBBMjI5AA=="}]}
[2026-01-28 00:30:25] Saving to DB - content: record_1769578217833.webm, media_url: 882480841336174, local_file: uploads/media_69799eec0f6949.58637457.ogg
[2026-01-28 00:30:42] Raw input: {"recipient":"573168950803","media_url":"http://localhost:8080/uploads/media_69799f01048d32.15387881.jpg","media_type":"image","caption":null,"filename":"media-url.jpg"}
[2026-01-28 00:30:42] Input decoded: {"recipient":"573168950803","media_url":"http:\/\/localhost:8080\/uploads\/media_69799f01048d32.15387881.jpg","media_type":"image","caption":null,"filename":"media-url.jpg"}
[2026-01-28 00:30:44] Upload result: {"id":"2331604630676404"}
[2026-01-28 00:30:44] Media ID obtained: 2331604630676404
[2026-01-28 00:30:44] Send media by id response: {"messaging_product":"whatsapp","contacts":[{"input":"573168950803","wa_id":"573168950803"}],"messages":[{"id":"wamid.HBgMNTczMTY4OTUwODAzFQIAERgSMzNCNjY1RUMyNTNCNEYzM0MwAA=="}]}
[2026-01-28 00:30:44] WhatsApp response (successful): {"messaging_product":"whatsapp","contacts":[{"input":"573168950803","wa_id":"573168950803"}],"messages":[{"id":"wamid.HBgMNTczMTY4OTUwODAzFQIAERgSMzNCNjY1RUMyNTNCNEYzM0MwAA=="}]}
[2026-01-28 00:30:45] Saving to DB - content: media-url.jpg, media_url: 2331604630676404, local_file: uploads/media_69799f01048d32.15387881.jpg
[2026-01-28 00:31:45] Raw input: {"recipient":"573022548060","media_url":"http://localhost:8080/uploads/media_69799f4019c754.81016370.jpg","media_type":"image","caption":null,"filename":"media-url.jpg"}
[2026-01-28 00:31:45] Input decoded: {"recipient":"573022548060","media_url":"http:\/\/localhost:8080\/uploads\/media_69799f4019c754.81016370.jpg","media_type":"image","caption":null,"filename":"media-url.jpg"}
[2026-01-28 00:31:47] Upload result: {"id":"1983961412192518"}
[2026-01-28 00:31:47] Media ID obtained: 1983961412192518
[2026-01-28 00:31:48] Send media by id response: {"messaging_product":"whatsapp","contacts":[{"input":"573022548060","wa_id":"573022548060"}],"messages":[{"id":"wamid.HBgMNTczMDIyNTQ4MDYwFQIAERgSMDcxNUZFNDM1RDA2RkUyQzdGAA=="}]}
[2026-01-28 00:31:48] WhatsApp response (successful): {"messaging_product":"whatsapp","contacts":[{"input":"573022548060","wa_id":"573022548060"}],"messages":[{"id":"wamid.HBgMNTczMDIyNTQ4MDYwFQIAERgSMDcxNUZFNDM1RDA2RkUyQzdGAA=="}]}
[2026-01-28 00:31:48] Saving to DB - content: media-url.jpg, media_url: 1983961412192518, local_file: uploads/media_69799f4019c754.81016370.jpg
[2026-01-28 00:36:06] Raw input: {"recipient":"573022548060","media_url":"http://localhost:8080/uploads/media_6979a044c17079.66378227.ogg","media_type":"audio","caption":null,"filename":"record_1769578561744.webm"}
[2026-01-28 00:36:06] Input decoded: {"recipient":"573022548060","media_url":"http:\/\/localhost:8080\/uploads\/media_6979a044c17079.66378227.ogg","media_type":"audio","caption":null,"filename":"record_1769578561744.webm"}
[2026-01-28 00:36:08] Upload result: {"id":"1551128883003002"}
[2026-01-28 00:36:08] Media ID obtained: 1551128883003002
[2026-01-28 00:36:09] Send media by id response: {"messaging_product":"whatsapp","contacts":[{"input":"573022548060","wa_id":"573022548060"}],"messages":[{"id":"wamid.HBgMNTczMDIyNTQ4MDYwFQIAERgSNjFFMjczMzcwMjdGRjZEMTIyAA=="}]}
[2026-01-28 00:36:09] WhatsApp response (successful): {"messaging_product":"whatsapp","contacts":[{"input":"573022548060","wa_id":"573022548060"}],"messages":[{"id":"wamid.HBgMNTczMDIyNTQ4MDYwFQIAERgSNjFFMjczMzcwMjdGRjZEMTIyAA=="}]}
[2026-01-28 00:36:09] Saving to DB - content: record_1769578561744.webm, media_url: 1551128883003002, local_file: uploads/media_6979a044c17079.66378227.ogg, local_thumb: null
[2026-01-28 00:36:27] Raw input: {"recipient":"573022548060","media_url":"http://localhost:8080/uploads/media_6979a05981b3e3.76655176.jpg","media_type":"image","caption":null,"filename":"media-url.jpg"}
[2026-01-28 00:36:27] Input decoded: {"recipient":"573022548060","media_url":"http:\/\/localhost:8080\/uploads\/media_6979a05981b3e3.76655176.jpg","media_type":"image","caption":null,"filename":"media-url.jpg"}
[2026-01-28 00:36:28] Upload result: {"id":"3480062748838248"}
[2026-01-28 00:36:28] Media ID obtained: 3480062748838248
[2026-01-28 00:36:29] Send media by id response: {"messaging_product":"whatsapp","contacts":[{"input":"573022548060","wa_id":"573022548060"}],"messages":[{"id":"wamid.HBgMNTczMDIyNTQ4MDYwFQIAERgSRDM2RDA3ODc3RTBBMkU2OTRDAA=="}]}
[2026-01-28 00:36:29] WhatsApp response (successful): {"messaging_product":"whatsapp","contacts":[{"input":"573022548060","wa_id":"573022548060"}],"messages":[{"id":"wamid.HBgMNTczMDIyNTQ4MDYwFQIAERgSRDM2RDA3ODc3RTBBMkU2OTRDAA=="}]}
[2026-01-28 00:36:29] Saving to DB - content: media-url.jpg, media_url: 3480062748838248, local_file: uploads/media_6979a05981b3e3.76655176.jpg, local_thumb: uploads/media_6979a05981b3e3.76655176.jpg
[2026-01-28 00:47:02] Raw input: {"recipient":"573022548060","media_url":"http://localhost:8080/uploads/media_6979a2d407f105.12184484.jpg","media_type":"image","caption":null,"filename":"media-url.jpg"}
[2026-01-28 00:47:02] Input decoded: {"recipient":"573022548060","media_url":"http:\/\/localhost:8080\/uploads\/media_6979a2d407f105.12184484.jpg","media_type":"image","caption":null,"filename":"media-url.jpg"}
[2026-01-28 00:47:03] Upload result: {"id":"1953117515284557"}
[2026-01-28 00:47:03] Media ID obtained: 1953117515284557
[2026-01-28 00:47:04] Send media by id response: {"messaging_product":"whatsapp","contacts":[{"input":"573022548060","wa_id":"573022548060"}],"messages":[{"id":"wamid.HBgMNTczMDIyNTQ4MDYwFQIAERgSRTNCREM3QzczMDkzRDA1MzJEAA=="}]}
[2026-01-28 00:47:04] WhatsApp response (successful): {"messaging_product":"whatsapp","contacts":[{"input":"573022548060","wa_id":"573022548060"}],"messages":[{"id":"wamid.HBgMNTczMDIyNTQ4MDYwFQIAERgSRTNCREM3QzczMDkzRDA1MzJEAA=="}]}
[2026-01-28 00:47:05] Saving to DB - content: media-url.jpg, media_url: 1953117515284557, local_file: uploads/media_6979a2d407f105.12184484.jpg, local_thumb: uploads/media_6979a2d407f105.12184484.jpg
[2026-01-28 00:51:04] Raw input: {"recipient":"573022548060","media_url":"http://localhost:8080/uploads/media_6979a3c5b236e8.04274010.ogg","media_type":"audio","caption":null,"filename":"record_1769579459198.webm"}
[2026-01-28 00:51:04] Input decoded: {"recipient":"573022548060","media_url":"http:\/\/localhost:8080\/uploads\/media_6979a3c5b236e8.04274010.ogg","media_type":"audio","caption":null,"filename":"record_1769579459198.webm"}
[2026-01-28 00:51:05] Upload result: {"id":"893454973089562"}
[2026-01-28 00:51:05] Media ID obtained: 893454973089562
[2026-01-28 00:51:06] Send media by id response: {"messaging_product":"whatsapp","contacts":[{"input":"573022548060","wa_id":"573022548060"}],"messages":[{"id":"wamid.HBgMNTczMDIyNTQ4MDYwFQIAERgSQUYzRjE5QjE3OUE3OEIwQzlFAA=="}]}
[2026-01-28 00:51:06] WhatsApp response (successful): {"messaging_product":"whatsapp","contacts":[{"input":"573022548060","wa_id":"573022548060"}],"messages":[{"id":"wamid.HBgMNTczMDIyNTQ4MDYwFQIAERgSQUYzRjE5QjE3OUE3OEIwQzlFAA=="}]}
[2026-01-28 00:51:06] Saving to DB - content: record_1769579459198.webm, media_url: 893454973089562, local_file: uploads/media_6979a3c5b236e8.04274010.ogg, local_thumb: null
[2026-01-28 00:54:15] Raw input: {"recipient":"573022548060","media_url":"http://localhost:8080/uploads/media_6979a485787858.65742730.ogg","media_type":"audio","caption":null,"filename":"record_1769579651401.webm"}
[2026-01-28 00:54:15] Input decoded: {"recipient":"573022548060","media_url":"http:\/\/localhost:8080\/uploads\/media_6979a485787858.65742730.ogg","media_type":"audio","caption":null,"filename":"record_1769579651401.webm"}
[2026-01-28 00:54:17] Upload result: {"id":"759892943831328"}
[2026-01-28 00:54:17] Media ID obtained: 759892943831328
[2026-01-28 00:54:18] Send media by id response: {"messaging_product":"whatsapp","contacts":[{"input":"573022548060","wa_id":"573022548060"}],"messages":[{"id":"wamid.HBgMNTczMDIyNTQ4MDYwFQIAERgSODM5NEYwRDc2NjMxQUFEMzc2AA=="}]}
[2026-01-28 00:54:18] WhatsApp response (successful): {"messaging_product":"whatsapp","contacts":[{"input":"573022548060","wa_id":"573022548060"}],"messages":[{"id":"wamid.HBgMNTczMDIyNTQ4MDYwFQIAERgSODM5NEYwRDc2NjMxQUFEMzc2AA=="}]}
[2026-01-28 00:54:18] Saving to DB - content: record_1769579651401.webm, media_url: 759892943831328, local_file: uploads/media_6979a485787858.65742730.ogg, local_thumb: null
[2026-01-28 00:59:18] Raw input: {"recipient":"573022548060","media_url":"http://localhost:8080/uploads/media_6979a5b4690f83.69035164.ogg","media_type":"audio","caption":null,"filename":"record_1769579952844.webm"}
[2026-01-28 00:59:18] Input decoded: {"recipient":"573022548060","media_url":"http:\/\/localhost:8080\/uploads\/media_6979a5b4690f83.69035164.ogg","media_type":"audio","caption":null,"filename":"record_1769579952844.webm"}
[2026-01-28 00:59:19] Upload result: {"id":"1334972502008596"}
[2026-01-28 00:59:19] Media ID obtained: 1334972502008596
[2026-01-28 00:59:20] Send media by id response: {"messaging_product":"whatsapp","contacts":[{"input":"573022548060","wa_id":"573022548060"}],"messages":[{"id":"wamid.HBgMNTczMDIyNTQ4MDYwFQIAERgSRTgyNEZEN0EwOTgzODI3RTlBAA=="}]}
[2026-01-28 00:59:20] WhatsApp response (successful): {"messaging_product":"whatsapp","contacts":[{"input":"573022548060","wa_id":"573022548060"}],"messages":[{"id":"wamid.HBgMNTczMDIyNTQ4MDYwFQIAERgSRTgyNEZEN0EwOTgzODI3RTlBAA=="}]}
[2026-01-28 00:59:21] Saving to DB - content: record_1769579952844.webm, media_url: 1334972502008596, local_file: uploads/media_6979a5b4690f83.69035164.ogg, local_thumb: null
[2026-01-28 01:16:06] Raw input: {"recipient":"573022548060","media_url":"http://localhost:8080/uploads/media_6979a9a3d39a72.62917885.ogg","media_type":"audio","caption":null,"filename":"record_1769580961330.webm"}
[2026-01-28 01:16:06] Input decoded: {"recipient":"573022548060","media_url":"http:\/\/localhost:8080\/uploads\/media_6979a9a3d39a72.62917885.ogg","media_type":"audio","caption":null,"filename":"record_1769580961330.webm"}
[2026-01-28 01:16:07] Upload result: {"id":"1601261651067176"}
[2026-01-28 01:16:07] Media ID obtained: 1601261651067176
[2026-01-28 01:16:08] Send media by id response: {"messaging_product":"whatsapp","contacts":[{"input":"573022548060","wa_id":"573022548060"}],"messages":[{"id":"wamid.HBgMNTczMDIyNTQ4MDYwFQIAERgSNEJDQ0YzQUZENTU4RjVERDI5AA=="}]}
[2026-01-28 01:16:08] WhatsApp response (successful): {"messaging_product":"whatsapp","contacts":[{"input":"573022548060","wa_id":"573022548060"}],"messages":[{"id":"wamid.HBgMNTczMDIyNTQ4MDYwFQIAERgSNEJDQ0YzQUZENTU4RjVERDI5AA=="}]}
[2026-01-28 01:16:08] Saving to DB - content: record_1769580961330.webm, media_url: 1601261651067176, local_file: uploads/media_6979a9a3d39a72.62917885.ogg, local_thumb: null
[2026-01-28 01:25:23] Raw input: {"recipient":"573022548060","media_url":"http://localhost:8080/uploads/media_6979abd12b8131.97353205.ogg","media_type":"audio","caption":null,"filename":"record_1769581518627.webm"}
[2026-01-28 01:25:23] Input decoded: {"recipient":"573022548060","media_url":"http:\/\/localhost:8080\/uploads\/media_6979abd12b8131.97353205.ogg","media_type":"audio","caption":null,"filename":"record_1769581518627.webm"}
[2026-01-28 01:25:24] Upload result: {"id":"1043532494648003"}
[2026-01-28 01:25:24] Media ID obtained: 1043532494648003
[2026-01-28 01:25:25] Send media by id response: {"messaging_product":"whatsapp","contacts":[{"input":"573022548060","wa_id":"573022548060"}],"messages":[{"id":"wamid.HBgMNTczMDIyNTQ4MDYwFQIAERgSMDE3NzA2RUU4QTcyMDYyQzZFAA=="}]}
[2026-01-28 01:25:25] WhatsApp response (successful): {"messaging_product":"whatsapp","contacts":[{"input":"573022548060","wa_id":"573022548060"}],"messages":[{"id":"wamid.HBgMNTczMDIyNTQ4MDYwFQIAERgSMDE3NzA2RUU4QTcyMDYyQzZFAA=="}]}
[2026-01-28 01:25:26] Saving to DB - content: record_1769581518627.webm, media_url: 1043532494648003, local_file: uploads/media_6979abd12b8131.97353205.ogg, local_thumb: null
[2026-01-28 01:49:36] Raw input: {"recipient":"573022548060","media_url":"http://localhost:8080/uploads/media_6979b17f07a0f4.63241756.mp4","media_type":"video","caption":null,"filename":"WhatsApp Video 2026-01-22 at 10.53.30.mp4"}
[2026-01-28 01:49:36] Input decoded: {"recipient":"573022548060","media_url":"http:\/\/localhost:8080\/uploads\/media_6979b17f07a0f4.63241756.mp4","media_type":"video","caption":null,"filename":"WhatsApp Video 2026-01-22 at 10.53.30.mp4"}
[2026-01-28 01:49:38] Upload result: {"id":"2079449202816102"}
[2026-01-28 01:49:38] Media ID obtained: 2079449202816102
[2026-01-28 01:49:39] Send media by id response: {"messaging_product":"whatsapp","contacts":[{"input":"573022548060","wa_id":"573022548060"}],"messages":[{"id":"wamid.HBgMNTczMDIyNTQ4MDYwFQIAERgSMjNGNDM0MzkxM0QxNURBODExAA=="}]}
[2026-01-28 01:49:39] WhatsApp response (successful): {"messaging_product":"whatsapp","contacts":[{"input":"573022548060","wa_id":"573022548060"}],"messages":[{"id":"wamid.HBgMNTczMDIyNTQ4MDYwFQIAERgSMjNGNDM0MzkxM0QxNURBODExAA=="}]}
[2026-01-28 01:49:39] Saving to DB - content: WhatsApp Video 2026-01-22 at 10.53.30.mp4, media_url: 2079449202816102, local_file: uploads/media_6979b17f07a0f4.63241756.mp4, local_thumb: uploads/media_6979b17f07a0f4.63241756.mp4
[2026-01-28 01:51:40] Raw input: {"recipient":"573022548060","media_url":"http://localhost:8080/uploads/media_6979b1fb55d5a8.95537352.png","media_type":"image","caption":null,"filename":"image.png"}
[2026-01-28 01:51:40] Input decoded: {"recipient":"573022548060","media_url":"http:\/\/localhost:8080\/uploads\/media_6979b1fb55d5a8.95537352.png","media_type":"image","caption":null,"filename":"image.png"}
[2026-01-28 01:51:42] Upload result: {"id":"928500006280411"}
[2026-01-28 01:51:42] Media ID obtained: 928500006280411
[2026-01-28 01:51:43] Send media by id response: {"messaging_product":"whatsapp","contacts":[{"input":"573022548060","wa_id":"573022548060"}],"messages":[{"id":"wamid.HBgMNTczMDIyNTQ4MDYwFQIAERgSMUM4NTYwOTc0QjBCQUJGQjBDAA=="}]}
[2026-01-28 01:51:43] WhatsApp response (successful): {"messaging_product":"whatsapp","contacts":[{"input":"573022548060","wa_id":"573022548060"}],"messages":[{"id":"wamid.HBgMNTczMDIyNTQ4MDYwFQIAERgSMUM4NTYwOTc0QjBCQUJGQjBDAA=="}]}
[2026-01-28 01:51:43] Saving to DB - content: image.png, media_url: 928500006280411, local_file: uploads/media_6979b1fb55d5a8.95537352.png, local_thumb: uploads/media_6979b1fb55d5a8.95537352.png
+28 -14
View File
@@ -63,9 +63,9 @@ try {
$allowedTypes = [
// Imágenes
'image/jpeg', 'image/jpg', 'image/png', 'image/webp',
// Videos
'video/mp4', 'video/3gpp', 'video/quicktime',
// Audios
// Videos (webm será convertido a mp4)
'video/mp4', 'video/3gpp', 'video/quicktime', 'video/webm',
// Audios (webm será convertido a ogg)
'audio/aac', 'audio/mp3', 'audio/mpeg', 'audio/ogg', 'audio/amr', 'audio/webm',
// Documentos
'application/pdf',
@@ -97,38 +97,52 @@ try {
throw new Exception('Error al guardar el archivo en el servidor');
}
// Si es audio WebM, intentar convertir a OGG/Opus (formatos más compatibles con WhatsApp)
// Convertir archivos WebM (audio y video) a formatos compatibles con WhatsApp
$converted = false;
$conversion_error = null;
if (strpos($fileType, 'audio/webm') === 0) {
if (strpos($fileType, 'audio/webm') === 0 || strpos($fileType, 'video/webm') === 0) {
// Evitar fatal si shell_exec está deshabilitado en el hosting
if (function_exists('shell_exec')) {
$ffmpeg = trim(@shell_exec('which ffmpeg 2>/dev/null'));
if ($ffmpeg) {
try {
if (strpos($fileType, 'audio/webm') === 0) {
// Audio WebM -> OGG/Opus
$convertedName = uniqid('media_', true) . '.ogg';
$convertedPath = $uploadDir . $convertedName;
// Convertir a Opus (bitrate moderado). Forzamos formato OGG, mono 48kHz y mapeamos la primera pista de audio.
// Esto ayuda a evitar contenedores/streams extra que pueden causar que el archivo suene como ruido.
$cmd = escapeshellcmd($ffmpeg) . ' -y -i ' . escapeshellarg($uploadPath) . ' -map 0:a:0 -c:a libopus -b:a 64k -vbr on -ar 48000 -ac 1 -f ogg ' . escapeshellarg($convertedPath) . ' 2>&1';
$newType = 'audio/ogg';
$logType = 'Audio';
} else {
// Video WebM -> MP4
$convertedName = uniqid('media_', true) . '.mp4';
$convertedPath = $uploadDir . $convertedName;
// Convertir a MP4 con H.264 (compatible con WhatsApp)
$cmd = escapeshellcmd($ffmpeg) . ' -y -i ' . escapeshellarg($uploadPath) . ' -c:v libx264 -preset fast -crf 23 -c:a aac -b:a 128k -movflags +faststart ' . escapeshellarg($convertedPath) . ' 2>&1';
$newType = 'video/mp4';
$logType = 'Video';
}
$out = @shell_exec($cmd);
// Añadir comprobación diagnóstica con "ffmpeg -i" sobre el archivo convertido para registrar detalles del stream
// Añadir comprobación diagnóstica con "ffmpeg -i" sobre el archivo convertido
$probe = @shell_exec(escapeshellcmd($ffmpeg) . ' -i ' . escapeshellarg($convertedPath) . ' 2>&1');
if (file_exists($convertedPath) && filesize($convertedPath) > 0) {
// Reemplazar archivo original por el convertido
@unlink($uploadPath);
$uploadPath = $convertedPath;
$uniqueName = $convertedName;
$fileType = 'audio/ogg';
$fileType = $newType;
$converted = true;
error_log('upload_media.php - Audio convertido a OGG/Opus: ' . $convertedName);
error_log("upload_media.php - $logType convertido: $convertedName");
error_log('upload_media.php - ffmpeg output: ' . substr($out ?: '', 0, 2000));
error_log('upload_media.php - ffmpeg probe:
' . substr($probe ?: '', 0, 2000));
error_log('upload_media.php - ffmpeg probe: ' . substr($probe ?: '', 0, 2000));
} else {
$conversion_error = 'Conversion failed or no output';
error_log('upload_media.php - Falló conversión con ffmpeg. Output: ' . substr($out ?: '', 0, 2000));
error_log('upload_media.php - ffmpeg probe after failure:\n' . substr($probe ?: '', 0, 2000));
error_log('upload_media.php - ffmpeg probe after failure: ' . substr($probe ?: '', 0, 2000));
}
} catch (Exception $ex) {
$conversion_error = 'Conversion exception: ' . $ex->getMessage();
@@ -136,11 +150,11 @@ try {
}
} else {
$conversion_error = 'ffmpeg not found';
error_log('upload_media.php - ffmpeg no encontrado, no se puede convertir audio/webm.');
error_log('upload_media.php - ffmpeg no encontrado, no se puede convertir webm.');
}
} else {
$conversion_error = 'shell_exec disabled';
error_log('upload_media.php - shell_exec disabled, no se puede convertir audio/webm.');
error_log('upload_media.php - shell_exec disabled, no se puede convertir webm.');
}
}
+157
View File
@@ -103,3 +103,160 @@
[2026-01-27 23:46:42] Graph API request to https://graph.facebook.com/v22.0/1217156256734654
[2026-01-27 23:46:42] Proxying Facebook media URL with Authorization to https://lookaside.fbsbx.com/whatsapp_business/attachments/?mid=1217156256734654&source=getMedia&ext=1769575902&hash=ARlgJj9uEQBbCft9SWvCsd7Muroo_d46iIYJwmdSwrQWLw
[2026-01-27 23:46:42] Resolved media URL from Graph for mid 2371342336638440: https://lookaside.fbsbx.com/whatsapp_business/attachments/?mid=2371342336638440&source=getMedia&ext=1769575902&hash=ARmC7xUghrqeNvaMeZgme9RMrbd771C1nty9RLOqJVItWA
[2026-01-28 00:02:21] Request: GET /api/version/media-url.php?id=3100504000137603&download=1 GET:{"id":"3100504000137603","download":"1"} POST:[]
[2026-01-28 00:02:21] Graph API request to https://graph.facebook.com/v22.0/3100504000137603
[2026-01-28 00:12:36] Request: GET /api/version/media-url.php?id=876668735219378 GET:{"id":"876668735219378"} POST:[]
[2026-01-28 00:12:36] Graph API request to https://graph.facebook.com/v22.0/876668735219378
[2026-01-28 00:12:36] Proxying Facebook media URL with Authorization to https://lookaside.fbsbx.com/whatsapp_business/attachments/?mid=876668735219378&source=getMedia&ext=1769577456&hash=ARmwFAN3qFOa9P44SrNAZBwNzLXLvix1Q6GCMlOZFsqrzA
[2026-01-28 00:21:12] Request: GET /api/version/media-url.php?id=1217156256734654 GET:{"id":"1217156256734654"} POST:[]
[2026-01-28 00:21:12] Graph API request to https://graph.facebook.com/v22.0/1217156256734654
[2026-01-28 00:21:12] Request: GET /api/version/media-url.php?url=https%3A%2F%2Flookaside.fbsbx.com%2Fwhatsapp_business%2Fattachments%2F%3Fmid%3D2371342336638440%26source%3Dwebhook%26ext%3D1769575796%26hash%3DARndVKdw1p0CCx3_DpTblR2aOVkb_W5mbTqGFPSOK77cjA GET:{"url":"https:\/\/lookaside.fbsbx.com\/whatsapp_business\/attachments\/?mid=2371342336638440&source=webhook&ext=1769575796&hash=ARndVKdw1p0CCx3_DpTblR2aOVkb_W5mbTqGFPSOK77cjA"} POST:[]
[2026-01-28 00:21:12] Proxying Facebook media URL with Authorization to https://lookaside.fbsbx.com/whatsapp_business/attachments/?mid=1217156256734654&source=getMedia&ext=1769577972&hash=ARmoanU8Nd36LwQ6xNz7eG63SBVvGepjczzi4M--a98BDg
[2026-01-28 00:21:12] Resolved media URL from Graph for mid 2371342336638440: https://lookaside.fbsbx.com/whatsapp_business/attachments/?mid=2371342336638440&source=getMedia&ext=1769577972&hash=ARl9BUAyWrmYag5aPXEyUWVVcM1w-zbjTOpuX5j7YfzG4A
[2026-01-28 00:31:03] Request: GET /api/version/media-url.php?url=https%3A%2F%2Flookaside.fbsbx.com%2Fwhatsapp_business%2Fattachments%2F%3Fmid%3D2371342336638440%26source%3Dwebhook%26ext%3D1769575796%26hash%3DARndVKdw1p0CCx3_DpTblR2aOVkb_W5mbTqGFPSOK77cjA GET:{"url":"https:\/\/lookaside.fbsbx.com\/whatsapp_business\/attachments\/?mid=2371342336638440&source=webhook&ext=1769575796&hash=ARndVKdw1p0CCx3_DpTblR2aOVkb_W5mbTqGFPSOK77cjA"} POST:[]
[2026-01-28 00:31:03] Request: GET /api/version/media-url.php?id=1217156256734654 GET:{"id":"1217156256734654"} POST:[]
[2026-01-28 00:31:03] Graph API request to https://graph.facebook.com/v22.0/1217156256734654
[2026-01-28 00:31:03] Resolved media URL from Graph for mid 2371342336638440: https://lookaside.fbsbx.com/whatsapp_business/attachments/?mid=2371342336638440&source=getMedia&ext=1769578563&hash=ARlRqx01CwGl-djJAkY2I4g-P5m7smHw95Wlm1kBJSiSDA
[2026-01-28 00:31:03] Proxying Facebook media URL with Authorization to https://lookaside.fbsbx.com/whatsapp_business/attachments/?mid=1217156256734654&source=getMedia&ext=1769578563&hash=ARmRofVHmPDUfJd1iAsyrPYbxenS2CgxlZKyZHE9Q76DqQ
[2026-01-28 00:31:18] Request: GET /api/version/media-url.php?url=https%3A%2F%2Flookaside.fbsbx.com%2Fwhatsapp_business%2Fattachments%2F%3Fmid%3D2371342336638440%26source%3Dwebhook%26ext%3D1769575796%26hash%3DARndVKdw1p0CCx3_DpTblR2aOVkb_W5mbTqGFPSOK77cjA GET:{"url":"https:\/\/lookaside.fbsbx.com\/whatsapp_business\/attachments\/?mid=2371342336638440&source=webhook&ext=1769575796&hash=ARndVKdw1p0CCx3_DpTblR2aOVkb_W5mbTqGFPSOK77cjA"} POST:[]
[2026-01-28 00:31:18] Request: GET /api/version/media-url.php?id=1217156256734654 GET:{"id":"1217156256734654"} POST:[]
[2026-01-28 00:31:18] Graph API request to https://graph.facebook.com/v22.0/1217156256734654
[2026-01-28 00:31:18] Resolved media URL from Graph for mid 2371342336638440: https://lookaside.fbsbx.com/whatsapp_business/attachments/?mid=2371342336638440&source=getMedia&ext=1769578578&hash=ARnVWo2U8tL_Yq7kNT0S7iXoCbZSIz9bLJkah-zCi3_8UA
[2026-01-28 00:31:18] Proxying Facebook media URL with Authorization to https://lookaside.fbsbx.com/whatsapp_business/attachments/?mid=1217156256734654&source=getMedia&ext=1769578578&hash=ARkDgAhqSzlVEdZj9OnaTFiddRAO4GN39Xpsd2XdUL3Ycw
[2026-01-28 00:31:52] Request: GET /api/version/media-url.php?url=https%3A%2F%2Flookaside.fbsbx.com%2Fwhatsapp_business%2Fattachments%2F%3Fmid%3D2371342336638440%26source%3Dwebhook%26ext%3D1769575796%26hash%3DARndVKdw1p0CCx3_DpTblR2aOVkb_W5mbTqGFPSOK77cjA GET:{"url":"https:\/\/lookaside.fbsbx.com\/whatsapp_business\/attachments\/?mid=2371342336638440&source=webhook&ext=1769575796&hash=ARndVKdw1p0CCx3_DpTblR2aOVkb_W5mbTqGFPSOK77cjA"} POST:[]
[2026-01-28 00:31:52] Resolved media URL from Graph for mid 2371342336638440: https://lookaside.fbsbx.com/whatsapp_business/attachments/?mid=2371342336638440&source=getMedia&ext=1769578612&hash=ARkn0okswwjwqUMNGWslpAzw_X5imoSyIt14oVH7j56AOw
[2026-01-28 00:32:15] Request: GET /api/version/media-url.php?id=1983961412192518 GET:{"id":"1983961412192518"} POST:[]
[2026-01-28 00:32:15] Graph API request to https://graph.facebook.com/v22.0/1983961412192518
[2026-01-28 00:32:15] Proxying Facebook media URL with Authorization to https://lookaside.fbsbx.com/whatsapp_business/attachments/?mid=1983961412192518&source=getMedia&ext=1769578635&hash=ARl8bnodHLay57oSohSgIppcpZ1x0NRoRzoeCufGANhUuA
[2026-01-28 00:35:55] Request: GET /api/version/media-url.php?url=https%3A%2F%2Flookaside.fbsbx.com%2Fwhatsapp_business%2Fattachments%2F%3Fmid%3D2371342336638440%26source%3Dwebhook%26ext%3D1769575796%26hash%3DARndVKdw1p0CCx3_DpTblR2aOVkb_W5mbTqGFPSOK77cjA GET:{"url":"https:\/\/lookaside.fbsbx.com\/whatsapp_business\/attachments\/?mid=2371342336638440&source=webhook&ext=1769575796&hash=ARndVKdw1p0CCx3_DpTblR2aOVkb_W5mbTqGFPSOK77cjA"} POST:[]
[2026-01-28 00:35:55] Request: GET /api/version/media-url.php?id=1217156256734654 GET:{"id":"1217156256734654"} POST:[]
[2026-01-28 00:35:55] Graph API request to https://graph.facebook.com/v22.0/1217156256734654
[2026-01-28 00:35:56] Resolved media URL from Graph for mid 2371342336638440: https://lookaside.fbsbx.com/whatsapp_business/attachments/?mid=2371342336638440&source=getMedia&ext=1769578856&hash=ARl46VfeoC_hpowfcJ8uwH4Bu6RKKGeMG-SdRCmDyEpAlg
[2026-01-28 00:35:56] Proxying Facebook media URL with Authorization to https://lookaside.fbsbx.com/whatsapp_business/attachments/?mid=1217156256734654&source=getMedia&ext=1769578856&hash=ARkbJHqldIyUkRCtH85bL0HgIoErQ8zBKtzpeuLl_Olxuw
[2026-01-28 00:36:12] Request: GET /api/version/media-url.php?url=https%3A%2F%2Flookaside.fbsbx.com%2Fwhatsapp_business%2Fattachments%2F%3Fmid%3D2371342336638440%26source%3Dwebhook%26ext%3D1769575796%26hash%3DARndVKdw1p0CCx3_DpTblR2aOVkb_W5mbTqGFPSOK77cjA GET:{"url":"https:\/\/lookaside.fbsbx.com\/whatsapp_business\/attachments\/?mid=2371342336638440&source=webhook&ext=1769575796&hash=ARndVKdw1p0CCx3_DpTblR2aOVkb_W5mbTqGFPSOK77cjA"} POST:[]
[2026-01-28 00:36:13] Resolved media URL from Graph for mid 2371342336638440: https://lookaside.fbsbx.com/whatsapp_business/attachments/?mid=2371342336638440&source=getMedia&ext=1769578873&hash=ARmQJn2gapAIIEOwHDt6s7kI1g0IHYc0x2ChKjKSU_R7FA
[2026-01-28 00:36:32] Request: GET /api/version/media-url.php?url=https%3A%2F%2Flookaside.fbsbx.com%2Fwhatsapp_business%2Fattachments%2F%3Fmid%3D2371342336638440%26source%3Dwebhook%26ext%3D1769575796%26hash%3DARndVKdw1p0CCx3_DpTblR2aOVkb_W5mbTqGFPSOK77cjA GET:{"url":"https:\/\/lookaside.fbsbx.com\/whatsapp_business\/attachments\/?mid=2371342336638440&source=webhook&ext=1769575796&hash=ARndVKdw1p0CCx3_DpTblR2aOVkb_W5mbTqGFPSOK77cjA"} POST:[]
[2026-01-28 00:36:33] Resolved media URL from Graph for mid 2371342336638440: https://lookaside.fbsbx.com/whatsapp_business/attachments/?mid=2371342336638440&source=getMedia&ext=1769578893&hash=ARmoYK93esbPfBmwtIcgZ_3J-9DQfmwnT_5A-SXbay5crw
[2026-01-28 00:36:59] Request: GET /api/version/media-url.php?id=3480062748838248 GET:{"id":"3480062748838248"} POST:[]
[2026-01-28 00:36:59] Graph API request to https://graph.facebook.com/v22.0/3480062748838248
[2026-01-28 00:36:59] Proxying Facebook media URL with Authorization to https://lookaside.fbsbx.com/whatsapp_business/attachments/?mid=3480062748838248&source=getMedia&ext=1769578919&hash=ARnnGogjEuOzgaECbYMz20tpgiudo_nyTmLtnXOI4vPgqw
[2026-01-28 00:39:29] Request: GET /api/version/media-url.php?id=1217156256734654 GET:{"id":"1217156256734654"} POST:[]
[2026-01-28 00:39:29] Graph API request to https://graph.facebook.com/v22.0/1217156256734654
[2026-01-28 00:39:30] Proxying Facebook media URL with Authorization to https://lookaside.fbsbx.com/whatsapp_business/attachments/?mid=1217156256734654&source=getMedia&ext=1769579070&hash=ARkKPaxFRIZGRlwW9mNRyF3oUAtq4Q-zr989V8MwIBzyLw
[2026-01-28 00:39:30] Request: GET /api/version/media-url.php?url=https%3A%2F%2Flookaside.fbsbx.com%2Fwhatsapp_business%2Fattachments%2F%3Fmid%3D2371342336638440%26source%3Dwebhook%26ext%3D1769575796%26hash%3DARndVKdw1p0CCx3_DpTblR2aOVkb_W5mbTqGFPSOK77cjA GET:{"url":"https:\/\/lookaside.fbsbx.com\/whatsapp_business\/attachments\/?mid=2371342336638440&source=webhook&ext=1769575796&hash=ARndVKdw1p0CCx3_DpTblR2aOVkb_W5mbTqGFPSOK77cjA"} POST:[]
[2026-01-28 00:39:30] Resolved media URL from Graph for mid 2371342336638440: https://lookaside.fbsbx.com/whatsapp_business/attachments/?mid=2371342336638440&source=getMedia&ext=1769579070&hash=ARmTcu9TN2oxQK2RIP960qWtzYwsbK9HNN56QveOc6GpIw
[2026-01-28 00:42:27] Request: GET /api/version/media-url.php?id=1217156256734654 GET:{"id":"1217156256734654"} POST:[]
[2026-01-28 00:42:27] Graph API request to https://graph.facebook.com/v22.0/1217156256734654
[2026-01-28 00:42:27] Proxying Facebook media URL with Authorization to https://lookaside.fbsbx.com/whatsapp_business/attachments/?mid=1217156256734654&source=getMedia&ext=1769579247&hash=ARkY9LAAA7csYlYr-SW3f3GlOKJnR9KGm_Ck3Q3YzUl07Q
[2026-01-28 00:42:28] Request: GET /api/version/media-url.php?url=https%3A%2F%2Flookaside.fbsbx.com%2Fwhatsapp_business%2Fattachments%2F%3Fmid%3D2371342336638440%26source%3Dwebhook%26ext%3D1769575796%26hash%3DARndVKdw1p0CCx3_DpTblR2aOVkb_W5mbTqGFPSOK77cjA GET:{"url":"https:\/\/lookaside.fbsbx.com\/whatsapp_business\/attachments\/?mid=2371342336638440&source=webhook&ext=1769575796&hash=ARndVKdw1p0CCx3_DpTblR2aOVkb_W5mbTqGFPSOK77cjA"} POST:[]
[2026-01-28 00:42:28] Resolved media URL from Graph for mid 2371342336638440: https://lookaside.fbsbx.com/whatsapp_business/attachments/?mid=2371342336638440&source=getMedia&ext=1769579248&hash=ARkP81S6ZYYxl_m5jEnb5ILAMfdGBHKRFQBckN9exlZYZA
[2026-01-28 00:46:44] Request: GET /api/version/media-url.php?id=1217156256734654 GET:{"id":"1217156256734654"} POST:[]
[2026-01-28 00:46:44] Graph API request to https://graph.facebook.com/v22.0/1217156256734654
[2026-01-28 00:46:44] Request: GET /api/version/media-url.php?url=https%3A%2F%2Flookaside.fbsbx.com%2Fwhatsapp_business%2Fattachments%2F%3Fmid%3D2371342336638440%26source%3Dwebhook%26ext%3D1769575796%26hash%3DARndVKdw1p0CCx3_DpTblR2aOVkb_W5mbTqGFPSOK77cjA GET:{"url":"https:\/\/lookaside.fbsbx.com\/whatsapp_business\/attachments\/?mid=2371342336638440&source=webhook&ext=1769575796&hash=ARndVKdw1p0CCx3_DpTblR2aOVkb_W5mbTqGFPSOK77cjA"} POST:[]
[2026-01-28 00:46:44] Resolved media URL from Graph for mid 2371342336638440: https://lookaside.fbsbx.com/whatsapp_business/attachments/?mid=2371342336638440&source=getMedia&ext=1769579504&hash=ARmTFmCOFSKbhtRfAK92DvtWo4GPZjeaeiJUsFy5hDbtEQ
[2026-01-28 00:46:45] Proxying Facebook media URL with Authorization to https://lookaside.fbsbx.com/whatsapp_business/attachments/?mid=1217156256734654&source=getMedia&ext=1769579505&hash=ARk0PxLzqiGSL5obxGtS5BU09ARd5uqK6XJKL1_Sw5WSRg
[2026-01-28 00:47:08] Request: GET /api/version/media-url.php?url=https%3A%2F%2Flookaside.fbsbx.com%2Fwhatsapp_business%2Fattachments%2F%3Fmid%3D2371342336638440%26source%3Dwebhook%26ext%3D1769575796%26hash%3DARndVKdw1p0CCx3_DpTblR2aOVkb_W5mbTqGFPSOK77cjA GET:{"url":"https:\/\/lookaside.fbsbx.com\/whatsapp_business\/attachments\/?mid=2371342336638440&source=webhook&ext=1769575796&hash=ARndVKdw1p0CCx3_DpTblR2aOVkb_W5mbTqGFPSOK77cjA"} POST:[]
[2026-01-28 00:47:08] Resolved media URL from Graph for mid 2371342336638440: https://lookaside.fbsbx.com/whatsapp_business/attachments/?mid=2371342336638440&source=getMedia&ext=1769579528&hash=ARmRbSE47_fis_BjpvpeQbA-00ywmKSUuZQjIAWvoAIlcQ
[2026-01-28 00:49:35] Request: GET /api/version/media-url.php?id=1217156256734654 GET:{"id":"1217156256734654"} POST:[]
[2026-01-28 00:49:35] Graph API request to https://graph.facebook.com/v22.0/1217156256734654
[2026-01-28 00:49:35] Request: GET /api/version/media-url.php?url=https%3A%2F%2Flookaside.fbsbx.com%2Fwhatsapp_business%2Fattachments%2F%3Fmid%3D2371342336638440%26source%3Dwebhook%26ext%3D1769575796%26hash%3DARndVKdw1p0CCx3_DpTblR2aOVkb_W5mbTqGFPSOK77cjA GET:{"url":"https:\/\/lookaside.fbsbx.com\/whatsapp_business\/attachments\/?mid=2371342336638440&source=webhook&ext=1769575796&hash=ARndVKdw1p0CCx3_DpTblR2aOVkb_W5mbTqGFPSOK77cjA"} POST:[]
[2026-01-28 00:49:35] Resolved media URL from Graph for mid 2371342336638440: https://lookaside.fbsbx.com/whatsapp_business/attachments/?mid=2371342336638440&source=getMedia&ext=1769579675&hash=ARkIayYGe-1jZ0mTJeA7M893JUhie6RGrFzeDetowshI5w
[2026-01-28 00:49:35] Proxying Facebook media URL with Authorization to https://lookaside.fbsbx.com/whatsapp_business/attachments/?mid=1217156256734654&source=getMedia&ext=1769579675&hash=ARnUJ8I9qM_rzdz7BWv-22oSEXi7SFVI5QGFjUwCHz_3kA
[2026-01-28 00:50:34] Request: GET /api/version/media-url.php?id=1551128883003002&download=1 GET:{"id":"1551128883003002","download":"1"} POST:[]
[2026-01-28 00:50:34] Graph API request to https://graph.facebook.com/v22.0/1551128883003002
[2026-01-28 00:51:09] Request: GET /api/version/media-url.php?url=https%3A%2F%2Flookaside.fbsbx.com%2Fwhatsapp_business%2Fattachments%2F%3Fmid%3D2371342336638440%26source%3Dwebhook%26ext%3D1769575796%26hash%3DARndVKdw1p0CCx3_DpTblR2aOVkb_W5mbTqGFPSOK77cjA GET:{"url":"https:\/\/lookaside.fbsbx.com\/whatsapp_business\/attachments\/?mid=2371342336638440&source=webhook&ext=1769575796&hash=ARndVKdw1p0CCx3_DpTblR2aOVkb_W5mbTqGFPSOK77cjA"} POST:[]
[2026-01-28 00:51:09] Resolved media URL from Graph for mid 2371342336638440: https://lookaside.fbsbx.com/whatsapp_business/attachments/?mid=2371342336638440&source=getMedia&ext=1769579769&hash=ARl25XtRezFQE4LNk2ltWGpUCH8fGtGyGhMjVSRFLbeVLg
[2026-01-28 00:53:47] Request: GET /api/version/media-url.php?id=1217156256734654 GET:{"id":"1217156256734654"} POST:[]
[2026-01-28 00:53:47] Request: GET /api/version/media-url.php?url=https%3A%2F%2Flookaside.fbsbx.com%2Fwhatsapp_business%2Fattachments%2F%3Fmid%3D2371342336638440%26source%3Dwebhook%26ext%3D1769575796%26hash%3DARndVKdw1p0CCx3_DpTblR2aOVkb_W5mbTqGFPSOK77cjA GET:{"url":"https:\/\/lookaside.fbsbx.com\/whatsapp_business\/attachments\/?mid=2371342336638440&source=webhook&ext=1769575796&hash=ARndVKdw1p0CCx3_DpTblR2aOVkb_W5mbTqGFPSOK77cjA"} POST:[]
[2026-01-28 00:53:47] Graph API request to https://graph.facebook.com/v22.0/1217156256734654
[2026-01-28 00:53:47] Resolved media URL from Graph for mid 2371342336638440: https://lookaside.fbsbx.com/whatsapp_business/attachments/?mid=2371342336638440&source=getMedia&ext=1769579927&hash=ARnKJqihLQvUOsIMvDxRl6_ToIWFolgiMwv0EpQtAce_WQ
[2026-01-28 00:53:47] Proxying Facebook media URL with Authorization to https://lookaside.fbsbx.com/whatsapp_business/attachments/?mid=1217156256734654&source=getMedia&ext=1769579927&hash=ARmluaFcjYAHj3AUqm9kT96xYB2wn-NMyVSa36eFyj6jkQ
[2026-01-28 00:54:22] Request: GET /api/version/media-url.php?url=https%3A%2F%2Flookaside.fbsbx.com%2Fwhatsapp_business%2Fattachments%2F%3Fmid%3D2371342336638440%26source%3Dwebhook%26ext%3D1769575796%26hash%3DARndVKdw1p0CCx3_DpTblR2aOVkb_W5mbTqGFPSOK77cjA GET:{"url":"https:\/\/lookaside.fbsbx.com\/whatsapp_business\/attachments\/?mid=2371342336638440&source=webhook&ext=1769575796&hash=ARndVKdw1p0CCx3_DpTblR2aOVkb_W5mbTqGFPSOK77cjA"} POST:[]
[2026-01-28 00:54:22] Resolved media URL from Graph for mid 2371342336638440: https://lookaside.fbsbx.com/whatsapp_business/attachments/?mid=2371342336638440&source=getMedia&ext=1769579962&hash=ARmKutEdaebPLIDdbo5NFxqUgqL_itap2Rs-929uRkbm1w
[2026-01-28 00:54:55] Request: GET /api/version/media-url.php?local=uploads%2Fmedia_6979a2d407f105.12184484.jpg GET:{"local":"uploads\/media_6979a2d407f105.12184484.jpg"} POST:[]
[2026-01-28 00:55:25] Request: GET /api/version/media-url.php?local=uploads%2Fmedia_6979a2d407f105.12184484.jpg GET:{"local":"uploads\/media_6979a2d407f105.12184484.jpg"} POST:[]
[2026-01-28 00:55:33] Request: GET /api/version/media-url.php?local=uploads%2Fmedia_6979a2d407f105.12184484.jpg GET:{"local":"uploads\/media_6979a2d407f105.12184484.jpg"} POST:[]
[2026-01-28 00:58:18] Request: GET /api/version/media-url.php?local=uploads%2Fmedia_6979a2d407f105.12184484.jpg GET:{"local":"uploads\/media_6979a2d407f105.12184484.jpg"} POST:[]
[2026-01-28 00:59:02] Request: GET /api/version/media-url.php?url=https%3A%2F%2Flookaside.fbsbx.com%2Fwhatsapp_business%2Fattachments%2F%3Fmid%3D2371342336638440%26source%3Dwebhook%26ext%3D1769575796%26hash%3DARndVKdw1p0CCx3_DpTblR2aOVkb_W5mbTqGFPSOK77cjA GET:{"url":"https:\/\/lookaside.fbsbx.com\/whatsapp_business\/attachments\/?mid=2371342336638440&source=webhook&ext=1769575796&hash=ARndVKdw1p0CCx3_DpTblR2aOVkb_W5mbTqGFPSOK77cjA"} POST:[]
[2026-01-28 00:59:02] Request: GET /api/version/media-url.php?id=1217156256734654 GET:{"id":"1217156256734654"} POST:[]
[2026-01-28 00:59:02] Graph API request to https://graph.facebook.com/v22.0/1217156256734654
[2026-01-28 00:59:02] Resolved media URL from Graph for mid 2371342336638440: https://lookaside.fbsbx.com/whatsapp_business/attachments/?mid=2371342336638440&source=getMedia&ext=1769580242&hash=ARlmivoTwZhpdMULc1VdeSk_2GQXSHx9_SXYGbF7AJ9Fag
[2026-01-28 00:59:02] Proxying Facebook media URL with Authorization to https://lookaside.fbsbx.com/whatsapp_business/attachments/?mid=1217156256734654&source=getMedia&ext=1769580242&hash=ARmenndhGQKTEctc9p9lc9F4cGFTr6IMx88N3iL_RuT3Zw
[2026-01-28 00:59:24] Request: GET /api/version/media-url.php?url=https%3A%2F%2Flookaside.fbsbx.com%2Fwhatsapp_business%2Fattachments%2F%3Fmid%3D2371342336638440%26source%3Dwebhook%26ext%3D1769575796%26hash%3DARndVKdw1p0CCx3_DpTblR2aOVkb_W5mbTqGFPSOK77cjA GET:{"url":"https:\/\/lookaside.fbsbx.com\/whatsapp_business\/attachments\/?mid=2371342336638440&source=webhook&ext=1769575796&hash=ARndVKdw1p0CCx3_DpTblR2aOVkb_W5mbTqGFPSOK77cjA"} POST:[]
[2026-01-28 00:59:25] Resolved media URL from Graph for mid 2371342336638440: https://lookaside.fbsbx.com/whatsapp_business/attachments/?mid=2371342336638440&source=getMedia&ext=1769580265&hash=ARnOU-1V45VtKJI2XzRFHD6BSyGBjUSTIqGJdBzs933bAw
[2026-01-28 01:03:12] Request: GET /api/version/media-url.php?id=1217156256734654 GET:{"id":"1217156256734654"} POST:[]
[2026-01-28 01:03:12] Graph API request to https://graph.facebook.com/v22.0/1217156256734654
[2026-01-28 01:03:12] Request: GET /api/version/media-url.php?url=https%3A%2F%2Flookaside.fbsbx.com%2Fwhatsapp_business%2Fattachments%2F%3Fmid%3D2371342336638440%26source%3Dwebhook%26ext%3D1769575796%26hash%3DARndVKdw1p0CCx3_DpTblR2aOVkb_W5mbTqGFPSOK77cjA GET:{"url":"https:\/\/lookaside.fbsbx.com\/whatsapp_business\/attachments\/?mid=2371342336638440&source=webhook&ext=1769575796&hash=ARndVKdw1p0CCx3_DpTblR2aOVkb_W5mbTqGFPSOK77cjA"} POST:[]
[2026-01-28 01:03:12] Proxying Facebook media URL with Authorization to https://lookaside.fbsbx.com/whatsapp_business/attachments/?mid=1217156256734654&source=getMedia&ext=1769580492&hash=ARn93MHvoEKXeawiU3sBI9u3xVB0plX3bNOc6FHj9WVFog
[2026-01-28 01:03:12] Resolved media URL from Graph for mid 2371342336638440: https://lookaside.fbsbx.com/whatsapp_business/attachments/?mid=2371342336638440&source=getMedia&ext=1769580492&hash=ARnWkfo-X22eZ-dKOhkni6yzUXhTOsS38uIW7LG8F_Grng
[2026-01-28 01:08:42] Request: GET /api/version/media-url.php?url=https%3A%2F%2Flookaside.fbsbx.com%2Fwhatsapp_business%2Fattachments%2F%3Fmid%3D2371342336638440%26source%3Dwebhook%26ext%3D1769575796%26hash%3DARndVKdw1p0CCx3_DpTblR2aOVkb_W5mbTqGFPSOK77cjA GET:{"url":"https:\/\/lookaside.fbsbx.com\/whatsapp_business\/attachments\/?mid=2371342336638440&source=webhook&ext=1769575796&hash=ARndVKdw1p0CCx3_DpTblR2aOVkb_W5mbTqGFPSOK77cjA"} POST:[]
[2026-01-28 01:08:42] Request: GET /api/version/media-url.php?id=1217156256734654 GET:{"id":"1217156256734654"} POST:[]
[2026-01-28 01:08:42] Graph API request to https://graph.facebook.com/v22.0/1217156256734654
[2026-01-28 01:08:42] Resolved media URL from Graph for mid 2371342336638440: https://lookaside.fbsbx.com/whatsapp_business/attachments/?mid=2371342336638440&source=getMedia&ext=1769580822&hash=ARmzjPWWs1VtMhON00noHMjb2S7Z2NzzMXQS6kRVWz_GMw
[2026-01-28 01:08:42] Proxying Facebook media URL with Authorization to https://lookaside.fbsbx.com/whatsapp_business/attachments/?mid=1217156256734654&source=getMedia&ext=1769580822&hash=ARmsky0v0oPn3oem400y-tAQQ6n44y-Pr5A0Fy4_ym08Sw
[2026-01-28 01:10:45] Request: GET /api/version/media-url.php?id=1217156256734654 GET:{"id":"1217156256734654"} POST:[]
[2026-01-28 01:10:45] Request: GET /api/version/media-url.php?url=https%3A%2F%2Flookaside.fbsbx.com%2Fwhatsapp_business%2Fattachments%2F%3Fmid%3D2371342336638440%26source%3Dwebhook%26ext%3D1769575796%26hash%3DARndVKdw1p0CCx3_DpTblR2aOVkb_W5mbTqGFPSOK77cjA GET:{"url":"https:\/\/lookaside.fbsbx.com\/whatsapp_business\/attachments\/?mid=2371342336638440&source=webhook&ext=1769575796&hash=ARndVKdw1p0CCx3_DpTblR2aOVkb_W5mbTqGFPSOK77cjA"} POST:[]
[2026-01-28 01:10:45] Graph API request to https://graph.facebook.com/v22.0/1217156256734654
[2026-01-28 01:10:45] Proxying Facebook media URL with Authorization to https://lookaside.fbsbx.com/whatsapp_business/attachments/?mid=1217156256734654&source=getMedia&ext=1769580945&hash=ARk25aum1ipZPmiCLuvdicHJb9ex0rx8OaT5Z0NPRd3GZw
[2026-01-28 01:10:45] Resolved media URL from Graph for mid 2371342336638440: https://lookaside.fbsbx.com/whatsapp_business/attachments/?mid=2371342336638440&source=getMedia&ext=1769580945&hash=ARmIfLoMEJXUbvwj3h6IgyLZDGarDY5dvQtTZtWbpqJYxA
[2026-01-28 01:15:57] Request: GET /api/version/media-url.php?url=https%3A%2F%2Flookaside.fbsbx.com%2Fwhatsapp_business%2Fattachments%2F%3Fmid%3D2371342336638440%26source%3Dwebhook%26ext%3D1769575796%26hash%3DARndVKdw1p0CCx3_DpTblR2aOVkb_W5mbTqGFPSOK77cjA GET:{"url":"https:\/\/lookaside.fbsbx.com\/whatsapp_business\/attachments\/?mid=2371342336638440&source=webhook&ext=1769575796&hash=ARndVKdw1p0CCx3_DpTblR2aOVkb_W5mbTqGFPSOK77cjA"} POST:[]
[2026-01-28 01:15:57] Request: GET /api/version/media-url.php?id=1217156256734654 GET:{"id":"1217156256734654"} POST:[]
[2026-01-28 01:15:57] Graph API request to https://graph.facebook.com/v22.0/1217156256734654
[2026-01-28 01:15:57] Resolved media URL from Graph for mid 2371342336638440: https://lookaside.fbsbx.com/whatsapp_business/attachments/?mid=2371342336638440&source=getMedia&ext=1769581257&hash=ARnrEU19RUEQsZDRQEZQS0vD9QujFwJ4sA02YqQJ-37OVg
[2026-01-28 01:15:57] Proxying Facebook media URL with Authorization to https://lookaside.fbsbx.com/whatsapp_business/attachments/?mid=1217156256734654&source=getMedia&ext=1769581257&hash=ARnlPUXu741I3wXzXm6Ns1UjsSVnJuh5AJaHUnDk7TuUug
[2026-01-28 01:16:11] Request: GET /api/version/media-url.php?url=https%3A%2F%2Flookaside.fbsbx.com%2Fwhatsapp_business%2Fattachments%2F%3Fmid%3D2371342336638440%26source%3Dwebhook%26ext%3D1769575796%26hash%3DARndVKdw1p0CCx3_DpTblR2aOVkb_W5mbTqGFPSOK77cjA GET:{"url":"https:\/\/lookaside.fbsbx.com\/whatsapp_business\/attachments\/?mid=2371342336638440&source=webhook&ext=1769575796&hash=ARndVKdw1p0CCx3_DpTblR2aOVkb_W5mbTqGFPSOK77cjA"} POST:[]
[2026-01-28 01:16:12] Resolved media URL from Graph for mid 2371342336638440: https://lookaside.fbsbx.com/whatsapp_business/attachments/?mid=2371342336638440&source=getMedia&ext=1769581272&hash=ARkFqWHyaasxAljVpFWiD6HLQKSQ4D_yd8JsDrVUREalQQ
[2026-01-28 01:18:55] Request: GET /api/version/media-url.php?id=1217156256734654 GET:{"id":"1217156256734654"} POST:[]
[2026-01-28 01:18:55] Graph API request to https://graph.facebook.com/v22.0/1217156256734654
[2026-01-28 01:18:55] Request: GET /api/version/media-url.php?url=https%3A%2F%2Flookaside.fbsbx.com%2Fwhatsapp_business%2Fattachments%2F%3Fmid%3D2371342336638440%26source%3Dwebhook%26ext%3D1769575796%26hash%3DARndVKdw1p0CCx3_DpTblR2aOVkb_W5mbTqGFPSOK77cjA GET:{"url":"https:\/\/lookaside.fbsbx.com\/whatsapp_business\/attachments\/?mid=2371342336638440&source=webhook&ext=1769575796&hash=ARndVKdw1p0CCx3_DpTblR2aOVkb_W5mbTqGFPSOK77cjA"} POST:[]
[2026-01-28 01:18:55] Proxying Facebook media URL with Authorization to https://lookaside.fbsbx.com/whatsapp_business/attachments/?mid=1217156256734654&source=getMedia&ext=1769581435&hash=ARnFdlxA1CZ7s6df99Lvrksxj0tPcjzREd7IzTEHuGfF2A
[2026-01-28 01:18:55] Resolved media URL from Graph for mid 2371342336638440: https://lookaside.fbsbx.com/whatsapp_business/attachments/?mid=2371342336638440&source=getMedia&ext=1769581435&hash=ARlLQ93XXsVGb4xTDoy6pa5bceRwLUBQ2Tb58-5qx8tcRA
[2026-01-28 01:19:33] Request: GET /api/version/media-url.php?id=1601261651067176&download=1 GET:{"id":"1601261651067176","download":"1"} POST:[]
[2026-01-28 01:19:33] Graph API request to https://graph.facebook.com/v22.0/1601261651067176
[2026-01-28 01:19:42] Request: GET /api/version/media-url.php?local=uploads%2Fmedia_6979a2d407f105.12184484.jpg GET:{"local":"uploads\/media_6979a2d407f105.12184484.jpg"} POST:[]
[2026-01-28 01:20:39] Request: GET /api/version/media-url.php?id=1217156256734654 GET:{"id":"1217156256734654"} POST:[]
[2026-01-28 01:20:39] Graph API request to https://graph.facebook.com/v22.0/1217156256734654
[2026-01-28 01:20:39] Proxying Facebook media URL with Authorization to https://lookaside.fbsbx.com/whatsapp_business/attachments/?mid=1217156256734654&source=getMedia&ext=1769581539&hash=ARn5A34U8V_qmSuZQJl7xYix7Lr-CC8DI-K36SgrcqJyRA
[2026-01-28 01:22:26] Request: GET /api/version/media-url.php?url=https%3A%2F%2Flookaside.fbsbx.com%2Fwhatsapp_business%2Fattachments%2F%3Fmid%3D2371342336638440%26source%3Dwebhook%26ext%3D1769575796%26hash%3DARndVKdw1p0CCx3_DpTblR2aOVkb_W5mbTqGFPSOK77cjA GET:{"url":"https:\/\/lookaside.fbsbx.com\/whatsapp_business\/attachments\/?mid=2371342336638440&source=webhook&ext=1769575796&hash=ARndVKdw1p0CCx3_DpTblR2aOVkb_W5mbTqGFPSOK77cjA"} POST:[]
[2026-01-28 01:22:26] Request: GET /api/version/media-url.php?id=1217156256734654 GET:{"id":"1217156256734654"} POST:[]
[2026-01-28 01:22:26] Graph API request to https://graph.facebook.com/v22.0/1217156256734654
[2026-01-28 01:22:26] Resolved media URL from Graph for mid 2371342336638440: https://lookaside.fbsbx.com/whatsapp_business/attachments/?mid=2371342336638440&source=getMedia&ext=1769581646&hash=ARnEOR2YXK9c-xUeehKuZrowGgZ5KIuC8VZjf2Xr0IIpAQ
[2026-01-28 01:22:26] Proxying Facebook media URL with Authorization to https://lookaside.fbsbx.com/whatsapp_business/attachments/?mid=1217156256734654&source=getMedia&ext=1769581646&hash=ARlhztt7jCFwChEJOM-PvQ47LBfjik-w6ljnvfuI9i8efw
[2026-01-28 01:24:47] Request: GET /api/version/media-url.php?id=1217156256734654 GET:{"id":"1217156256734654"} POST:[]
[2026-01-28 01:24:47] Graph API request to https://graph.facebook.com/v22.0/1217156256734654
[2026-01-28 01:24:47] Proxying Facebook media URL with Authorization to https://lookaside.fbsbx.com/whatsapp_business/attachments/?mid=1217156256734654&source=getMedia&ext=1769581787&hash=ARnzH0guEPfWRWU51ldsgxmZs966Qd8Qfle7rEW1nLggaQ
[2026-01-28 01:24:47] Request: GET /api/version/media-url.php?url=https%3A%2F%2Flookaside.fbsbx.com%2Fwhatsapp_business%2Fattachments%2F%3Fmid%3D2371342336638440%26source%3Dwebhook%26ext%3D1769575796%26hash%3DARndVKdw1p0CCx3_DpTblR2aOVkb_W5mbTqGFPSOK77cjA GET:{"url":"https:\/\/lookaside.fbsbx.com\/whatsapp_business\/attachments\/?mid=2371342336638440&source=webhook&ext=1769575796&hash=ARndVKdw1p0CCx3_DpTblR2aOVkb_W5mbTqGFPSOK77cjA"} POST:[]
[2026-01-28 01:24:47] Resolved media URL from Graph for mid 2371342336638440: https://lookaside.fbsbx.com/whatsapp_business/attachments/?mid=2371342336638440&source=getMedia&ext=1769581787&hash=ARnFw0RXJtxRRVYmhMD3oeFTJiGiMxb-G4tV3pBjNL6_SQ
[2026-01-28 01:25:29] Request: GET /api/version/media-url.php?url=https%3A%2F%2Flookaside.fbsbx.com%2Fwhatsapp_business%2Fattachments%2F%3Fmid%3D2371342336638440%26source%3Dwebhook%26ext%3D1769575796%26hash%3DARndVKdw1p0CCx3_DpTblR2aOVkb_W5mbTqGFPSOK77cjA GET:{"url":"https:\/\/lookaside.fbsbx.com\/whatsapp_business\/attachments\/?mid=2371342336638440&source=webhook&ext=1769575796&hash=ARndVKdw1p0CCx3_DpTblR2aOVkb_W5mbTqGFPSOK77cjA"} POST:[]
[2026-01-28 01:25:29] Resolved media URL from Graph for mid 2371342336638440: https://lookaside.fbsbx.com/whatsapp_business/attachments/?mid=2371342336638440&source=getMedia&ext=1769581829&hash=ARlRzKB6IF46_AlDA7o7gapnrQFJPQ8sDJgCHWB8iHILJQ
[2026-01-28 01:30:00] Request: GET /api/version/media-url.php?id=1217156256734654 GET:{"id":"1217156256734654"} POST:[]
[2026-01-28 01:30:00] Graph API request to https://graph.facebook.com/v22.0/1217156256734654
[2026-01-28 01:30:00] Request: GET /api/version/media-url.php?url=https%3A%2F%2Flookaside.fbsbx.com%2Fwhatsapp_business%2Fattachments%2F%3Fmid%3D2371342336638440%26source%3Dwebhook%26ext%3D1769575796%26hash%3DARndVKdw1p0CCx3_DpTblR2aOVkb_W5mbTqGFPSOK77cjA GET:{"url":"https:\/\/lookaside.fbsbx.com\/whatsapp_business\/attachments\/?mid=2371342336638440&source=webhook&ext=1769575796&hash=ARndVKdw1p0CCx3_DpTblR2aOVkb_W5mbTqGFPSOK77cjA"} POST:[]
[2026-01-28 01:30:00] Resolved media URL from Graph for mid 2371342336638440: https://lookaside.fbsbx.com/whatsapp_business/attachments/?mid=2371342336638440&source=getMedia&ext=1769582100&hash=ARkKkePqgcaQambyFaT-q7fmcItK9CRzre9LqZ99MY2tRQ
[2026-01-28 01:30:00] Proxying Facebook media URL with Authorization to https://lookaside.fbsbx.com/whatsapp_business/attachments/?mid=1217156256734654&source=getMedia&ext=1769582100&hash=ARkk8hsjClEX4fhebbWogS6yUSnR0tf6xibLB3OFl_m8yw
[2026-01-28 01:49:29] Request: GET /api/version/media-url.php?id=1217156256734654 GET:{"id":"1217156256734654"} POST:[]
[2026-01-28 01:49:29] Graph API request to https://graph.facebook.com/v22.0/1217156256734654
[2026-01-28 01:49:29] Request: GET /api/version/media-url.php?url=https%3A%2F%2Flookaside.fbsbx.com%2Fwhatsapp_business%2Fattachments%2F%3Fmid%3D2371342336638440%26source%3Dwebhook%26ext%3D1769575796%26hash%3DARndVKdw1p0CCx3_DpTblR2aOVkb_W5mbTqGFPSOK77cjA GET:{"url":"https:\/\/lookaside.fbsbx.com\/whatsapp_business\/attachments\/?mid=2371342336638440&source=webhook&ext=1769575796&hash=ARndVKdw1p0CCx3_DpTblR2aOVkb_W5mbTqGFPSOK77cjA"} POST:[]
[2026-01-28 01:49:29] Resolved media URL from Graph for mid 2371342336638440: https://lookaside.fbsbx.com/whatsapp_business/attachments/?mid=2371342336638440&source=getMedia&ext=1769583269&hash=ARlsuHrQo0NV1y3mLfDFwu6VKo6zw0Vft-xdrNbqwNDKSw
[2026-01-28 01:49:30] Proxying Facebook media URL with Authorization to https://lookaside.fbsbx.com/whatsapp_business/attachments/?mid=1217156256734654&source=getMedia&ext=1769583269&hash=ARl8a7X-B2EJZVDbLlIu-9SHY0U7d-qv_iJm7eG75GiYCA
[2026-01-28 01:49:42] Request: GET /api/version/media-url.php?url=https%3A%2F%2Flookaside.fbsbx.com%2Fwhatsapp_business%2Fattachments%2F%3Fmid%3D2371342336638440%26source%3Dwebhook%26ext%3D1769575796%26hash%3DARndVKdw1p0CCx3_DpTblR2aOVkb_W5mbTqGFPSOK77cjA GET:{"url":"https:\/\/lookaside.fbsbx.com\/whatsapp_business\/attachments\/?mid=2371342336638440&source=webhook&ext=1769575796&hash=ARndVKdw1p0CCx3_DpTblR2aOVkb_W5mbTqGFPSOK77cjA"} POST:[]
[2026-01-28 01:49:43] Resolved media URL from Graph for mid 2371342336638440: https://lookaside.fbsbx.com/whatsapp_business/attachments/?mid=2371342336638440&source=getMedia&ext=1769583283&hash=ARkFBlfW7vGkyhucCoWVgirgWMi4rSfVsXmiPIqExxtM8A
[2026-01-28 01:51:22] Request: GET /api/version/media-url.php?id=1217156256734654 GET:{"id":"1217156256734654"} POST:[]
[2026-01-28 01:51:22] Request: GET /api/version/media-url.php?url=https%3A%2F%2Flookaside.fbsbx.com%2Fwhatsapp_business%2Fattachments%2F%3Fmid%3D2371342336638440%26source%3Dwebhook%26ext%3D1769575796%26hash%3DARndVKdw1p0CCx3_DpTblR2aOVkb_W5mbTqGFPSOK77cjA GET:{"url":"https:\/\/lookaside.fbsbx.com\/whatsapp_business\/attachments\/?mid=2371342336638440&source=webhook&ext=1769575796&hash=ARndVKdw1p0CCx3_DpTblR2aOVkb_W5mbTqGFPSOK77cjA"} POST:[]
[2026-01-28 01:51:22] Graph API request to https://graph.facebook.com/v22.0/1217156256734654
[2026-01-28 01:51:22] Proxying Facebook media URL with Authorization to https://lookaside.fbsbx.com/whatsapp_business/attachments/?mid=1217156256734654&source=getMedia&ext=1769583382&hash=ARlNJc4-zhtmMDC6ZX5YvLG-QZlcNIjMX7IpNB6Z6YzdOA
[2026-01-28 01:51:22] Resolved media URL from Graph for mid 2371342336638440: https://lookaside.fbsbx.com/whatsapp_business/attachments/?mid=2371342336638440&source=getMedia&ext=1769583382&hash=ARlZV7UnYDg_jagmLF6YEbGh8v50rUfxYFEw-yLNBBgbHg
[2026-01-28 01:51:46] Request: GET /api/version/media-url.php?url=https%3A%2F%2Flookaside.fbsbx.com%2Fwhatsapp_business%2Fattachments%2F%3Fmid%3D2371342336638440%26source%3Dwebhook%26ext%3D1769575796%26hash%3DARndVKdw1p0CCx3_DpTblR2aOVkb_W5mbTqGFPSOK77cjA GET:{"url":"https:\/\/lookaside.fbsbx.com\/whatsapp_business\/attachments\/?mid=2371342336638440&source=webhook&ext=1769575796&hash=ARndVKdw1p0CCx3_DpTblR2aOVkb_W5mbTqGFPSOK77cjA"} POST:[]
[2026-01-28 01:51:47] Resolved media URL from Graph for mid 2371342336638440: https://lookaside.fbsbx.com/whatsapp_business/attachments/?mid=2371342336638440&source=getMedia&ext=1769583407&hash=ARnwqEl9qw2RK6Yd9gAXYj8TNbqqqMGIspUjWdE6nqhL1Q
[2026-01-28 01:52:04] Request: GET /api/version/media-url.php?url=https%3A%2F%2Flookaside.fbsbx.com%2Fwhatsapp_business%2Fattachments%2F%3Fmid%3D2371342336638440%26source%3Dwebhook%26ext%3D1769575796%26hash%3DARndVKdw1p0CCx3_DpTblR2aOVkb_W5mbTqGFPSOK77cjA GET:{"url":"https:\/\/lookaside.fbsbx.com\/whatsapp_business\/attachments\/?mid=2371342336638440&source=webhook&ext=1769575796&hash=ARndVKdw1p0CCx3_DpTblR2aOVkb_W5mbTqGFPSOK77cjA"} POST:[]
[2026-01-28 01:52:04] Resolved media URL from Graph for mid 2371342336638440: https://lookaside.fbsbx.com/whatsapp_business/attachments/?mid=2371342336638440&source=getMedia&ext=1769583424&hash=ARl_asnI_rejXyYQaCO7NAYFbfQpBDdRxmWzt-CmmJUrCg
[2026-01-28 02:00:11] Request: GET /api/version/media-url.php?id=1217156256734654 GET:{"id":"1217156256734654"} POST:[]
[2026-01-28 02:00:11] Graph API request to https://graph.facebook.com/v22.0/1217156256734654
[2026-01-28 02:00:11] Request: GET /api/version/media-url.php?url=https%3A%2F%2Flookaside.fbsbx.com%2Fwhatsapp_business%2Fattachments%2F%3Fmid%3D2371342336638440%26source%3Dwebhook%26ext%3D1769575796%26hash%3DARndVKdw1p0CCx3_DpTblR2aOVkb_W5mbTqGFPSOK77cjA GET:{"url":"https:\/\/lookaside.fbsbx.com\/whatsapp_business\/attachments\/?mid=2371342336638440&source=webhook&ext=1769575796&hash=ARndVKdw1p0CCx3_DpTblR2aOVkb_W5mbTqGFPSOK77cjA"} POST:[]
[2026-01-28 02:00:11] Proxying Facebook media URL with Authorization to https://lookaside.fbsbx.com/whatsapp_business/attachments/?mid=1217156256734654&source=getMedia&ext=1769583911&hash=ARkOiy7N3swvb64kubAAcPhyEaoXC5Pf96qQK8dlGjfZRw
[2026-01-28 02:00:11] Resolved media URL from Graph for mid 2371342336638440: https://lookaside.fbsbx.com/whatsapp_business/attachments/?mid=2371342336638440&source=getMedia&ext=1769583911&hash=ARnR5vosdyiSyrizuAG-OROPmxikGRc-vT-PPe2VbT13Xg
[2026-01-28 02:00:25] Request: GET /api/version/media-url.php?id=1217156256734654 GET:{"id":"1217156256734654"} POST:[]
[2026-01-28 02:00:25] Graph API request to https://graph.facebook.com/v22.0/1217156256734654
[2026-01-28 02:00:25] Request: GET /api/version/media-url.php?url=https%3A%2F%2Flookaside.fbsbx.com%2Fwhatsapp_business%2Fattachments%2F%3Fmid%3D2371342336638440%26source%3Dwebhook%26ext%3D1769575796%26hash%3DARndVKdw1p0CCx3_DpTblR2aOVkb_W5mbTqGFPSOK77cjA GET:{"url":"https:\/\/lookaside.fbsbx.com\/whatsapp_business\/attachments\/?mid=2371342336638440&source=webhook&ext=1769575796&hash=ARndVKdw1p0CCx3_DpTblR2aOVkb_W5mbTqGFPSOK77cjA"} POST:[]
[2026-01-28 02:00:26] Resolved media URL from Graph for mid 2371342336638440: https://lookaside.fbsbx.com/whatsapp_business/attachments/?mid=2371342336638440&source=getMedia&ext=1769583926&hash=ARkwnC8kELAgDSp4Qy8qvU3cY6trphM71K08Ebb3rQDeXg
[2026-01-28 02:00:26] Proxying Facebook media URL with Authorization to https://lookaside.fbsbx.com/whatsapp_business/attachments/?mid=1217156256734654&source=getMedia&ext=1769583926&hash=ARk42Fz7IB5FhjLHoVW-GF05cM0WjbFZU0NerKLOUo1W9g
+23 -6
View File
@@ -37,6 +37,8 @@ function isValidMediaUrl(u) {
function ensureProxyUrl(u, download = false) {
if (!isValidMediaUrl(u)) return '';
if (u.startsWith('/') || u.indexOf('/api/version/media-url.php') === 0) return u;
// Si empieza con api/ agregar / al inicio
if (u.startsWith('api/')) return '/' + u;
try {
const parsed = new URL(u);
const host = parsed.host.toLowerCase();
@@ -84,7 +86,8 @@ function renderMediaMessage(msg) {
// Full URL for opening (prefer local file then external with id or url)
let fullUrl = '';
if (isValidMediaUrl(localFile)) {
fullUrl = `/api/version/media-url.php?local=${encodeURIComponent(localFile)}`;
// Usar archivo local directamente sin proxy
fullUrl = localFile.startsWith('/') ? localFile : `/${localFile}`;
} else if (isValidMediaUrl(mediaExternal)) {
let mid = '';
if (mediaExternal.indexOf('api/get_media.php?id=') !== -1) {
@@ -108,16 +111,28 @@ function renderMediaMessage(msg) {
return `<div class="text-muted"><em>Imagen no disponible</em></div>`;
}
case 'video': {
const src = ensureProxyUrl(effective || msg.media_url || '');
// Priorizar local_file sobre otras fuentes
let src = '';
if (isValidMediaUrl(localFile)) {
src = localFile.startsWith('/') ? localFile : `/${localFile}`;
} else {
src = ensureProxyUrl(effective || msg.media_url || '');
}
if (src) return `<div class="message-media"><video controls><source src="${escapeHtml(src)}" type="video/mp4">Tu navegador no soporta video.</video></div>${content?`<div>${escapeHtml(content)}</div>`:''}`;
return `<div class="text-muted"><em>Video no disponible</em></div>`;
}
case 'audio': {
const src = ensureProxyUrl(effective || msg.media_url || '');
// Priorizar local_file sobre otras fuentes
let src = '';
if (isValidMediaUrl(localFile)) {
src = localFile.startsWith('/') ? localFile : `/${localFile}`;
} else {
src = ensureProxyUrl(effective || msg.media_url || '');
}
if (!src) return `<div class="text-muted"><em>Audio no disponible</em></div>`;
// build download link when possible (api/get_media.php?id=... or direct url or local file)
let audioDownload = null;
const candidate = isValidMediaUrl(effective) ? effective : (isValidMediaUrl(msg.media_url) ? msg.media_url : (isValidMediaUrl(localFile) ? localFile : null));
const candidate = isValidMediaUrl(localFile) ? localFile : (isValidMediaUrl(effective) ? effective : (isValidMediaUrl(msg.media_url) ? msg.media_url : null));
if (candidate && candidate.indexOf && candidate.indexOf('api/get_media.php?id=') !== -1) {
const parts = candidate.split('id=');
const mid = parts[1] ? parts[1] : '';
@@ -125,7 +140,8 @@ function renderMediaMessage(msg) {
} else if (candidate && /^https?:\/\//i.test(candidate)) {
audioDownload = `/api/version/media-url.php?url=${encodeURIComponent(candidate)}&download=1`;
} else if (candidate && candidate === localFile) {
audioDownload = `/api/version/media-url.php?local=${encodeURIComponent(candidate)}&download=1`;
// Usar archivo local directamente sin proxy
audioDownload = localFile.startsWith('/') ? localFile : `/${localFile}`;
}
let html = `<div class="message-media"><audio controls><source src="${escapeHtml(src)}" type="audio/mpeg">Tu navegador no soporta audio.</audio></div>`;
@@ -143,7 +159,8 @@ function renderMediaMessage(msg) {
} else if (isValidMediaUrl(mediaExternal) && /^https?:\/\//i.test(mediaExternal)) {
docUrl = `/api/version/media-url.php?url=${encodeURIComponent(mediaExternal)}&download=1`;
} else if (isValidMediaUrl(localFile)) {
docUrl = `/api/version/media-url.php?local=${encodeURIComponent(localFile)}&download=1`;
// Usar archivo local directamente sin proxy
docUrl = localFile.startsWith('/') ? localFile : `/${localFile}`;
}
if (docUrl) return `<a href="${docUrl}" class="message-document" target="_blank" rel="noopener noreferrer"><i class="fas fa-file-pdf"></i><span>${escapeHtml(filename)}</span></a>`;
return `<div class="message-document" style="opacity:0.6;"><i class="fas fa-file-pdf"></i><span>${escapeHtml(filename)} (No disponible)</span></div>`;
+6 -6
View File
@@ -5,12 +5,12 @@
* Fecha: 4 de enero de 2026 - Sistema de autenticación unificado
*/
// Configuración de la base de datos (compatible con cPanel)
define('DB_HOST', '77.37.126.126');
define('DB_PORT', '3306');
define('DB_NAME', 'usite_whatsapp_bot');
define('DB_USER', 'usite_whatsapp_user');
define('DB_PASS', '6q!Vio0fn@PFmsLyHjH5'); // Se generará automáticamente en la instalación
// Configuración de la base de datos - usa variables de entorno si están disponibles
define('DB_HOST', getenv('DB_HOST') ?: '77.37.126.126');
define('DB_PORT', getenv('DB_PORT') ?: '3306');
define('DB_NAME', getenv('DB_NAME') ?: 'usite_whatsapp_bot');
define('DB_USER', getenv('DB_USER') ?: 'usite_whatsapp_user');
define('DB_PASS', getenv('DB_PASS') ?: '6q!Vio0fn@PFmsLyHjH5');
define('DB_CHARSET', 'utf8mb4');
// Configuración de autenticación (LEGACY - migrado a BD)
+115 -58
View File
@@ -1425,6 +1425,9 @@ if (!isset($_SESSION['user_id'])) {
// Solo incrementar unread_count si no es la conversación activa
if (String(this.currentUserId) !== String(userId)) {
conv.unread_count = (conv.unread_count || 0) + 1;
} else {
// Si es la conversación activa, mantener unread_count en 0
conv.unread_count = 0;
}
// Mover al inicio de la lista
@@ -1445,8 +1448,7 @@ if (!isset($_SESSION['user_id'])) {
// Re-renderizar solo la lista de conversaciones
console.log('🔄 Re-renderizando lista de conversaciones...');
console.log('🔍 Verificando this:', this);
console.log('🔍 this.renderConversations existe?', typeof this.renderConversations);
console.log('🔍 Filtro activo:', this.conversationFilter);
console.log('🔍 this.conversations:', this.conversations);
if (typeof this.renderConversations === 'function') {
@@ -2252,8 +2254,14 @@ if (!isset($_SESSION['user_id'])) {
filterSelect.value = this.conversationFilter;
filterSelect.addEventListener('change', (e) => {
this.conversationFilter = e.target.value || 'all';
// reload conversations from first page
this.loadConversations(1, false);
console.log('🔍 Filtro cambiado a:', this.conversationFilter);
// Re-renderizar la lista actual sin recargar desde el servidor
this.renderConversations();
// También recargar desde el servidor para asegurar datos actualizados
// (pero esto es secundario y puede ser en background)
this.loadConversations(1, false).catch(err => {
console.warn('Error recargando conversaciones después de cambiar filtro:', err);
});
});
}
}
@@ -2505,8 +2513,16 @@ if (!isset($_SESSION['user_id'])) {
}
console.log('✅ Container encontrado:', container);
console.log('🔍 Filtro activo:', this.conversationFilter);
if (this.conversations.length === 0) {
// Aplicar filtro de no leídos
let conversationsToShow = this.conversations;
if (this.conversationFilter === 'unread') {
conversationsToShow = this.conversations.filter(c => (c.unread_count || 0) > 0);
console.log(`🔍 Filtro 'unread' aplicado: ${conversationsToShow.length} de ${this.conversations.length} conversaciones`);
}
if (conversationsToShow.length === 0) {
const emptyMsg = this.conversationFilter === 'unread' ? 'No hay conversaciones no leídas' : 'No hay conversaciones aún';
container.innerHTML = `
<div class="text-center p-4">
@@ -2518,13 +2534,35 @@ if (!isset($_SESSION['user_id'])) {
return;
}
console.log('📋 Generando HTML para', this.conversations.length, 'conversaciones');
console.log('📋 Generando HTML para', conversationsToShow.length, 'conversaciones');
// Ahora el backend devuelve por usuario: last_message, last_time, unread_count y avatar_url
const html = this.conversations.map(conv => {
const html = conversationsToShow.map(conv => {
const isActive = conv.user_id == this.currentUserId ? 'active' : '';
const time = this.formatTime(conv.last_time);
const preview = this.truncateText(conv.last_message || 'Sin mensajes', 50);
// Mostrar miniatura si el último mensaje es multimedia y tiene archivo local
let preview = '';
const hasLocalMedia = conv.last_local_file || conv.last_local_thumb;
const isMediaType = conv.message_type && ['image', 'video', 'audio', 'document'].includes(conv.message_type);
if (hasLocalMedia && isMediaType) {
const thumbSrc = conv.last_local_thumb || conv.last_local_file;
const mediaIcon = {
'image': '🖼️',
'video': '🎥',
'audio': '🎵',
'document': '📎'
}[conv.message_type] || '📎';
if (conv.message_type === 'image' || conv.message_type === 'video') {
preview = `<img src="/${thumbSrc}" alt="media" style="width:40px; height:40px; object-fit:cover; border-radius:4px; margin-right:8px; vertical-align:middle;"> ${mediaIcon} ${conv.last_message || 'Multimedia'}`;
} else {
preview = `${mediaIcon} ${conv.last_message || 'Archivo'}`;
}
} else {
preview = this.truncateText(conv.last_message || 'Sin mensajes', 50);
}
const avatar = conv.avatar_url ? `<img src="${conv.avatar_url}" alt="avatar" class="conversation-avatar-img">` : `<div class="conversation-avatar">${this.getInitials(conv.name)}</div>`;
@@ -2555,7 +2593,7 @@ if (!isset($_SESSION['user_id'])) {
console.log('📄 HTML generado, longitud:', html.length, 'caracteres');
// Detectar nuevas notificaciones: comparar unread counts previos
// Detectar nuevas notificaciones: comparar unread counts previos (usar todas las conversaciones, no solo las filtradas)
if (!this._prevConversations) this._prevConversations = {};
this.conversations.forEach(c => {
const prev = this._prevConversations[c.user_id] || { unread_count: 0 };
@@ -2690,6 +2728,14 @@ if (!isset($_SESSION['user_id'])) {
// Clear any staged messages from a previous conversation and hide indicator
try { if (this._stagedMessageIds) { this._stagedMessageIds.clear(); this._stagedCount = 0; this.hideNewMessagesIndicator(); } } catch(e) {}
// Actualizar inmediatamente el contador de no leídos a 0 en la lista local
const conv = this.conversations.find(c => c.user_id === userId);
if (conv) {
conv.unread_count = 0;
// Re-renderizar la lista para reflejar el cambio inmediatamente
this.renderConversations();
}
// Actualizar UI
document.getElementById('no-conversation').style.display = 'none';
document.getElementById('chat-area').style.display = 'flex';
@@ -2706,8 +2752,7 @@ if (!isset($_SESSION['user_id'])) {
});
document.querySelector(`[data-user-id="${userId}"]`)?.classList.add('active');
// Actualizar estado del toggle del bot según datos de la conversación
const conv = this.conversations.find(c => c.user_id === userId);
// Actualizar estado del toggle del bot según datos de la conversación (ya obtuvimos conv arriba)
if (conv) {
const btn = document.getElementById('bot-toggle');
const holdIndicator = document.getElementById('hold-indicator');
@@ -4744,6 +4789,7 @@ if (!isset($_SESSION['user_id'])) {
const uploadResponse = await fetch('api/upload_media.php', {
method: 'POST',
credentials: 'same-origin', // Enviar cookies de sesión
body: formData
});
const uploadContentType = uploadResponse.headers.get('content-type') || '';
@@ -4769,6 +4815,7 @@ if (!isset($_SESSION['user_id'])) {
const sendResponse = await fetch('api/send_media_message.php', {
method: 'POST',
credentials: 'same-origin', // Enviar cookies de sesión
headers: {
'Content-Type': 'application/json'
},
@@ -4834,9 +4881,13 @@ if (!isset($_SESSION['user_id'])) {
}
// Éxito
console.log('✅ Archivo enviado exitosamente, recargando mensajes...');
this.cancelMediaUpload();
await this.loadconversations(this.currentUserId, false);
// SSE actualizará las conversaciones automáticamente
// Recargar mensajes para mostrar el multimedia recién enviado
await this.loadMessages(this.currentUserId, true, true);
// SSE actualizará las conversaciones en la lista automáticamente
// this.loadConversations();
} catch (error) {
@@ -4867,27 +4918,60 @@ if (!isset($_SESSION['user_id'])) {
renderMediaMessage(message) {
if (!message) return '';
const mediaType = message.message_type || message.media_type || 'text';
const mediaUrl = message.media_url || message.media_url_external || '';
const mediaUrl = message.media_url || '';
const mediaUrlExternal = message.media_url_external || '';
const caption = message.content || '';
const localFile = message.local_file || '';
const localThumb = message.local_thumb || '';
const thumb = localThumb ? (`/${localThumb}`) : (localFile ? (`/${localFile}`) : (message.media_url_external || mediaUrl));
const messageId = message.id || message.message_id || '';
let full = '';
if (localFile) {
full = `/api/version/media-url.php?local=${encodeURIComponent(localFile)}`;
} else if (message.media_url_external) {
if (message.media_url_external.indexOf('api/get_media.php?id=') !== -1) {
const parts = message.media_url_external.split('id=');
const mid = parts[1] ? decodeURIComponent(parts[1]) : '';
full = `/api/version/media-url.php?id=${encodeURIComponent(mid)}`;
} else if (/^https?:\/\//i.test(message.media_url_external)) {
full = message.media_url_external;
} else {
full = `/api/version/media-url.php?url=${encodeURIComponent(message.media_url_external)}`;
// Debug completo del mensaje
if (mediaType === 'audio' || mediaType === 'image' || mediaType === 'video') {
console.log(`🎵 Media message (${mediaType}) - Datos completos:`, {
id: messageId,
localFile: localFile,
localThumb: localThumb,
mediaUrl: mediaUrl,
mediaUrlExternal: mediaUrlExternal,
hasLocalFile: !!localFile,
messageType: mediaType
});
}
// Prioridad: archivo local > proxy con DB id > media_url_external > URL externa
let full = '';
let thumb = '';
if (localFile) {
// ✅ PRIORIDAD 1: Archivo local guardado
full = `/${localFile}`;
thumb = localThumb ? `/${localThumb}` : `/${localFile}`;
console.log('✅ Usando archivo local:', full);
} else if (messageId && !mediaUrlExternal) {
// ⚠️ PRIORIDAD 2: Usar proxy con el ID de la base de datos solo si no hay media_url_external
full = `/api/version/media-url.php?id=${encodeURIComponent(messageId)}`;
thumb = full;
console.log('⚠️ No hay local_file, usando proxy con messageId:', messageId);
} else if (mediaUrlExternal) {
// 🔄 PRIORIDAD 3: Usar media_url_external (puede ser api/get_media.php o URL directa)
full = mediaUrlExternal.startsWith('/') ? mediaUrlExternal : `/${mediaUrlExternal}`;
thumb = full;
console.log('🔄 Usando media_url_external:', full);
} else if (mediaUrl && /^\d+$/.test(mediaUrl)) {
// 📱 PRIORIDAD 4: Es un ID de WhatsApp (solo números)
full = `/api/get_media.php?id=${encodeURIComponent(mediaUrl)}`;
thumb = full;
console.log('📱 Usando whatsapp_media_id con get_media.php:', mediaUrl);
} else if (mediaUrl && /^https?:\/\//i.test(mediaUrl)) {
// 🌐 PRIORIDAD 5: URL externa directa (puede estar caducada)
full = mediaUrl;
thumb = mediaUrl;
console.log('🌐 Usando URL externa:', full);
} else {
full = mediaUrl || '';
// ❌ Fallback: sin media disponible
console.warn('❌ No hay fuente de media disponible para el mensaje', messageId);
full = '';
thumb = '';
}
switch (mediaType) {
@@ -4913,22 +4997,8 @@ if (!isset($_SESSION['user_id'])) {
${caption ? `<div>${escapeHtml(caption)}</div>` : ''}
`;
case 'audio': {
let audioSrc = '';
if (localFile) {
audioSrc = `/${localFile}`;
} else if (message.media_url_external) {
if (message.media_url_external.indexOf('api/get_media.php?id=') !== -1) {
const parts = message.media_url_external.split('id=');
const mid = parts[1] ? decodeURIComponent(parts[1]) : '';
audioSrc = `/api/version/media-url.php?id=${encodeURIComponent(mid)}`;
} else if (/^https?:\/\//i.test(message.media_url_external)) {
audioSrc = message.media_url_external;
} else {
audioSrc = `/api/version/media-url.php?url=${encodeURIComponent(message.media_url_external)}`;
}
} else {
audioSrc = mediaUrl;
}
// Para audio, usar la misma lógica de full
let audioSrc = full;
return `
<div class="message-media">
<audio controls>
@@ -4939,20 +5009,7 @@ if (!isset($_SESSION['user_id'])) {
`;
}
case 'document': {
let docUrl = '';
if (localFile) {
docUrl = `/api/version/media-url.php?local=${encodeURIComponent(localFile)}&download=1`;
} else if (message.media_url_external) {
if (message.media_url_external.indexOf('api/get_media.php?id=') !== -1) {
const parts = message.media_url_external.split('id=');
const mid = parts[1] ? decodeURIComponent(parts[1]) : '';
docUrl = `/api/version/media-url.php?id=${encodeURIComponent(mid)}&download=1`;
} else if (/^https?:\/\//i.test(message.media_url_external)) {
docUrl = `/api/version/media-url.php?url=${encodeURIComponent(message.media_url_external)}&download=1`;
} else {
docUrl = `/api/version/media-url.php?url=${encodeURIComponent(message.media_url_external)}&download=1`;
}
}
let docUrl = full;
if (!docUrl) {
return `
<div class="message-document disabled">
+5
View File
@@ -0,0 +1,5 @@
# Netscape HTTP Cookie File
# https://curl.se/docs/http-cookies.html
# This file was generated by libcurl! Edit at your own risk.
#HttpOnly_localhost FALSE / FALSE 0 PHPSESSID daf8a8bcba369b9a7059dea53de6e56d
File diff suppressed because one or more lines are too long
Executable
+394
View File
@@ -0,0 +1,394 @@
#!/bin/bash
#############################################################
# DEPLOY SCRIPT PARA VPS - WhatsApp Bot
#
# Uso: ./deploy-vps.sh [opciones]
#
# Opciones:
# --install Primera instalación (instala Docker, etc)
# --update Solo actualizar código y reiniciar
# --ssl Generar/renovar certificados SSL
# --logs Ver logs de la aplicación
# --status Ver estado de los contenedores
#############################################################
set -e
# ==================== CONFIGURACIÓN ====================
# Modificar estos valores según tu VPS
DOMAIN="bot.u-s.app" # Tu dominio
EMAIL="admin@u-s.app" # Email para Let's Encrypt
APP_DIR="/var/www/whatsapp" # Directorio de la app en VPS
COMPOSE_FILE="docker-compose.prod.yml"
# Colores para output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# ==================== FUNCIONES ====================
log_info() {
echo -e "${BLUE}[INFO]${NC} $1"
}
log_success() {
echo -e "${GREEN}[OK]${NC} $1"
}
log_warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# Verificar que se ejecuta como root o con sudo
check_root() {
if [ "$EUID" -ne 0 ]; then
log_error "Este script debe ejecutarse como root o con sudo"
exit 1
fi
}
# Instalar Docker y Docker Compose
install_docker() {
log_info "Instalando Docker..."
# Verificar si Docker ya está instalado
if command -v docker &> /dev/null; then
log_success "Docker ya está instalado: $(docker --version)"
else
curl -fsSL https://get.docker.com | sh
systemctl enable docker
systemctl start docker
log_success "Docker instalado correctamente"
fi
# Verificar Docker Compose
if command -v docker-compose &> /dev/null || docker compose version &> /dev/null; then
log_success "Docker Compose disponible"
else
log_info "Instalando Docker Compose plugin..."
apt-get update
apt-get install -y docker-compose-plugin
log_success "Docker Compose instalado"
fi
}
# Instalar Certbot para SSL
install_certbot() {
log_info "Instalando Certbot para SSL..."
if command -v certbot &> /dev/null; then
log_success "Certbot ya está instalado"
else
apt-get update
apt-get install -y certbot
log_success "Certbot instalado"
fi
}
# Generar certificados SSL con Let's Encrypt
generate_ssl() {
log_info "Generando certificados SSL para ${DOMAIN}..."
# Detener contenedores que usan puerto 80
cd "$APP_DIR"
docker compose -f "$COMPOSE_FILE" down 2>/dev/null || true
# Generar certificado
certbot certonly \
--standalone \
--preferred-challenges http \
--agree-tos \
--email "$EMAIL" \
-d "$DOMAIN" \
--non-interactive
if [ -f "/etc/letsencrypt/live/${DOMAIN}/fullchain.pem" ]; then
log_success "Certificado SSL generado correctamente"
# Crear directorio y copiar certificados
mkdir -p "$APP_DIR/docker/ssl"
cp "/etc/letsencrypt/live/${DOMAIN}/fullchain.pem" "$APP_DIR/docker/ssl/server.crt"
cp "/etc/letsencrypt/live/${DOMAIN}/privkey.pem" "$APP_DIR/docker/ssl/server.key"
chmod 600 "$APP_DIR/docker/ssl/server.key"
log_success "Certificados copiados a docker/ssl/"
else
log_error "Error generando certificado SSL"
exit 1
fi
}
# Renovar certificados SSL
renew_ssl() {
log_info "Renovando certificados SSL..."
cd "$APP_DIR"
docker compose -f "$COMPOSE_FILE" stop app 2>/dev/null || true
certbot renew --standalone
# Copiar certificados actualizados
if [ -f "/etc/letsencrypt/live/${DOMAIN}/fullchain.pem" ]; then
cp "/etc/letsencrypt/live/${DOMAIN}/fullchain.pem" "$APP_DIR/docker/ssl/server.crt"
cp "/etc/letsencrypt/live/${DOMAIN}/privkey.pem" "$APP_DIR/docker/ssl/server.key"
log_success "Certificados renovados"
fi
docker compose -f "$COMPOSE_FILE" start app
}
# Configurar cron para renovación automática de SSL
setup_ssl_cron() {
log_info "Configurando renovación automática de SSL..."
CRON_CMD="0 3 * * * cd $APP_DIR && docker compose -f $COMPOSE_FILE stop app && certbot renew --quiet && docker compose -f $COMPOSE_FILE start app"
# Añadir al crontab si no existe
(crontab -l 2>/dev/null | grep -v "certbot renew"; echo "$CRON_CMD") | crontab -
log_success "Cron configurado para renovar SSL cada día a las 3am"
}
# Crear archivo .env si no existe
create_env_file() {
if [ ! -f "$APP_DIR/.env" ]; then
log_info "Creando archivo .env..."
cat > "$APP_DIR/.env" << 'EOF'
# ============================================
# CONFIGURACIÓN DE PRODUCCIÓN
# ============================================
# Base de datos
DB_HOST=77.37.126.126
DB_PORT=3306
DB_NAME=usite_whatsapp_bot
DB_USER=usite_whatsapp_user
DB_PASS=6q!Vio0fn@PFmsLyHjH5
# Redis (opcional, dejar vacío si no usa password)
REDIS_PASSWORD=
# WhatsApp API (obtener de Facebook Developer)
WHATSAPP_TOKEN=
WHATSAPP_PHONE_NUMBER_ID=
WEBHOOK_VERIFY_TOKEN=
EOF
log_warn "Archivo .env creado. ¡EDITA LAS CREDENCIALES antes de continuar!"
log_warn "Ejecuta: nano $APP_DIR/.env"
else
log_success "Archivo .env ya existe"
fi
}
# Crear directorios necesarios
create_directories() {
log_info "Creando directorios..."
mkdir -p "$APP_DIR/logs"
mkdir -p "$APP_DIR/uploads"
mkdir -p "$APP_DIR/docker/ssl"
# Permisos
chown -R www-data:www-data "$APP_DIR/logs" 2>/dev/null || true
chown -R www-data:www-data "$APP_DIR/uploads" 2>/dev/null || true
chmod -R 755 "$APP_DIR/logs"
chmod -R 755 "$APP_DIR/uploads"
log_success "Directorios creados"
}
# Construir y levantar contenedores
deploy() {
log_info "Desplegando aplicación..."
cd "$APP_DIR"
# Construir imágenes
log_info "Construyendo imágenes Docker..."
docker compose -f "$COMPOSE_FILE" build --no-cache
# Detener contenedores existentes
log_info "Deteniendo contenedores anteriores..."
docker compose -f "$COMPOSE_FILE" down --remove-orphans 2>/dev/null || true
# Levantar nuevos contenedores
log_info "Levantando contenedores..."
docker compose -f "$COMPOSE_FILE" up -d
# Esperar a que estén listos
log_info "Esperando a que los servicios estén listos..."
sleep 10
# Verificar estado
if docker compose -f "$COMPOSE_FILE" ps | grep -q "Up"; then
log_success "Contenedores ejecutándose correctamente"
else
log_error "Error iniciando contenedores"
docker compose -f "$COMPOSE_FILE" logs --tail=50
exit 1
fi
}
# Actualizar código y reiniciar
update() {
log_info "Actualizando aplicación..."
cd "$APP_DIR"
# Si es un repo git, hacer pull
if [ -d ".git" ]; then
log_info "Actualizando desde Git..."
git pull origin main || git pull origin master
fi
# Reconstruir y reiniciar
docker compose -f "$COMPOSE_FILE" build
docker compose -f "$COMPOSE_FILE" up -d
log_success "Aplicación actualizada"
}
# Ver logs
show_logs() {
cd "$APP_DIR"
docker compose -f "$COMPOSE_FILE" logs -f --tail=100
}
# Ver estado
show_status() {
cd "$APP_DIR"
echo ""
echo "==================== ESTADO DE CONTENEDORES ===================="
docker compose -f "$COMPOSE_FILE" ps
echo ""
echo "==================== USO DE RECURSOS ===================="
docker stats --no-stream --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}"
echo ""
echo "==================== VERIFICACIÓN DE SERVICIOS ===================="
# Verificar HTTP
HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" "http://localhost/health.php" 2>/dev/null || echo "ERROR")
if [ "$HTTP_STATUS" = "200" ]; then
log_success "HTTP: OK (status $HTTP_STATUS)"
else
log_error "HTTP: FALLO (status $HTTP_STATUS)"
fi
# Verificar HTTPS
HTTPS_STATUS=$(curl -sk -o /dev/null -w "%{http_code}" "https://localhost/health.php" 2>/dev/null || echo "ERROR")
if [ "$HTTPS_STATUS" = "200" ]; then
log_success "HTTPS: OK (status $HTTPS_STATUS)"
else
log_warn "HTTPS: $HTTPS_STATUS (puede necesitar certificados SSL)"
fi
echo ""
echo "==================== URLS ===================="
echo " App: https://${DOMAIN}"
echo " Health: https://${DOMAIN}/health.php"
}
# Primera instalación completa
full_install() {
check_root
echo ""
echo "=========================================="
echo " INSTALACIÓN COMPLETA - WhatsApp Bot"
echo "=========================================="
echo ""
install_docker
install_certbot
create_directories
create_env_file
echo ""
log_warn "ANTES DE CONTINUAR:"
log_warn "1. Edita el archivo .env con tus credenciales"
log_warn "2. Asegúrate que el dominio ${DOMAIN} apunta a este servidor"
echo ""
read -p "¿Continuar con el deploy? (s/n): " -n 1 -r
echo ""
if [[ $REPLY =~ ^[Ss]$ ]]; then
generate_ssl
setup_ssl_cron
deploy
show_status
else
log_info "Deploy cancelado. Ejecuta './deploy-vps.sh' cuando estés listo."
fi
}
# Mostrar ayuda
show_help() {
echo ""
echo "Uso: ./deploy-vps.sh [opción]"
echo ""
echo "Opciones:"
echo " --install Primera instalación completa"
echo " --update Actualizar código y reiniciar"
echo " --ssl Generar/renovar certificados SSL"
echo " --logs Ver logs en tiempo real"
echo " --status Ver estado de servicios"
echo " --help Mostrar esta ayuda"
echo ""
echo "Ejemplos:"
echo " sudo ./deploy-vps.sh --install # Primera vez"
echo " sudo ./deploy-vps.sh --update # Actualizar"
echo " ./deploy-vps.sh --logs # Ver logs"
echo ""
}
# ==================== MAIN ====================
case "${1:-}" in
--install)
full_install
;;
--update)
update
;;
--ssl)
check_root
generate_ssl
;;
--renew-ssl)
check_root
renew_ssl
;;
--logs)
show_logs
;;
--status)
show_status
;;
--help|-h)
show_help
;;
*)
# Sin argumentos, hacer deploy normal
if [ ! -d "$APP_DIR" ] || [ ! -f "$APP_DIR/$COMPOSE_FILE" ]; then
log_error "Directorio de aplicación no encontrado: $APP_DIR"
log_info "Para primera instalación usa: sudo ./deploy-vps.sh --install"
exit 1
fi
deploy
show_status
;;
esac
+69 -11
View File
@@ -2,6 +2,31 @@
# Uso: docker-compose -f docker-compose.dev.yml up -d
services:
# MySQL Database
mysql:
image: mysql:8.0
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}
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
networks:
- whatsapp-dev-network
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p${DB_ROOT_PASSWORD:-root_password_2026}"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
# Aplicación PHP con Nginx - MODO DEV
app:
build:
@@ -13,25 +38,28 @@ services:
restart: unless-stopped
working_dir: /var/www/html
ports:
- "8080:80" # Aplicación web
- "8080:80" # HTTP
- "8443:443" # HTTPS
- "9003:9003" # XDebug (para IDEs)
volumes:
# Hot-reload: cambios en código se reflejan inmediatamente
- ./:/var/www/html
- ./logs:/var/www/html/logs
- ./uploads:/var/www/html/uploads
- ./docker/nginx/ssl.conf:/etc/nginx/http.d/ssl.conf:ro
- ssl-certs:/etc/nginx/ssl
- php-socket:/run/php
environment:
# Entorno de desarrollo
- APP_ENV=development
- APP_DEBUG=true
# Base de datos
- DB_HOST=${DB_HOST}
# Base de datos - apuntar a MySQL local
- DB_HOST=mysql
- DB_PORT=${DB_PORT:-3306}
- DB_NAME=${DB_NAME}
- DB_USER=${DB_USER}
- DB_PASS=${DB_PASS}
- DB_NAME=${DB_NAME:-usite_whatsapp_bot}
- DB_USER=${DB_USER:-usite_whatsapp_user}
- DB_PASS=${DB_PASS:-6q!Vio0fn@PFmsLyHjH5}
# Redis
- REDIS_HOST=redis
@@ -62,6 +90,8 @@ services:
depends_on:
redis:
condition: service_healthy
mysql:
condition: service_healthy
networks:
- whatsapp-dev-network
healthcheck:
@@ -109,11 +139,11 @@ services:
environment:
- APP_ENV=development
- APP_DEBUG=true
- DB_HOST=${DB_HOST}
- DB_HOST=mysql
- DB_PORT=${DB_PORT:-3306}
- DB_NAME=${DB_NAME}
- DB_USER=${DB_USER}
- DB_PASS=${DB_PASS}
- DB_NAME=${DB_NAME:-usite_whatsapp_bot}
- DB_USER=${DB_USER:-usite_whatsapp_user}
- DB_PASS=${DB_PASS:-6q!Vio0fn@PFmsLyHjH5}
- REDIS_HOST=redis
- REDIS_PORT=6379
- REDIS_PASSWORD=${REDIS_PASSWORD:-}
@@ -123,6 +153,8 @@ services:
depends_on:
redis:
condition: service_healthy
mysql:
condition: service_healthy
networks:
- whatsapp-dev-network
command: php worker.php --daemon
@@ -161,14 +193,40 @@ services:
ports:
- "8083:8080"
environment:
- ADMINER_DEFAULT_SERVER=${DB_HOST}
- ADMINER_DEFAULT_SERVER=mysql
- ADMINER_DESIGN=nette
depends_on:
mysql:
condition: service_healthy
networks:
- whatsapp-dev-network
# phpMyAdmin - Gestor de base de datos alternativo
phpmyadmin:
image: phpmyadmin:latest
container_name: whatsapp-dev-phpmyadmin
restart: unless-stopped
ports:
- "8084:80"
environment:
PMA_HOST: mysql
PMA_PORT: 3306
PMA_USER: ${DB_USER:-usite_whatsapp_user}
PMA_PASSWORD: ${DB_PASS:-6q!Vio0fn@PFmsLyHjH5}
UPLOAD_LIMIT: 100M
depends_on:
mysql:
condition: service_healthy
networks:
- whatsapp-dev-network
volumes:
mysql-dev-data:
driver: local
redis-dev-data:
driver: local
ssl-certs:
driver: local
php-socket:
driver: local
+124
View File
@@ -0,0 +1,124 @@
# Docker Compose para PRODUCCIÓN
# Uso: docker-compose -f docker-compose.prod.yml up -d
services:
# Aplicación PHP con Nginx - MODO PRODUCCIÓN
app:
build:
context: .
dockerfile: Dockerfile
args:
- APP_ENV=production
container_name: whatsapp-app
restart: always
ports:
- "80:80"
- "443:443"
volumes:
- ./logs:/var/www/html/logs
- ./uploads:/var/www/html/uploads
- ./docker/nginx/ssl.conf:/etc/nginx/http.d/ssl.conf:ro
- /etc/letsencrypt:/etc/letsencrypt:ro
- ssl-certs:/etc/nginx/ssl
environment:
# Entorno
- APP_ENV=production
- APP_DEBUG=false
# Base de datos - usar base de datos externa (producción actual)
- DB_HOST=${DB_HOST:-77.37.126.126}
- DB_PORT=${DB_PORT:-3306}
- DB_NAME=${DB_NAME:-usite_whatsapp_bot}
- DB_USER=${DB_USER:-usite_whatsapp_user}
- DB_PASS=${DB_PASS}
# Redis
- REDIS_HOST=redis
- REDIS_PORT=6379
- REDIS_PASSWORD=${REDIS_PASSWORD:-}
# WhatsApp API
- WHATSAPP_TOKEN=${WHATSAPP_TOKEN}
- WHATSAPP_PHONE_NUMBER_ID=${WHATSAPP_PHONE_NUMBER_ID}
- WEBHOOK_VERIFY_TOKEN=${WEBHOOK_VERIFY_TOKEN}
# Configuración de producción
- LOG_LEVEL=WARNING
- ENABLE_RATE_LIMIT=true
- DISPLAY_ERRORS=0
- ERROR_REPORTING=0
# PHP optimizado para producción
- PHP_MEMORY_LIMIT=256M
- PHP_MAX_EXECUTION_TIME=60
- PHP_OPCACHE_ENABLE=1
depends_on:
redis:
condition: service_healthy
networks:
- whatsapp-network
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost/health.php"]
interval: 30s
timeout: 5s
retries: 3
start_period: 30s
# Redis para colas y caché
redis:
image: redis:7-alpine
container_name: whatsapp-redis
restart: always
command: >
redis-server
--appendonly yes
--maxmemory 128mb
--maxmemory-policy allkeys-lru
volumes:
- redis-data:/data
networks:
- whatsapp-network
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 3s
retries: 5
# Workers para procesamiento en background
worker:
build:
context: .
dockerfile: Dockerfile.worker
container_name: whatsapp-worker
restart: always
volumes:
- ./logs:/var/www/html/logs
environment:
- APP_ENV=production
- APP_DEBUG=false
- DB_HOST=${DB_HOST:-77.37.126.126}
- DB_PORT=${DB_PORT:-3306}
- DB_NAME=${DB_NAME:-usite_whatsapp_bot}
- DB_USER=${DB_USER:-usite_whatsapp_user}
- DB_PASS=${DB_PASS}
- REDIS_HOST=redis
- REDIS_PORT=6379
- REDIS_PASSWORD=${REDIS_PASSWORD:-}
- LOG_LEVEL=WARNING
depends_on:
redis:
condition: service_healthy
networks:
- whatsapp-network
command: php worker.php --daemon
volumes:
redis-data:
driver: local
ssl-certs:
driver: local
networks:
whatsapp-network:
driver: bridge
+7
View File
@@ -17,6 +17,11 @@ echo "📁 Creando directorios..."
mkdir -p /var/www/html/logs
mkdir -p /var/www/html/uploads
mkdir -p /run/php
mkdir -p /var/lib/nginx/tmp/client_body
mkdir -p /var/lib/nginx/tmp/proxy
mkdir -p /var/lib/nginx/tmp/fastcgi
mkdir -p /var/lib/nginx/tmp/uwsgi
mkdir -p /var/lib/nginx/tmp/scgi
# Establecer permisos
echo "🔐 Configurando permisos..."
@@ -24,6 +29,8 @@ chown -R www-data:www-data /var/www/html/logs
chown -R www-data:www-data /var/www/html/uploads
chmod -R 755 /var/www/html/logs
chmod -R 755 /var/www/html/uploads
chown -R nginx:nginx /var/lib/nginx/tmp
chmod -R 755 /var/lib/nginx/tmp
# Información de desarrollo
echo ""
+50 -10
View File
@@ -13,29 +13,69 @@ until redis-cli -h ${REDIS_HOST:-redis} ping 2>/dev/null; do
done
echo "✅ Redis conectado"
# Esperar a que MySQL esté disponible (solo si no es localhost ni IP interna)
if [ "${DB_HOST}" != "localhost" ] && [ "${DB_HOST}" != "127.0.0.1" ] && [ "${DB_HOST}" != "db" ]; then
echo "️ Usando base de datos externa (${DB_HOST}), omitiendo verificación de conectividad..."
else
echo "⏳ Esperando MySQL..."
until mysql -h ${DB_HOST:-db} -u ${DB_USER:-whatsapp_user} -p${DB_PASS} -e "SELECT 1" >/dev/null 2>&1; do
echo " MySQL no disponible, esperando..."
# Esperar a que MySQL esté disponible
if [ "${DB_HOST}" = "mysql" ] || [ "${DB_HOST}" = "db" ]; then
echo "⏳ Esperando MySQL local..."
MAX_RETRIES=30
RETRY=0
until mariadb -h "${DB_HOST:-mysql}" -u "${DB_USER:-whatsapp_user}" -p"${DB_PASS}" --skip-ssl -e "SELECT 1" >/dev/null 2>&1; do
RETRY=$((RETRY + 1))
if [ $RETRY -ge $MAX_RETRIES ]; then
echo "⚠️ MySQL no disponible después de $MAX_RETRIES intentos, continuando..."
break
fi
echo " MySQL no disponible, intento $RETRY/$MAX_RETRIES..."
sleep 2
done
if [ $RETRY -lt $MAX_RETRIES ]; then
echo "✅ MySQL conectado"
fi
elif [ "${DB_HOST}" != "localhost" ] && [ "${DB_HOST}" != "127.0.0.1" ]; then
echo "️ Usando base de datos externa (${DB_HOST}), omitiendo verificación de conectividad..."
fi
# Crear directorios si no existen
mkdir -p /var/www/html/logs
mkdir -p /var/www/html/uploads
mkdir -p /var/cache/nginx
mkdir -p /run/php
mkdir -p /var/lib/nginx/tmp/client_body
mkdir -p /var/lib/nginx/tmp/proxy
mkdir -p /var/lib/nginx/tmp/fastcgi
mkdir -p /var/lib/nginx/tmp/uwsgi
mkdir -p /var/lib/nginx/tmp/scgi
# Establecer permisos
# Establecer permisos ANTES de iniciar servicios
chown -R www:www /var/www/html/logs
chown -R www:www /var/www/html/uploads
chmod -R 755 /var/www/html/logs
chmod -R 755 /var/www/html/uploads
chmod -R 775 /var/www/html/logs
chmod -R 775 /var/www/html/uploads
# IMPORTANTE: nginx necesita acceso a sus directorios temporales
# Usar grupo nginx para compartir acceso entre nginx y www
chown -R nginx:nginx /var/lib/nginx
chmod -R 775 /var/lib/nginx/tmp
# Generar certificados SSL si no existen
SSL_DIR="/etc/nginx/ssl"
if [ ! -f "$SSL_DIR/server.crt" ] || [ ! -f "$SSL_DIR/server.key" ]; then
echo "🔐 Generando certificados SSL autofirmados..."
mkdir -p "$SSL_DIR"
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout "$SSL_DIR/server.key" \
-out "$SSL_DIR/server.crt" \
-subj "/C=CO/ST=Cundinamarca/L=Bogota/O=U-Site/OU=WhatsApp Bot/CN=bot.u-s.app" \
-addext "subjectAltName=DNS:bot.u-s.app,DNS:localhost,DNS:*.u-s.app,IP:127.0.0.1" 2>/dev/null || \
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout "$SSL_DIR/server.key" \
-out "$SSL_DIR/server.crt" \
-subj "/C=CO/ST=Cundinamarca/L=Bogota/O=U-Site/OU=WhatsApp Bot/CN=bot.u-s.app"
chmod 600 "$SSL_DIR/server.key"
chmod 644 "$SSL_DIR/server.crt"
echo "✅ Certificados SSL generados para bot.u-s.app"
else
echo "✅ Certificados SSL existentes encontrados"
fi
# Ejecutar migraciones de BD (si existen)
if [ -f /var/www/html/database/migrate.php ]; then
+10
View File
@@ -0,0 +1,10 @@
#!/bin/bash
# Script para corregir permisos después de que nginx inicie
sleep 2
# Corregir permisos de directorios nginx
chown -R nginx:nginx /var/lib/nginx
chmod -R 755 /var/lib/nginx/tmp
echo "✅ Permisos de nginx corregidos"
+1 -1
View File
@@ -1,4 +1,4 @@
user www;
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
+97
View File
@@ -0,0 +1,97 @@
# Configuración HTTPS para producción con Let's Encrypt
# Este archivo se monta en: /etc/nginx/http.d/ssl.conf
# Redirigir HTTP a HTTPS
server {
listen 80;
listen [::]:80;
server_name bot.u-s.app;
# Permitir verificación de Let's Encrypt
location /.well-known/acme-challenge/ {
root /var/www/html;
}
# Redirigir todo lo demás a HTTPS
location / {
return 301 https://$host$request_uri;
}
}
# HTTPS Server
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name bot.u-s.app;
# Certificados SSL de Let's Encrypt
ssl_certificate /etc/letsencrypt/live/bot.u-s.app/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/bot.u-s.app/privkey.pem;
# Configuración SSL moderna
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
# HSTS (descomentar en producción después de verificar que SSL funciona)
# add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
# Headers de seguridad
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
# Root y configuración PHP
root /var/www/html;
index index.php index.html;
# Tamaño máximo de upload
client_max_body_size 50M;
# Logs
access_log /var/log/nginx/ssl_access.log;
error_log /var/log/nginx/ssl_error.log;
# PHP-FPM
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass unix:/run/php/php-fpm.sock;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_param HTTPS on;
fastcgi_read_timeout 300;
}
# API endpoints
location /api/ {
try_files $uri $uri/ /api/index.php?$query_string;
}
# Webhook
location /webhook {
try_files $uri $uri/ /webhook.php?$query_string;
}
# Archivos estáticos - cache agresivo
location ~* \.(css|js|jpg|jpeg|png|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 30d;
add_header Cache-Control "public, immutable";
}
# Denegar acceso a archivos sensibles
location ~ /\. {
deny all;
}
location ~ /(config|logs|backups|database)/ {
deny all;
}
# Fallback
location / {
try_files $uri $uri/ /index.php?$query_string;
}
}
+185
View File
@@ -0,0 +1,185 @@
# Configuración SSL para bot.u-s.app
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name bot.u-s.app localhost;
root /var/www/html;
index index.php index.html;
# SSL Certificates
ssl_certificate /etc/nginx/ssl/server.crt;
ssl_certificate_key /etc/nginx/ssl/server.key;
# SSL Configuration
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets off;
# Modern TLS configuration
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
# Logs
access_log /var/log/nginx/whatsapp-ssl-access.log;
error_log /var/log/nginx/whatsapp-ssl-error.log warn;
# Aumentar timeouts para webhook
fastcgi_read_timeout 300;
fastcgi_send_timeout 300;
proxy_read_timeout 300;
proxy_send_timeout 300;
# Root location
location / {
try_files $uri $uri/ /index.php?$query_string;
}
# PHP files
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
# FastCGI buffers
fastcgi_buffers 16 16k;
fastcgi_buffer_size 32k;
# Importante para que webhook responda rápido
fastcgi_buffering off;
}
# Webhook endpoint
location ~ ^/api/webhook(_optimized)?\.php$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index webhook_optimized.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
fastcgi_buffering off;
fastcgi_request_buffering off;
fastcgi_read_timeout 60;
fastcgi_send_timeout 60;
add_header Cache-Control "no-store, no-cache, must-revalidate";
expires off;
}
# Health check endpoint
location /health.php {
fastcgi_pass 127.0.0.1:9000;
fastcgi_param SCRIPT_FILENAME $document_root/health.php;
include fastcgi_params;
access_log off;
}
# Static assets caching
location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2|ttf|eot)$ {
expires 30d;
add_header Cache-Control "public, immutable";
access_log off;
}
# Deny access to hidden files
location ~ /\. {
deny all;
access_log off;
log_not_found off;
}
# Deny access to sensitive files
location ~ /(?:composer\.json|composer\.lock|package\.json|\.env|\.git) {
deny all;
access_log off;
log_not_found off;
}
# Uploads directory
location ^~ /uploads/ {
alias /var/www/html/uploads/;
autoindex off;
# Security: solo permitir ciertos tipos de archivo
location ~* \.(php|phtml|php3|php4|php5|pl|py|cgi|sh)$ {
deny all;
}
}
# SSE endpoint
location ~ ^/api/sse.*\.php$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
fastcgi_buffering off;
fastcgi_cache off;
proxy_buffering off;
chunked_transfer_encoding off;
fastcgi_read_timeout 3600;
fastcgi_send_timeout 3600;
keepalive_timeout 3600;
add_header Content-Type text/event-stream;
add_header Cache-Control no-cache;
add_header X-Accel-Buffering no;
}
}
# Redirección HTTP a HTTPS
server {
listen 80;
listen [::]:80;
server_name _;
# Para desarrollo, mantener HTTP disponible
# Descomentar las siguientes líneas para forzar HTTPS:
# return 301 https://$host$request_uri;
# Por ahora, servir también en HTTP
root /var/www/html;
index index.php index.html;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
fastcgi_buffers 16 16k;
fastcgi_buffer_size 32k;
fastcgi_buffering off;
}
location /health.php {
fastcgi_pass 127.0.0.1:9000;
fastcgi_param SCRIPT_FILENAME $document_root/health.php;
include fastcgi_params;
access_log off;
}
location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2|ttf|eot)$ {
expires 30d;
add_header Cache-Control "public, immutable";
access_log off;
}
location ~ /\. {
deny all;
}
}
+2 -1
View File
@@ -21,7 +21,8 @@ php_admin_value[error_log] = /var/log/php-fpm-error.log
; Security
; Nota: curl_exec y curl_multi_exec son necesarios para WhatsApp API
php_admin_value[disable_functions] = exec,passthru,shell_exec,system,proc_open,popen,parse_ini_file,show_source
; shell_exec habilitado para conversión de medios con ffmpeg
php_admin_value[disable_functions] = exec,passthru,system,proc_open,popen,parse_ini_file,show_source
; Performance
request_terminate_timeout = 300s
+1
View File
@@ -20,6 +20,7 @@ date.timezone = America/Bogota
expose_php = Off
allow_url_fopen = On
allow_url_include = Off
disable_functions =
; Session
session.save_handler = redis
+34
View File
@@ -0,0 +1,34 @@
#!/bin/sh
# Script para generar certificados SSL autofirmados para desarrollo
SSL_DIR="/etc/nginx/ssl"
# Crear directorio si no existe
mkdir -p "$SSL_DIR"
# Verificar si ya existen los certificados
if [ -f "$SSL_DIR/server.crt" ] && [ -f "$SSL_DIR/server.key" ]; then
echo "✅ Certificados SSL ya existen en $SSL_DIR"
exit 0
fi
echo "🔐 Generando certificados SSL autofirmados para desarrollo..."
# Generar clave privada y certificado autofirmado
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout "$SSL_DIR/server.key" \
-out "$SSL_DIR/server.crt" \
-subj "/C=CO/ST=Cundinamarca/L=Bogota/O=WhatsApp Bot Dev/OU=Development/CN=localhost" \
-addext "subjectAltName=DNS:localhost,DNS:*.localhost,IP:127.0.0.1"
# Establecer permisos correctos
chmod 600 "$SSL_DIR/server.key"
chmod 644 "$SSL_DIR/server.crt"
echo "✅ Certificados SSL generados exitosamente:"
echo " - Certificado: $SSL_DIR/server.crt"
echo " - Clave: $SSL_DIR/server.key"
echo ""
echo "⚠️ NOTA: Estos son certificados autofirmados para DESARROLLO."
echo " Los navegadores mostrarán advertencias de seguridad."
echo " Para producción, usa certificados de Let's Encrypt u otra CA."
+28
View File
@@ -0,0 +1,28 @@
<?php
/**
* Script para actualizar local_thumb en mensajes existentes
* que tienen local_file pero no tienen local_thumb
*/
require_once __DIR__ . '/classes/Database.php';
try {
$db = new Database();
// Actualizar mensajes de tipo image/video que tienen local_file pero no local_thumb
$sql = "UPDATE conversations
SET local_thumb = local_file
WHERE message_type IN ('image', 'video')
AND local_file IS NOT NULL
AND local_file != ''
AND (local_thumb IS NULL OR local_thumb = '')";
$affected = $db->execute($sql);
echo "✅ Actualizados {$affected} registros\n";
echo "Los mensajes multimedia ahora usan sus archivos locales como miniatura\n";
} catch (Exception $e) {
echo "❌ Error: " . $e->getMessage() . "\n";
exit(1);
}
@@ -0,0 +1,7 @@
-- Migration: Add whatsapp_media_id column to conversations
-- This column stores the WhatsApp Media ID returned when uploading files
-- Date: 27 de enero de 2026
ALTER TABLE conversations
ADD COLUMN whatsapp_media_id VARCHAR(255) NULL AFTER media_url,
ADD INDEX idx_whatsapp_media (whatsapp_media_id);
+66
View File
@@ -0,0 +1,66 @@
#!/bin/bash
# Script para probar envío de archivos multimedia
# Verifica que se guarden correctamente en la BD
echo "🧪 Prueba de Envío de Archivos Multimedia"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
# Verificar estructura de BD
echo "1️⃣ Verificando columnas de la tabla conversations..."
docker exec whatsapp-dev-app php -r "
require 'config/config.php';
\$db = Database::getInstance();
\$cols = \$db->fetchAll('DESCRIBE conversations');
\$important = ['whatsapp_media_id', 'media_url', 'local_file', 'local_thumb', 'filename', 'mime_type'];
echo 'Columnas importantes:' . PHP_EOL;
foreach (\$cols as \$col) {
if (in_array(\$col['Field'], \$important)) {
echo ' ✅ ' . \$col['Field'] . ' (' . \$col['Type'] . ')' . PHP_EOL;
}
}
" || { echo "❌ Error verificando BD"; exit 1; }
echo ""
echo "2️⃣ Últimos 3 mensajes multimedia enviados:"
docker exec whatsapp-dev-app php -r "
require 'config/config.php';
\$db = Database::getInstance();
\$messages = \$db->fetchAll(
'SELECT id, user_id, message_type, content, media_url, whatsapp_media_id,
local_file, filename, created_at
FROM conversations
WHERE message_type IN (\"image\", \"video\", \"audio\", \"document\")
AND direction = \"outgoing\"
ORDER BY created_at DESC
LIMIT 3'
);
if (empty(\$messages)) {
echo ' ️ No hay mensajes multimedia en la BD aún' . PHP_EOL;
} else {
foreach (\$messages as \$msg) {
echo PHP_EOL;
echo ' 📎 ID: ' . \$msg['id'] . PHP_EOL;
echo ' Tipo: ' . \$msg['message_type'] . PHP_EOL;
echo ' Contenido: ' . substr(\$msg['content'], 0, 50) . PHP_EOL;
echo ' Media ID WA: ' . (\$msg['whatsapp_media_id'] ?: 'null') . PHP_EOL;
echo ' Archivo Local: ' . (\$msg['local_file'] ?: 'null') . PHP_EOL;
echo ' Fecha: ' . \$msg['created_at'] . PHP_EOL;
}
}
"
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "✅ Pruebas completadas"
echo ""
echo "📝 Cómo probar:"
echo " 1. Abre http://localhost:8080/conversations.php"
echo " 2. Selecciona una conversación"
echo " 3. Haz click en el botón adjuntar (📎)"
echo " 4. Selecciona una imagen o documento"
echo " 5. Envía el archivo"
echo " 6. Ejecuta este script de nuevo para ver si se guardó"
echo ""
+1
View File
@@ -0,0 +1 @@
<?php session_start(); $_SESSION["user_id"] = 1; ?>