import type { Article, Category } from '#shared/types/blog' interface RawCategory extends Category { nameEn?: string | null } interface RawArticle { id: string title: string titleEn: string | null slug: string publishedAt: string content: string | null contentEn: string | null cover: string coverAlt: string | null coverAltEn: string | null authorName: string | null expand?: { category?: RawCategory } } export default defineEventHandler(async (event): Promise
=> { const slug = getRouterParam(event, 'slug') if (!slug) { throw createError({ statusCode: 400, statusMessage: 'Missing article slug' }) } const lang = langParam(getQuery(event).lang) const response = await pbFetch>( `/api/collections/articles/records?fields=${ARTICLE_DETAIL_FIELDS}&expand=category&filter=${encodeURIComponent(`slug = ${quote(slug)}`)}&${pagination(1, 1)}` ) const article = response.items[0] if (!article) { throw createError({ statusCode: 404, statusMessage: 'Article not found' }) } const hasTranslation = Boolean(article.titleEn && article.contentEn) // An /en/ request for an article with no translation yet 404s cleanly, // rather than silently falling back to Italian text on an English URL — // see docs/content-model.md. if (lang === 'en' && !hasTranslation) { throw createError({ statusCode: 404, statusMessage: 'Article not found' }) } const title = lang === 'en' ? article.titleEn! : article.title const content = lang === 'en' ? article.contentEn : article.content const coverAlt = lang === 'en' ? article.coverAltEn || article.coverAlt : article.coverAlt const category = article.expand?.category ? { name: lang === 'en' ? article.expand.category.nameEn || article.expand.category.name : article.expand.category.name, slug: article.expand.category.slug, } : null return { title, slug: article.slug, publishedAt: article.publishedAt, cover: toMediaImage('articles', article.id, article.cover, coverAlt), category, html: renderMarkdown(content), summary: summarise(content), author: article.authorName || null, hasTranslation, } })