Strapi already records which admin user created an entry, so the byline needs no extra field to fill in: the Article content type sets populateCreatorFields and the API layer exposes only that user's firstname and lastname. The name also feeds the BlogPosting structured data. Also fixes the gap below the article header, which until now came from the cover image's margin and therefore vanished on articles without a cover, leaving the body text against the byline.
44 lines
1.2 KiB
TypeScript
44 lines
1.2 KiB
TypeScript
import type { Article } from '#shared/types/blog'
|
|
|
|
interface AdminUser {
|
|
firstname: string | null
|
|
lastname: string | null
|
|
}
|
|
|
|
type RawArticle = Omit<Article, 'html' | 'summary' | 'author'> & {
|
|
content: string | null
|
|
createdBy?: AdminUser | null
|
|
}
|
|
|
|
/** Joins the admin user's name parts; returns null when neither is set. */
|
|
function byline(user: AdminUser | null | undefined): string | null {
|
|
const name = [user?.firstname, user?.lastname].filter(Boolean).join(' ').trim()
|
|
return name || null
|
|
}
|
|
|
|
export default defineEventHandler(async (event): Promise<Article> => {
|
|
const slug = getRouterParam(event, 'slug')
|
|
|
|
if (!slug) {
|
|
throw createError({ statusCode: 400, statusMessage: 'Missing article slug' })
|
|
}
|
|
|
|
const response = await strapiFetch<StrapiList<RawArticle>>(
|
|
`/api/articles?${ARTICLE_DETAIL_QUERY}&filters[slug][$eq]=${encodeURIComponent(slug)}&pagination[pageSize]=1`
|
|
)
|
|
|
|
const article = response.data[0]
|
|
|
|
if (!article) {
|
|
throw createError({ statusCode: 404, statusMessage: 'Article not found' })
|
|
}
|
|
|
|
const { content, createdBy, ...rest } = article
|
|
return {
|
|
...rest,
|
|
html: renderMarkdown(content),
|
|
summary: summarise(content),
|
|
author: byline(createdBy),
|
|
}
|
|
})
|