]+class="[^"]*field--name-body[^"]*"[^>]*>([\s\S]*?)<\/div>\s*<\/div>/i);
+ if (bodyMatch) {
+ content = bodyMatch[1].trim();
+ } else {
+ // Fallback: extract p tags between article
+ const articleMatch = html.match(/
]*>([\s\S]*?)<\/article>/i);
+ if (articleMatch) {
+ const pTags = articleMatch[1].match(/([\s\S]*?)<\/p>/ig);
+ if (pTags) content = pTags.join('\n');
+ }
+ }
+
+ // Strip out tags except p, b, i, strong, em, ul, li, ol
+ content = content.replace(/<(?!p|b|i|strong|em|ul|li|ol|\/p|\/b|\/i|\/strong|\/em|\/ul|\/li|\/ol)[^>]+>/ig, '');
+
+ return { title, image, date, content };
+}
+
+async function run() {
+ try {
+ const list = await databases.listCollections(DB_ID);
+ const map = {};
+ list.collections.forEach(c => { map[c.name.toLowerCase()] = c.$id; });
+ const postsCollectionId = map['posts'] || 'posts';
+ const mediaCollectionId = map['media'] || 'media';
+ const pubTypesCollectionId = map['publication types'] || map['publication_types'] || 'publication_types';
+
+ const typeRes = await databases.listDocuments(DB_ID, pubTypesCollectionId, [Query.equal('codigo', 'NOTICIA')]);
+ const typeId = typeRes.documents.length > 0 ? typeRes.documents[0].$id : null;
+
+ for (let i = 0; i < urls.length; i++) {
+ const url = urls[i];
+ console.log('Processing:', url);
+ const data = await extractData(url);
+ console.log('Extracted Title:', data.title);
+ console.log('Extracted Date:', data.date);
+ console.log('Extracted Image:', data.image);
+
+ let mediaId = null;
+ if (data.image) {
+ try {
+ const imgRes = await axios.get(data.image, { responseType: 'arraybuffer' });
+ const buffer = Buffer.from(imgRes.data, 'binary');
+ const filePath = `/tmp/scraped_img_${i}.jpg`;
+ fs.writeFileSync(filePath, buffer);
+
+ let fileParam;
+ if (InputFile && InputFile.fromPath) fileParam = InputFile.fromPath(filePath, `image${i}.jpg`);
+ else fileParam = fs.createReadStream(filePath);
+
+ const file = await storage.createFile(BUCKET_ID, ID.unique(), fileParam);
+ const mediaDoc = await databases.createDocument(DB_ID, mediaCollectionId, ID.unique(), {
+ file_id: file.$id, bucket_id: BUCKET_ID,
+ titulo: data.title, tipo: 'imagen'
+ });
+ mediaId = mediaDoc.$id;
+ } catch(err) {
+ console.error('Error uploading image:', err.message);
+ }
+ }
+
+ const doc = {
+ titulo: data.title.substring(0, 100),
+ slug: `multivision-${Date.now()}-${i}`,
+ tipo_publicacion_id: typeId,
+ estado: 'Publicado',
+ fecha_publicacion: data.date,
+ contenido: data.content,
+ imagen_destacada_id: mediaId
+ };
+
+ const res = await databases.createDocument(DB_ID, postsCollectionId, ID.unique(), doc);
+ console.log('Published! ID:', res.$id);
+ console.log('---------------------------');
+ }
+ } catch(e) {
+ console.error(e);
+ }
+}
+run();
diff --git a/publish-single.js b/publish-single.js
new file mode 100644
index 0000000..50979ce
--- /dev/null
+++ b/publish-single.js
@@ -0,0 +1,41 @@
+const { Client, Databases, ID, Query } = require('node-appwrite');
+require('dotenv').config({ path: '.env.local' });
+
+const client = new Client()
+ .setEndpoint(process.env.NEXT_PUBLIC_APPWRITE_ENDPOINT)
+ .setProject(process.env.NEXT_PUBLIC_APPWRITE_PROJECT_ID)
+ .setKey(process.env.APPWRITE_API_KEY);
+
+const databases = new Databases(client);
+const DB_ID = process.env.APPWRITE_DATABASE_ID;
+
+async function run() {
+ try {
+ const list = await databases.listCollections(DB_ID);
+ const map = {};
+ list.collections.forEach(c => { map[c.name.toLowerCase()] = c.$id; });
+ const postsCollectionId = map['posts'] || 'posts';
+ const pubTypesCollectionId = map['publication types'] || map['publication_types'] || 'publication_types';
+
+ const typeRes = await databases.listDocuments(DB_ID, pubTypesCollectionId, [Query.equal('codigo', 'NOTICIA')]);
+ const typeId = typeRes.documents.length > 0 ? typeRes.documents[0].$id : null;
+
+ const bodyHtml = `
En esta Semana Mayor, el departamento se prepara para una de las temporadas turísticas y culturales más importantes del año. Para potenciar la economía local, la Gobernación del Cauca lanzó oficialmente la Ruta "Cauca Lo Es Todo", una estrategia que reúne 5 ferias comerciales diseñadas para enamorar a propios y visitantes.
Estas vitrinas comerciales, distribuidas estratégicamente, serán el escenario perfecto para disfrutar de la tradición, la gastronomía y las artesanías de la región. Es una apuesta directa por el turismo sostenible, donde el talento de los emprendedores de las diferentes subregiones se convierte en el protagonista.
La administración departamental invita a la comunidad a recorrer estas ferias, apoyar los negocios locales y descubrir por qué en esta Semana Santa, el #CaucaLoEsTodo.
Etiquetas: #CaucaLoEsTodo #SemanaSanta #Emprendimiento #Cultura #Tradición #GobernaciónDelCauca
`;
+
+ const doc = {
+ titulo: "🌅 Semana Santa con sello local: La ruta 'Cauca Lo Es Todo' activa 5 ferias comerciales",
+ slug: 'ruta-cauca-lo-es-todo-ferias',
+ tipo_publicacion_id: typeId,
+ estado: 'Publicado',
+ fecha_publicacion: new Date('2026-03-30T10:00:00.000Z').toISOString(),
+ contenido: bodyHtml,
+ resumen_redes: 'La Gobernación invita a recorrer el departamento durante la Semana Mayor a través de 5 vitrinas comerciales que resaltan el talento de nuestros emprendedores.',
+ };
+
+ const res = await databases.createDocument(DB_ID, postsCollectionId, ID.unique(), doc);
+ console.log('Publicación exitosa! ID:', res.$id);
+ } catch(e) {
+ console.error(e);
+ }
+}
+run();
diff --git a/src/components/publications/PostCard.tsx b/src/components/publications/PostCard.tsx
index 702b0b0..27baaf1 100644
--- a/src/components/publications/PostCard.tsx
+++ b/src/components/publications/PostCard.tsx
@@ -39,8 +39,8 @@ function resolveImageUrl(
return `${endpoint}/storage/buckets/${bucket_id}/files/${file_id}/preview?project=${projectId}&width=600&height=400&quality=80`;
}
- // Fallback: just an ID, use default 'media' bucket
- return `${endpoint}/storage/buckets/media/files/${post.imagen_destacada_id}/preview?project=${projectId}&width=600&height=400&quality=80`;
+ // If it's a string, we can't generate a valid URL because it's a document ID, not a file ID.
+ return null;
}
/** Maps PostStatus to a visual badge style */
@@ -63,6 +63,7 @@ export const PostCard: React.FC = ({
appwriteProjectId,
globalSettings,
}) => {
+ const [imageError, setImageError] = React.useState(false);
const imageUrl = resolveImageUrl(post, appwriteEndpoint, appwriteProjectId);
// Safe text rendering — no raw HTML
@@ -134,11 +135,12 @@ export const PostCard: React.FC = ({
)}
{/* Imagen Destacada */}
- {imageUrl ? (
+ {imageUrl && !imageError ? (
// eslint-disable-next-line @next/next/no-img-element

setImageError(true)}
className="w-full h-full object-cover transition-transform duration-500 group-hover:scale-105"
/>
) : (
diff --git a/test-appwrite.js b/test-appwrite.js
new file mode 100644
index 0000000..920c5f7
--- /dev/null
+++ b/test-appwrite.js
@@ -0,0 +1,23 @@
+const { Client, Databases } = require('node-appwrite');
+require('dotenv').config({ path: '.env.local' });
+
+const client = new Client()
+ .setEndpoint(process.env.NEXT_PUBLIC_APPWRITE_ENDPOINT)
+ .setProject(process.env.NEXT_PUBLIC_APPWRITE_PROJECT_ID)
+ .setKey(process.env.APPWRITE_API_KEY);
+
+const databases = new Databases(client);
+
+async function run() {
+ try {
+ const result = await databases.listDocuments(
+ process.env.APPWRITE_DATABASE_ID,
+ 'whatsapp_messages'
+ );
+ console.log(`Found ${result.documents.length} messages.`);
+ console.log(JSON.stringify(result.documents, null, 2));
+ } catch (error) {
+ console.error(error);
+ }
+}
+run();
diff --git a/test-evolution.js b/test-evolution.js
new file mode 100644
index 0000000..4def4ac
--- /dev/null
+++ b/test-evolution.js
@@ -0,0 +1,30 @@
+const axios = require('axios');
+const fs = require('fs');
+
+const EVOLUTION_API_URL = 'https://evolution.almeira.com.co';
+const EVOLUTION_API_KEY = 'mgXxu2eAwyhWNnptFEouUE6qLkvwVm6K';
+const EVOLUTION_INSTANCE_NAME = 'AutomataAlmeira';
+const CHAT_ID = '573134848203-1408546044@g.us';
+
+async function testGetBase64() {
+ try {
+ const response = await axios.post(
+ `${EVOLUTION_API_URL}/chat/findMessages/${EVOLUTION_INSTANCE_NAME}?page=1&limit=5`,
+ { where: { "key": { "remoteJid": CHAT_ID } } },
+ { headers: { 'apikey': EVOLUTION_API_KEY, 'Content-Type': 'application/json' } }
+ );
+ const msg = response.data.messages.records[0]; // Assuming it's the imageMessage we saw
+
+ const base64Res = await axios.post(
+ `${EVOLUTION_API_URL}/chat/getBase64FromMediaMessage/${EVOLUTION_INSTANCE_NAME}`,
+ { message: msg },
+ { headers: { 'apikey': EVOLUTION_API_KEY, 'Content-Type': 'application/json' } }
+ );
+
+ console.log("Success getting base64. Base64 length:", base64Res.data.base64 ? base64Res.data.base64.length : 'none');
+ } catch (e) {
+ console.error(e.response ? e.response.data : e.message);
+ }
+}
+
+testGetBase64();