import { NextResponse } from 'next/server'; import { ImageResponse } from 'next/og'; import { Document, Paragraph, TextRun, Packer, ImageRun, HeadingLevel, ExternalHyperlink, AlignmentType } from 'docx'; import { createAdminClient } from '@/lib/appwrite.server'; import { Query } from 'node-appwrite'; import { APPWRITE_ALMEIRA_CONFIG, endpoint, projectId } from '@/lib/appwrite'; import { collectionMap } from '@/lib/schema'; function getAppwriteImageUrl(fileId: string, bucketId?: string, width = 400, height = 250, isVideo = false) { if (!fileId) return ''; const finalBucketId = bucketId || APPWRITE_ALMEIRA_CONFIG.mediaBucketId; if (isVideo) { return `${endpoint}/storage/buckets/${finalBucketId}/files/${fileId}/view?project=${projectId}`; } return `${endpoint}/storage/buckets/${finalBucketId}/files/${fileId}/preview?project=${projectId}&width=${width}&height=${height}&quality=80`; } export const runtime = 'nodejs'; export async function GET(request: Request) { try { const { searchParams } = new URL(request.url); const q = searchParams.get('q') || ''; const sort = searchParams.get('sort') || 'fecha_publicacion'; const dir = searchParams.get('dir') || 'desc'; const status = searchParams.get('status') || ''; const year = searchParams.get('year'); const month = searchParams.get('month'); const client = await createAdminClient(); const postsCollectionId = collectionMap['posts']; const mediaCollectionId = collectionMap['media']; if (!postsCollectionId) { return NextResponse.json({ error: 'Configuración de colección incorrecta' }, { status: 500 }); } const queries = [Query.limit(100), Query.offset(0)]; if (dir === 'asc') queries.push(Query.orderAsc(sort)); else queries.push(Query.orderDesc(sort)); if (q) queries.push(Query.contains('titulo', q)); if (status && status !== 'all') { if (status === 'publicado') queries.push(Query.equal('estado', ['publicado', 'Publicado'])); else if (status === 'borrador') queries.push(Query.equal('estado', ['borrador', 'Borrador'])); else if (status === 'pendiente') queries.push(Query.equal('estado', ['pendiente', 'Pendiente'])); else if (status === 'archivado') queries.push(Query.equal('estado', ['archivado', 'Archivado'])); else queries.push(Query.equal('estado', status)); } if (year || month) { const filterYear = year ? Number(year) : new Date().getFullYear(); if (month) { const startDate = new Date(Date.UTC(filterYear, Number(month) - 1, 1)).toISOString(); const endDate = new Date(Date.UTC(filterYear, Number(month), 1)).toISOString(); queries.push(Query.greaterThanEqual('fecha_publicacion', startDate)); queries.push(Query.lessThan('fecha_publicacion', endDate)); } else { const startDate = new Date(Date.UTC(filterYear, 0, 1)).toISOString(); const endDate = new Date(Date.UTC(filterYear + 1, 0, 1)).toISOString(); queries.push(Query.greaterThanEqual('fecha_publicacion', startDate)); queries.push(Query.lessThan('fecha_publicacion', endDate)); } } const response = await client.databases.listDocuments( APPWRITE_ALMEIRA_CONFIG.databaseId, postsCollectionId, queries ); const posts = response.documents; const mediaIds = Array.from(new Set(posts.map(p => typeof p.imagen_destacada_id === 'object' && p.imagen_destacada_id !== null ? p.imagen_destacada_id.$id : p.imagen_destacada_id).filter(id => typeof id === 'string' && id.length > 0) as string[])); let mediaDocs: Record = {}; if (mediaIds.length > 0 && mediaCollectionId) { try { const relRes = await client.databases.listDocuments( APPWRITE_ALMEIRA_CONFIG.databaseId, mediaCollectionId, [Query.equal('$id', mediaIds), Query.limit(100)] ); relRes.documents.forEach(doc => { mediaDocs[doc.$id] = doc; }); } catch (err) { console.error('Error resolviendo las imágenes para el reporte', err); } } const docChildren: any[] = [ new Paragraph({ text: `Reporte de Publicaciones - Almeira Web`, heading: HeadingLevel.HEADING_1, alignment: AlignmentType.CENTER, spacing: { after: 400 } }) ]; for (const post of posts) { const fechaFormat = post.fecha_publicacion ? new Date(post.fecha_publicacion).toLocaleDateString('es-CO') : 'Sin fecha'; const mediaId = typeof post.imagen_destacada_id === 'object' && post.imagen_destacada_id !== null ? post.imagen_destacada_id.$id : post.imagen_destacada_id; let imageUrl = ''; if (mediaId && mediaDocs[mediaId]) { const mediaInfo = mediaDocs[mediaId]; if (mediaInfo.tipo === 'imagen') { imageUrl = getAppwriteImageUrl(mediaInfo.file_id, mediaInfo.bucket_id, 600, 400, false); } } let imageAdded = false; try { const plainTextResumen = (post.resumen_redes || post.contenido || '').replace(/<[^>]*>?/gm, ' ').replace(/\s+/g, ' ').trim(); const safeResumen = plainTextResumen.length > 120 ? plainTextResumen.substring(0, 120) + '...' : plainTextResumen; // Use native JSX now that the file is renamed to route.tsx const cardImage = new ImageResponse( (
{imageUrl ? ( ) : (
Almeira Web
)}

{post.titulo || 'Sin título'}

{safeResumen}

), { width: 600, height: 600 } ); const imgBuffer = await cardImage.arrayBuffer(); docChildren.push( new Paragraph({ children: [ new ImageRun({ data: imgBuffer, transformation: { width: 500, height: 333 }, type: 'png' }) ], alignment: AlignmentType.CENTER, spacing: { after: 200 } }) ); imageAdded = true; } catch (err) { console.error('Error fetching image for Word Document', err); } if (!imageAdded) { docChildren.push( new Paragraph({ children: [ new TextRun({ text: "[No se pudo generar la tarjeta virtual]", italics: true }) ], alignment: AlignmentType.CENTER, spacing: { after: 200 } }) ); } docChildren.push( new Paragraph({ children: [ new ExternalHyperlink({ children: [ new TextRun({ text: `https://almeira.com.co/${post.slug || post.$id}`, style: "Hyperlink" }), ], link: `https://almeira.com.co/${post.slug || post.$id}`, }) ], spacing: { after: 400 } }) ); docChildren.push( new Paragraph({ text: "______________________________________________________", alignment: AlignmentType.CENTER, spacing: { after: 200 } }) ); } if (posts.length === 0) { docChildren.push(new Paragraph({ text: "No se encontraron publicaciones con los filtros seleccionados." })); } const doc = new Document({ sections: [{ properties: {}, children: docChildren, }] }); const buffer = await Packer.toBuffer(doc); const dataArray = new Uint8Array(buffer); return new NextResponse(dataArray, { status: 200, headers: { 'Content-Type': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'Content-Disposition': `attachment; filename=reporte_publicaciones_${new Date().toISOString().split('T')[0]}.docx`, }, }); } catch (error: any) { console.error('General error Word API:', error); return NextResponse.json({ error: 'Error al generar el documento', details: error?.message || 'Error desconocido' }, { status: 500 }); } }