97 lines
8.9 KiB
Markdown
97 lines
8.9 KiB
Markdown
# Contexto de Desarrollo y Arquitectura - Almeira Web
|
|
|
|
Este archivo sirve como memoria técnica e histórica para los desarrolladores y agentes de IA (como Antigravity) que continúen trabajando en este proyecto.
|
|
|
|
---
|
|
|
|
## 🛠️ Stack Tecnológico
|
|
* **Core**: Next.js 16 (App Router, con Server Actions habilitados).
|
|
* **Base de Datos y Backend**: Appwrite (Autenticación, Base de datos y Almacenamiento).
|
|
* **Editor de Contenido**: Tiptap Editor (Rich Text con soporte para imágenes, videos y embeds).
|
|
* **Estilos**: TailwindCSS.
|
|
* **Empaquetado**: Node standalone.
|
|
|
|
---
|
|
|
|
## 📂 Configuración del Entorno (`.env.local`)
|
|
Para levantar el proyecto en desarrollo local (`npm run dev`), se requiere un archivo `.env.local` en la raíz con el siguiente formato:
|
|
|
|
```env
|
|
NEXT_PUBLIC_APPWRITE_ENDPOINT=https://appwrite.almeira.com.co/v1
|
|
NEXT_PUBLIC_APPWRITE_PROJECT_ID=69b84df5003bd8957c5c
|
|
APPWRITE_API_KEY=standard_cce3cd... (Ver Gitea o Consola Appwrite)
|
|
APPWRITE_DATABASE_ID=almeira
|
|
NEXT_PUBLIC_APPWRITE_MEDIA_BUCKET_ID=media_bucket
|
|
|
|
# Evolution API (WhatsApp)
|
|
EVOLUTION_API_URL=https://evolution.almeira.com.co
|
|
EVOLUTION_API_KEY=mgXxu2eA...
|
|
EVOLUTION_INSTANCE_NAME=AutomataAlmeira
|
|
|
|
# Server Config
|
|
SERVER_ACTIONS_BODY_SIZE_LIMIT=100mb
|
|
```
|
|
|
|
---
|
|
|
|
## 🎬 Solución del Reproductor de Video (Hito Clave)
|
|
Durante el desarrollo, se resolvió un problema crítico donde los videos subidos a las publicaciones no se reproducían en producción:
|
|
|
|
### 1. El Origen del Problema
|
|
Appwrite utiliza el endpoint `/preview` para imágenes y miniaturas. Sin embargo, para archivos de video, el navegador requiere soporte de reproducción por rangos (Range Requests) y streaming. El endpoint `/preview` limita la reproducción y bloquea el video en Safari y Chrome. La solución es usar el endpoint `/view`.
|
|
|
|
### 2. Implementación de la Corrección
|
|
* **En el Editor (`RichTextEditor.tsx` / `MediaGridSelector.tsx`)**: Se configuró la biblioteca de medios y el nodo de video de Tiptap para reemplazar automáticamente `/preview?` por `/view?` en las URLs de origen de los videos.
|
|
* **En el Servidor (`entities.ts`)**: Se actualizó el validador `safeHtmlSchema` que limpia el código HTML con `DOMPurify` para permitir las etiquetas `<video>` y `<source>`, junto a los atributos `controls`, `src`, `muted`, `loop`, `autoplay`, `preload` y `poster`. Además, normaliza cualquier URL de video errónea antes de guardarla en la base de datos.
|
|
* **En la Publicación (`[slug]/page.tsx`)**: Se implementó una mejora dinámica que extrae el primer video del contenido y lo coloca como **Hero Media** en la cabecera (usando la imagen destacada como `poster` de portada), eliminando el video redundante del cuerpo del texto. También normaliza URLs antiguas al vuelo.
|
|
|
|
---
|
|
|
|
## 🔄 Scripts de Despliegue y Compilación
|
|
* **`package-hostinger.sh`**: Script Bash que compila el proyecto en modo `standalone`, copia los assets estáticos de `.next/static` y `public/`, inyecta la configuración para PM2 (`ecosystem.config.js`), configura el `package.json` para que Hostinger corra directamente `server.js` y comprime todo en un archivo zip listo para subir: **`almeira-web-deploy.zip`**.
|
|
|
|
### Comando para empaquetar:
|
|
```bash
|
|
PATH="/home/carlos/.nvm/versions/node/v22.14.0/bin:$PATH" bash package-hostinger.sh
|
|
```
|
|
|
|
---
|
|
|
|
## 💡 Instrucciones para Agentes de IA (Continuar Trabajo)
|
|
Si eres un asistente de codificación de IA (como Antigravity), ten en cuenta lo siguiente al iniciar:
|
|
1. Este proyecto utiliza Next.js standalone. Cualquier cambio en las rutas o componentes debe comprobarse compilando el proyecto localmente.
|
|
2. La sanitización de HTML es muy estricta. Si necesitas añadir nuevos tags interactivos a las publicaciones, debes agregarlos al whitelist de `safeHtmlSchema` en `src/app/actions/entities.ts` y al sanitizador en `src/app/[slug]/page.tsx`.
|
|
|
|
---
|
|
|
|
## 🏛️ Historial de Hitos Técnicos y Errores Resueltos (Logs de Sesiones)
|
|
|
|
A lo largo del desarrollo de la plataforma, se han resuelto varios problemas críticos de arquitectura y renderizado. A continuación, se detallan para evitar regresiones:
|
|
|
|
### 1. Error de Serialización de Componentes Servidor a Cliente ("Only plain objects...")
|
|
* **Problema**: Al intentar pasar colecciones de documentos directamente desde las Server Actions o Server Components de Next.js a componentes cliente (como `NewsCard` o `GlobalSidebar`), Next.js arrojaba el error: *"Only plain objects can be passed to Client Components"*. Esto ocurría porque los SDKs de Appwrite y ciertas funciones devuelven instancias de clase complejas u objetos con métodos prototípicos no serializables.
|
|
* **Solución**: Se implementó una política estricta de **desestructuración y serialización profunda** en los flujos de datos. En las Server Actions (por ejemplo, en `src/app/actions/entities.ts` y al consultar datos en `Home`), se aplica un parseo profundo `JSON.parse(JSON.stringify(data))` o mapeo explícito de propiedades antes de retornar los datos al cliente.
|
|
|
|
### 2. Resolución de IDs de Media y Logotipo Corporativo Roto
|
|
* **Problema**: El logotipo corporativo y varios recursos multimedia en la interfaz fallaban en renderizarse de forma intermitente debido a inconsistencias al resolver los IDs de documentos de Appwrite frente a los IDs reales de los archivos en Storage.
|
|
* **Solución**: Se independizó la tabla de base de datos `media` de Appwrite de los IDs de archivo reales del Storage. Se ajustó el helper `getAppwriteImageUrl` para recibir el `file_id` y el `bucket_id` correctos mapeados desde el documento de la colección `media`. También se implementó una detección robusta de tipos de archivo (mimetype) en las acciones del servidor al subir archivos para categorizar correctamente imágenes y videos.
|
|
|
|
### 3. Renderizado de Contenido Mixto (Texto Plano con HTML)
|
|
* **Problema**: Al publicar noticias, las etiquetas HTML guardadas por el editor enriquecido se mostraban en el frontend como texto plano legible (ej. se veía literalmente `<p>Texto</p>`), o bien el texto plano sin estructura perdía los saltos de línea.
|
|
* **Solución**: Se integró `isomorphic-dompurify` junto con la función `formatTextToHtml` en la vista de detalle. Esta función detecta si el contenido ya contiene etiquetas de estructura HTML. Si las tiene, respeta los elementos (sanitizándolos con un whitelist de seguridad XSS); si es texto plano, convierte inteligentemente los dobles saltos de línea en párrafos (`<p>`) y saltos individuales en retornos de carro (`<br />`), garantizando un diseño limpio.
|
|
|
|
### 4. Migración de Infraestructura a Coolify y Soporte Multi-dominio
|
|
* **Problema**: Caídas constantes del servidor en Hostinger debido a limitaciones de memoria y errores 503 en despliegues tradicionales de Node.js.
|
|
* **Solución**: Se migró el entorno de despliegue a **Coolify**, una plataforma de self-hosting que gestiona contenedores Docker. Se configuró el enrutamiento para soportar múltiples orígenes y dominios sin errores de cabeceras CORS en el backend, mejorando la disponibilidad global de la web de Almeira.
|
|
|
|
### 5. Automatización de Publicaciones vía WhatsApp con IA y Whitelist en Appwrite
|
|
* **Arquitectura**: Se integró Evolution API con Google Gemini (`gemini-3.6-flash`) y Appwrite para procesar mensajes y fotos enviados por clientes o salas de prensa directamente en WhatsApp.
|
|
* **Almacenamiento en Base de Datos Appwrite**: Los chats y clientes autorizados se registran y gestionan directamente en la base de datos de **Appwrite** (`whatsapp_messages` con estado `whitelist_active`/`whitelist_paused`), permitiendo añadir, pausar o eliminar clientes en tiempo real desde el Dashboard sin necesidad de redesplegar o reiniciar el servidor.
|
|
* **Sincronización con Repositorio**: Cada modificación realizada en Appwrite se sincroniza automáticamente como respaldo en `src/config/whatsapp-chats.json`.
|
|
* **Resolución Dinámica de Nombres**: Al recibir un evento en `/api/webhooks/whatsapp`, el webhook resuelve el nombre exacto del grupo o remitente consultando Evolution API (con caché en memoria) y valida coincidencias en tiempo real contra los registros activos de Appwrite en `src/lib/whatsapp-filter.ts`.
|
|
* **Gestión Visual en Dashboard**: En `/dashboard/settings`, los administradores pueden añadir nuevos clientes por nombre exacto, pausar/activar chats, o sincronizar automáticamente los grupos detectados en Evolution API con persistencia inmediata en Appwrite.
|
|
* **Flujo Editorial y Biblioteca de Medios**: Chat autorizado envía mensaje/multimedia -> Gemini estructura título, categoría, SEO y contenido limpio -> Si contiene imagen o video, el webhook sube el archivo a Appwrite Storage (`media_bucket`) y crea inmediatamente el documento en la **Biblioteca de Medios** (`media`), vinculándolo como `imagen_destacada_id` en el post -> Se calculan las dimensiones y previsualizaciones exactas (**640x360 px / 16:9** para Noticias; **640x640 px / 1:1** para Eventos y Clasificados) -> Se crea post en Appwrite en estado `borrador` -> El bot responde al grupo solicitando aprobación -> Administrador responde "Aprobar" -> El post pasa a estado `publicado`.
|
|
|
|
|
|
|