import { NextResponse } from 'next/server'; import { env } from '@/lib/env'; import { getCachedNewsData, cacheNewsData } from '@/lib/redis'; // Helper function to clean HTML content function cleanHtmlContent(text: string): string { if (!text) return ''; return text .replace(/<[^>]*>/g, '') // Remove HTML tags .replace(/ /g, ' ') // Replace   with space .replace(/\s+/g, ' ') // Replace multiple spaces with single space .trim(); } // Helper function to format time function formatDateTime(dateStr: string): { displayDate: string, timestamp: string } { try { const date = new Date(dateStr); // Format like "17 avr." to match the Duties widget style const day = date.getDate(); const month = date.toLocaleString('fr-FR', { month: 'short' }) .toLowerCase() .replace('.', ''); // Remove the dot that comes with French locale return { displayDate: `${day} ${month}.`, // Add the dot back for consistent styling timestamp: date.toLocaleString('fr-FR', { day: '2-digit', month: 'short', hour: '2-digit', minute: '2-digit', hour12: false }).replace(',', ' à') }; } catch (error) { return { displayDate: 'N/A', timestamp: 'N/A' }; } } // Helper function to truncate text function truncateText(text: string, maxLength: number): string { if (!text) return ''; const cleaned = cleanHtmlContent(text); if (cleaned.length <= maxLength) return cleaned; const lastSpace = cleaned.lastIndexOf(' ', maxLength); const truncated = cleaned.substring(0, lastSpace > 0 ? lastSpace : maxLength).trim(); return truncated.replace(/[.,!?]$/, '') + '...'; } // Helper function to format category function formatCategory(category: string): string | null { if (!category) return null; // Return null for all categories to remove the labels completely return null; } // Helper function to format source function formatSource(source: string): string { if (!source) return ''; const sourceName = source .replace(/^(https?:\/\/)?(www\.)?/i, '') .split('.')[0] .toLowerCase() .replace(/[^a-z0-9]/g, ' ') .trim(); return sourceName.charAt(0).toUpperCase() + sourceName.slice(1); } interface NewsItem { id: number; title: string; displayDate: string; timestamp: string; source: string; description: string | null; category: string | null; url: string; } export async function GET(request: Request) { try { // Check if we should bypass cache const url = new URL(request.url); const forceRefresh = url.searchParams.get('refresh') === 'true'; // Try to get data from cache if not forcing refresh if (!forceRefresh) { const cachedNews = await getCachedNewsData(); if (cachedNews) { console.log('Using cached news data'); return NextResponse.json(cachedNews); } } console.log('Fetching news from FastAPI server...'); const response = await fetch(`${env.NEWS_API_URL}/news?limit=12`, { method: 'GET', headers: { 'Accept': 'application/json', }, // Add timeout to prevent hanging signal: AbortSignal.timeout(5000) }); if (!response.ok) { console.error(`News API error: ${response.status} ${response.statusText}`); const contentType = response.headers.get('content-type'); if (contentType && !contentType.includes('application/json')) { console.error('News API returned non-JSON response'); return NextResponse.json( { error: 'News API returned invalid response format', status: response.status }, { status: 502 } ); } return NextResponse.json( { error: 'Failed to fetch news', status: response.status }, { status: 502 } ); } let articles; try { articles = await response.json(); } catch (error) { console.error('Failed to parse news API response:', error); return NextResponse.json( { error: 'Failed to parse news API response', details: error instanceof Error ? error.message : 'Unknown error' }, { status: 502 } ); } const formattedNews: NewsItem[] = articles.map((article: any) => ({ id: article.id, title: article.title, displayDate: formatDateTime(article.date).displayDate, timestamp: formatDateTime(article.date).timestamp, source: formatSource(article.source), description: truncateText(article.description || '', 200), category: formatCategory(article.category), url: article.url })); // Cache the results await cacheNewsData(formattedNews); return NextResponse.json(formattedNews); } catch (error) { console.error('News API error:', error); return NextResponse.json( { error: 'Failed to fetch news', details: error instanceof Error ? error.message : 'Unknown error' }, { status: 500 } ); } }