Files
windwiderstand/src/routes/api/search-index/+server.ts
T
Peter Meier f328989aa8
Deploy / verify (push) Successful in 37s
Deploy / deploy (push) Successful in 55s
seo: enforce trailing slash on all URLs
- 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.
2026-04-17 22:23:16 +02:00

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' } },
);
};