f328989aa8
- Set trailingSlash='always' globally via root layout. - Update postHref, pageHref, Pagination, tag/breadcrumb hrefs, blog→posts redirects to emit trailing slash. - Match old Astro canonical so indexed URLs hit 200 directly instead of 308-redirect.
98 lines
2.7 KiB
TypeScript
98 lines
2.7 KiB
TypeScript
import type { RequestHandler } from './$types';
|
|
import { json } from '@sveltejs/kit';
|
|
import { getPages, getPosts } from '$lib/cms';
|
|
import {
|
|
filterHiddenPosts,
|
|
sortPostsByDate,
|
|
getPostImageUrl,
|
|
postHref,
|
|
} from '$lib/blog-utils';
|
|
import { ensureTransformedImage } from '$lib/rusty-image';
|
|
|
|
export type SearchHit = {
|
|
type: 'page' | 'post';
|
|
slug: string;
|
|
href: string;
|
|
title: string;
|
|
subtitle: string;
|
|
excerpt: string;
|
|
image: string | null;
|
|
created: string | null;
|
|
isEvent?: boolean;
|
|
eventDate?: string | null;
|
|
};
|
|
|
|
function pageHref(p: { slug?: string; _slug?: string }): string {
|
|
const raw = (p.slug ?? p._slug ?? '').replace(/^\//, '').trim();
|
|
if (!raw || raw === 'home') return '/';
|
|
return '/' + raw.split('/').filter(Boolean).map(encodeURIComponent).join('/') + '/';
|
|
}
|
|
|
|
export const GET: RequestHandler = async () => {
|
|
let pages: Awaited<ReturnType<typeof getPages>> = [];
|
|
let posts: Awaited<ReturnType<typeof getPosts>> = [];
|
|
try {
|
|
[pages, posts] = await Promise.all([
|
|
getPages(),
|
|
getPosts({ resolve: ['postImage'] }),
|
|
]);
|
|
} catch {
|
|
return json({ hits: [] satisfies SearchHit[] });
|
|
}
|
|
|
|
posts = filterHiddenPosts(sortPostsByDate(posts));
|
|
|
|
const postHits: SearchHit[] = await Promise.all(
|
|
posts.map(async (p) => {
|
|
const raw = getPostImageUrl(p.postImage);
|
|
let image: string | null = null;
|
|
if (raw) {
|
|
try {
|
|
image = await ensureTransformedImage(raw, {
|
|
width: 160,
|
|
height: 106,
|
|
fit: 'cover',
|
|
format: 'webp',
|
|
});
|
|
} catch {
|
|
/* optional */
|
|
}
|
|
}
|
|
return {
|
|
type: 'post',
|
|
slug: p.slug ?? p._slug ?? '',
|
|
href: postHref(p),
|
|
title: p.headline ?? p.linkName ?? p._slug ?? '',
|
|
subtitle: p.subheadline ?? '',
|
|
excerpt: p.excerpt ?? '',
|
|
image,
|
|
created: p.created ?? null,
|
|
isEvent: Boolean((p as { isEvent?: boolean }).isEvent),
|
|
eventDate: (p as { eventDate?: string }).eventDate ?? null,
|
|
};
|
|
}),
|
|
);
|
|
|
|
const pageHits: SearchHit[] = pages
|
|
.filter((p) => !(p as { hideFromSearch?: boolean }).hideFromSearch)
|
|
.map((p) => ({
|
|
type: 'page',
|
|
slug: p.slug ?? p._slug ?? '',
|
|
href: pageHref(p),
|
|
title:
|
|
(p as { headline?: string }).headline ??
|
|
(p as { title?: string }).title ??
|
|
p._slug ??
|
|
'',
|
|
subtitle: (p as { subheadline?: string }).subheadline ?? '',
|
|
excerpt: (p as { excerpt?: string }).excerpt ?? '',
|
|
image: null,
|
|
created: (p as { created?: string }).created ?? null,
|
|
}));
|
|
|
|
return json(
|
|
{ hits: [...pageHits, ...postHits] satisfies SearchHit[] },
|
|
{ headers: { 'cache-control': 'public, max-age=60, s-maxage=60' } },
|
|
);
|
|
};
|