140 lines
4.3 KiB
TypeScript
140 lines
4.3 KiB
TypeScript
import { NextResponse } from 'next/server';
|
|
import { env } from '@/lib/env';
|
|
|
|
// 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() {
|
|
try {
|
|
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
|
|
}));
|
|
|
|
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 }
|
|
);
|
|
}
|
|
}
|