134 lines
3.5 KiB
TypeScript
134 lines
3.5 KiB
TypeScript
import type { PageServerLoad } from './$types';
|
|
import { error, isHttpError } from '@sveltejs/kit';
|
|
|
|
interface PostRow {
|
|
id: number;
|
|
slug: string;
|
|
title: string;
|
|
content: string;
|
|
cover: string | null;
|
|
date: string;
|
|
category_name: string | null;
|
|
category_slug: string | null;
|
|
author: string | null;
|
|
series_id: number | null;
|
|
series_name: string | null;
|
|
series_slug: string | null;
|
|
series_description: string | null;
|
|
series_weight: number | null;
|
|
}
|
|
|
|
export const load: PageServerLoad = async ({ platform, params }) => {
|
|
const db = platform?.env?.DB;
|
|
const { slug } = params;
|
|
|
|
if (!db) {
|
|
const dummyMarkdown = `
|
|
欢迎来到 BlogFlare 博客系统。由于当前在 \`vite dev\` 模式下没有连接到本地数据库,您看到的是兜底(Fallback)渲染的测试内容。
|
|
|
|
### DoIt 风格的 Admonitions 测试
|
|
|
|
> [!NOTE]
|
|
> 这是一个 Note (笔记) 提示框,可以用来补充额外的说明信息。
|
|
|
|
> [!TIP]
|
|
> 这是一个 Tip (提示) 提示框,通常用来给出最佳实践或小技巧。
|
|
|
|
> [!WARNING]
|
|
> 这是一个 Warning (警告) 提示框,用来引起用户的注意,避免发生错误。
|
|
|
|
> [!DANGER]
|
|
> 这是一个 Danger (危险) 提示框,警告用户该操作可能产生严重后果(比如删除数据)。
|
|
|
|
正文测试结束。
|
|
`;
|
|
|
|
return {
|
|
post: {
|
|
id: 1,
|
|
slug,
|
|
title: slug === 'hello-world' ? 'Hello, BlogFlare! (Fallback)' : 'The Beauty of Typecho Theme VOID',
|
|
content: dummyMarkdown,
|
|
cover: null,
|
|
date: '2026-07-12',
|
|
author: 'Admin'
|
|
},
|
|
tags: [
|
|
{ name: 'Welcome', id: 1 },
|
|
{ name: 'Design', id: 2 }
|
|
],
|
|
series: null,
|
|
seriesPosts: []
|
|
};
|
|
}
|
|
|
|
try {
|
|
const post = await db.prepare(
|
|
`SELECT p.id, p.slug, p.title, p.content, p.cover, p.published_at as date,
|
|
c.name as category_name, c.slug as category_slug,
|
|
u.username as author,
|
|
s.id as series_id, s.name as series_name, s.slug as series_slug,
|
|
s.description as series_description, p.series_weight
|
|
FROM posts p
|
|
LEFT JOIN categories c ON p.category_id = c.id
|
|
LEFT JOIN users u ON p.author_id = u.id
|
|
LEFT JOIN series s ON p.series_id = s.id
|
|
WHERE p.slug = ?`
|
|
).bind(slug).first<PostRow>();
|
|
|
|
if (!post) {
|
|
error(404, 'Post not found');
|
|
}
|
|
|
|
const tagsQuery = await db.prepare(
|
|
`SELECT t.name, t.id
|
|
FROM tags t
|
|
JOIN post_tags pt ON t.id = pt.tag_id
|
|
WHERE pt.post_id = ?`
|
|
).bind(post.id).all<{ name: string; id: number }>();
|
|
const tags = tagsQuery.results || [];
|
|
let series: {
|
|
id: number;
|
|
name: string;
|
|
slug: string;
|
|
description: string | null;
|
|
} | null = null;
|
|
let seriesPosts: Array<{
|
|
id: number;
|
|
slug: string;
|
|
title: string;
|
|
series_weight: number | null;
|
|
published_at: string;
|
|
}> = [];
|
|
if (post.series_id && post.series_name && post.series_slug) {
|
|
series = {
|
|
id: post.series_id,
|
|
name: post.series_name,
|
|
slug: post.series_slug,
|
|
description: post.series_description
|
|
};
|
|
const seriesQuery = await db.prepare(
|
|
`SELECT id, slug, title, series_weight, published_at
|
|
FROM posts
|
|
WHERE series_id = ?
|
|
ORDER BY series_weight IS NULL, series_weight ASC, published_at ASC, id ASC`
|
|
).bind(post.series_id).all();
|
|
seriesPosts = seriesQuery.results as unknown as typeof seriesPosts;
|
|
}
|
|
|
|
return {
|
|
post: {
|
|
...post,
|
|
date: new Date(post.date as string).toLocaleDateString()
|
|
},
|
|
tags,
|
|
series,
|
|
seriesPosts
|
|
};
|
|
} catch (err: unknown) {
|
|
if (isHttpError(err, 404)) throw err;
|
|
console.error('Error fetching post:', err);
|
|
error(500, 'Internal Server Error');
|
|
}
|
|
};
|