Files
blog/frontend/server/routes/content/articles/[slug].get.ts
T

75 lines
2.2 KiB
TypeScript
Raw Normal View History

2026-09-11 11:25:14 +02:00
import type { Article, Category } from '#shared/types/blog'
interface RawCategory extends Category {
nameEn?: string | null
}
2026-09-11 11:25:14 +02:00
interface RawArticle {
id: string
title: string
titleEn: string | null
2026-09-11 11:25:14 +02:00
slug: string
publishedAt: string
content: string | null
contentEn: string | null
2026-09-11 11:25:14 +02:00
cover: string
coverAlt: string | null
coverAltEn: string | null
2026-09-11 11:25:14 +02:00
authorName: string | null
expand?: { category?: RawCategory }
2026-09-11 11:25:14 +02:00
}
export default defineEventHandler(async (event): Promise<Article> => {
const slug = getRouterParam(event, 'slug')
if (!slug) {
throw createError({ statusCode: 400, statusMessage: 'Missing article slug' })
}
const lang = langParam(getQuery(event).lang)
2026-09-11 11:25:14 +02:00
const response = await pbFetch<PbList<RawArticle>>(
`/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
2026-09-11 11:25:14 +02:00
return {
title,
2026-09-11 11:25:14 +02:00
slug: article.slug,
publishedAt: article.publishedAt,
cover: toMediaImage('articles', article.id, article.cover, coverAlt),
category,
html: renderMarkdown(content),
summary: summarise(content),
2026-09-11 11:25:14 +02:00
author: article.authorName || null,
hasTranslation,
2026-09-11 11:25:14 +02:00
}
})