mirror of
https://github.com/KazooTTT/kazoottt-blog.git
synced 2025-06-16 23:41:21 +08:00
Refactor project structure and update dependencies; improve code formatting and consistency across multiple components and files
This commit is contained in:
11689
pnpm-lock.yaml
generated
11689
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -2,38 +2,38 @@
|
|||||||
import ToolSection from './ToolSection.astro'
|
import ToolSection from './ToolSection.astro'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
title: string
|
title: string
|
||||||
categories: {
|
categories: {
|
||||||
title: string
|
title: string
|
||||||
sections: {
|
sections: {
|
||||||
[key: string]: {
|
[key: string]: {
|
||||||
title: string
|
title: string
|
||||||
tools: {
|
tools: {
|
||||||
name: string
|
name: string
|
||||||
description: string
|
description: string
|
||||||
href?: string
|
href?: string
|
||||||
iconPath?: string
|
iconPath?: string
|
||||||
}[]
|
}[]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}[]
|
}[]
|
||||||
}
|
}
|
||||||
|
|
||||||
const { title, categories } = Astro.props
|
const { title, categories } = Astro.props
|
||||||
---
|
---
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<h2 class='mb-6 text-xl font-bold'>{title}</h2>
|
<h2 class='mb-6 text-xl font-bold'>{title}</h2>
|
||||||
{
|
{
|
||||||
categories.map((category) => (
|
categories.map((category) => (
|
||||||
<div class='mb-8'>
|
<div class='mb-8'>
|
||||||
<h3 class='mb-4 text-lg font-semibold'>{category.title}</h3>
|
<h3 class='mb-4 text-lg font-semibold'>{category.title}</h3>
|
||||||
{Object.values(category.sections).map((section) => (
|
{Object.values(category.sections).map((section) => (
|
||||||
<div class='mb-6'>
|
<div class='mb-6'>
|
||||||
<ToolSection title={section.title} tools={section.tools} />
|
<ToolSection title={section.title} tools={section.tools} />
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,9 +1,6 @@
|
|||||||
import GitHubCalendar from "react-github-calendar";
|
import GitHubCalendar from 'react-github-calendar'
|
||||||
const GithubHotLine = () => {
|
const GithubHotLine = () => {
|
||||||
return (
|
return <GitHubCalendar username='kazoottt' />
|
||||||
<GitHubCalendar username='kazoottt' />
|
}
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
export default GithubHotLine
|
||||||
export default GithubHotLine
|
|
||||||
|
@ -33,17 +33,19 @@ if (imagePath) {
|
|||||||
href={href}
|
href={href}
|
||||||
target={target}
|
target={target}
|
||||||
>
|
>
|
||||||
{imageComponent && (
|
{
|
||||||
<Image
|
imageComponent && (
|
||||||
src={imageComponent()}
|
<Image
|
||||||
alt={altText}
|
src={imageComponent()}
|
||||||
class='h-32 w-full rounded-2xl rounded-bl-none rounded-br-none object-cover'
|
alt={altText}
|
||||||
loading='eager'
|
class='h-32 w-full rounded-2xl rounded-bl-none rounded-br-none object-cover'
|
||||||
/>
|
loading='eager'
|
||||||
)}
|
/>
|
||||||
<div class='flex flex-col gap-y-2 flex-1 px-5 py-4 text-center justify-center w-full'>
|
)
|
||||||
|
}
|
||||||
|
<div class='flex w-full flex-1 flex-col justify-center gap-y-2 px-5 py-4 text-center'>
|
||||||
<h1 class='text-lg font-medium'>{heading}</h1>
|
<h1 class='text-lg font-medium'>{heading}</h1>
|
||||||
<h2 class='text-muted-foreground line-clamp-2 text-sm'>{subheading}</h2>
|
<h2 class='line-clamp-2 text-sm text-muted-foreground'>{subheading}</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<slot />
|
<slot />
|
||||||
|
@ -13,12 +13,14 @@ const { headings } = Astro.props
|
|||||||
const toc = generateToc(headings)
|
const toc = generateToc(headings)
|
||||||
---
|
---
|
||||||
|
|
||||||
<aside class='sticky top-20 order-2 -me-28 hidden h-[calc(100vh-6rem)] basis-60 lg:flex lg:flex-col'>
|
<aside
|
||||||
|
class='sticky top-20 order-2 -me-28 hidden h-[calc(100vh-6rem)] basis-60 lg:flex lg:flex-col'
|
||||||
|
>
|
||||||
{
|
{
|
||||||
toc.length > 0 && (
|
toc.length > 0 && (
|
||||||
<>
|
<>
|
||||||
<h2 class='mb-4 font-semibold'>TABLE OF CONTENTS</h2>
|
<h2 class='mb-4 font-semibold'>TABLE OF CONTENTS</h2>
|
||||||
<ul class='text-card-foreground overflow-y-auto pr-4 max-h-[calc(100vh-10rem)]'>
|
<ul class='max-h-[calc(100vh-10rem)] overflow-y-auto pr-4 text-card-foreground'>
|
||||||
{toc.map((heading) => (
|
{toc.map((heading) => (
|
||||||
<TOCHeading heading={heading} />
|
<TOCHeading heading={heading} />
|
||||||
))}
|
))}
|
||||||
|
@ -195,20 +195,22 @@ import { Image } from 'astro:assets'
|
|||||||
height='32'
|
height='32'
|
||||||
viewBox='0 0 24 24'
|
viewBox='0 0 24 24'
|
||||||
class='ml-2 h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:hidden dark:-rotate-90 dark:scale-0'
|
class='ml-2 h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:hidden dark:-rotate-90 dark:scale-0'
|
||||||
><path
|
><path
|
||||||
fill='currentColor'
|
fill='currentColor'
|
||||||
d='M12 15q1.25 0 2.125-.875T15 12q0-1.25-.875-2.125T12 9q-1.25 0-2.125.875T9 12q0 1.25.875 2.125T12 15m0 1q-1.671 0-2.836-1.164T8 12q0-1.671 1.164-2.836T12 8q1.671 0 2.836 1.164T16 12q0 1.671-1.164 2.836T12 16m-7-3.5H1.5v-1H5zm17.5 0H19v-1h3.5zM11.5 5V1.5h1V5zm0 17.5V19h1v3.5zM6.746 7.404l-2.16-2.098l.695-.744l2.111 2.134zM18.72 19.438l-2.117-2.14l.652-.702l2.16 2.098zM16.596 6.746l2.098-2.16l.744.695l-2.134 2.111zM4.562 18.72l2.14-2.117l.663.652l-2.078 2.179zM12 12'
|
d='M12 15q1.25 0 2.125-.875T15 12q0-1.25-.875-2.125T12 9q-1.25 0-2.125.875T9 12q0 1.25.875 2.125T12 15m0 1q-1.671 0-2.836-1.164T8 12q0-1.671 1.164-2.836T12 8q1.671 0 2.836 1.164T16 12q0 1.671-1.164 2.836T12 16m-7-3.5H1.5v-1H5zm17.5 0H19v-1h3.5zM11.5 5V1.5h1V5zm0 17.5V19h1v3.5zM6.746 7.404l-2.16-2.098l.695-.744l2.111 2.134zM18.72 19.438l-2.117-2.14l.652-.702l2.16 2.098zM16.596 6.746l2.098-2.16l.744.695l-2.134 2.111zM4.562 18.72l2.14-2.117l.663.652l-2.078 2.179zM12 12'
|
||||||
></path></svg>
|
></path></svg
|
||||||
|
>
|
||||||
<svg
|
<svg
|
||||||
xmlns='http://www.w3.org/2000/svg'
|
xmlns='http://www.w3.org/2000/svg'
|
||||||
width='32'
|
width='32'
|
||||||
height='32'
|
height='32'
|
||||||
viewBox='0 0 24 24'
|
viewBox='0 0 24 24'
|
||||||
class='ml-2 hidden h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:block dark:rotate-0 dark:scale-100'
|
class='ml-2 hidden h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:block dark:rotate-0 dark:scale-100'
|
||||||
><path
|
><path
|
||||||
fill='currentColor'
|
fill='currentColor'
|
||||||
d='M12.058 20q-3.334 0-5.667-2.333Q4.058 15.333 4.058 12q0-3.038 1.98-5.27Q8.02 4.5 10.942 4.097q.081 0 .159.006t.153.017q-.506.706-.801 1.57q-.295.865-.295 1.811q0 2.667 1.866 4.533q1.867 1.867 4.534 1.867q.952 0 1.813-.295q.862-.295 1.548-.801q.012.075.018.153q.005.078.005.158q-.384 2.923-2.615 4.904T12.057 20'
|
d='M12.058 20q-3.334 0-5.667-2.333Q4.058 15.333 4.058 12q0-3.038 1.98-5.27Q8.02 4.5 10.942 4.097q.081 0 .159.006t.153.017q-.506.706-.801 1.57q-.295.865-.295 1.811q0 2.667 1.866 4.533q1.867 1.867 4.534 1.867q.952 0 1.813-.295q.862-.295 1.548-.801q.012.075.018.153q.005.078.005.158q-.384 2.923-2.615 4.904T12.057 20'
|
||||||
></path></svg>
|
></path></svg
|
||||||
|
>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -223,7 +225,7 @@ import { Image } from 'astro:assets'
|
|||||||
function setupDarkModeToggle() {
|
function setupDarkModeToggle() {
|
||||||
const toggleDarkModeButton = document.getElementById('toggleDarkMode')
|
const toggleDarkModeButton = document.getElementById('toggleDarkMode')
|
||||||
const mobileToggleDarkMode = document.getElementById('mobileToggleDarkMode')
|
const mobileToggleDarkMode = document.getElementById('mobileToggleDarkMode')
|
||||||
|
|
||||||
function toggleTheme() {
|
function toggleTheme() {
|
||||||
const currentTheme = getCurrentTheme()
|
const currentTheme = getCurrentTheme()
|
||||||
const newTheme = currentTheme === 'dark' ? 'light' : 'dark'
|
const newTheme = currentTheme === 'dark' ? 'light' : 'dark'
|
||||||
|
@ -23,7 +23,14 @@ const articleDate = date?.toISOString()
|
|||||||
const { headings } = await post.render()
|
const { headings } = await post.render()
|
||||||
---
|
---
|
||||||
|
|
||||||
<PageLayout meta={{ articleDate, description: description ?? '', ogImage: socialImage, title }}>
|
<PageLayout
|
||||||
|
meta={{
|
||||||
|
articleDate,
|
||||||
|
description: description ?? '',
|
||||||
|
ogImage: socialImage,
|
||||||
|
title
|
||||||
|
}}
|
||||||
|
>
|
||||||
<div class='w-full'>
|
<div class='w-full'>
|
||||||
<Button title='Back' href={backHref} style='button'>
|
<Button title='Back' href={backHref} style='button'>
|
||||||
<svg
|
<svg
|
||||||
|
@ -1,163 +1,176 @@
|
|||||||
import type { APIRoute } from 'astro';
|
import type { APIRoute } from 'astro'
|
||||||
|
|
||||||
export const GET: APIRoute = async ({ params, locals, request }) => {
|
export const GET: APIRoute = async ({ params, locals, request }) => {
|
||||||
// Handle multiple path segments and decode the URL-encoded slug
|
// Handle multiple path segments and decode the URL-encoded slug
|
||||||
const slugSegments = params.slug?.split('/') || [];
|
const slugSegments = params.slug?.split('/') || []
|
||||||
const slug = decodeURIComponent(slugSegments.join('/'));
|
const slug = decodeURIComponent(slugSegments.join('/'))
|
||||||
|
|
||||||
if (!slug) {
|
|
||||||
return new Response(JSON.stringify({ error: 'Slug is required' }), {
|
|
||||||
status: 400,
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Access runtime environment through context
|
if (!slug) {
|
||||||
const ctx = locals as any;
|
return new Response(JSON.stringify({ error: 'Slug is required' }), {
|
||||||
const env = ctx.env || ctx.runtime?.env;
|
status: 400,
|
||||||
|
headers: { 'Content-Type': 'application/json' }
|
||||||
console.log('Context:', {
|
})
|
||||||
hasEnv: !!ctx.env,
|
}
|
||||||
hasRuntime: !!ctx.runtime,
|
|
||||||
envKeys: ctx.env ? Object.keys(ctx.env) : [],
|
|
||||||
runtimeEnvKeys: ctx.runtime?.env ? Object.keys(ctx.runtime.env) : []
|
|
||||||
});
|
|
||||||
|
|
||||||
const db = env?.DB;
|
// Access runtime environment through context
|
||||||
if (!db) {
|
const ctx = locals as any
|
||||||
console.error('D1 database not available');
|
const env = ctx.env || ctx.runtime?.env
|
||||||
return new Response(
|
|
||||||
JSON.stringify({
|
|
||||||
error: 'Database not configured',
|
|
||||||
context: {
|
|
||||||
hasEnv: !!ctx.env,
|
|
||||||
hasRuntime: !!ctx.runtime,
|
|
||||||
envKeys: ctx.env ? Object.keys(ctx.env) : [],
|
|
||||||
runtimeEnvKeys: ctx.runtime?.env ? Object.keys(ctx.runtime.env) : []
|
|
||||||
}
|
|
||||||
}), {
|
|
||||||
status: 500,
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
console.log('Context:', {
|
||||||
// Get current view count
|
hasEnv: !!ctx.env,
|
||||||
const { results } = await db
|
hasRuntime: !!ctx.runtime,
|
||||||
.prepare('SELECT views FROM pageviews WHERE slug = ?')
|
envKeys: ctx.env ? Object.keys(ctx.env) : [],
|
||||||
.bind(slug)
|
runtimeEnvKeys: ctx.runtime?.env ? Object.keys(ctx.runtime.env) : []
|
||||||
.all();
|
})
|
||||||
|
|
||||||
const views = results.length > 0 ? results[0].views : 0;
|
const db = env?.DB
|
||||||
|
if (!db) {
|
||||||
|
console.error('D1 database not available')
|
||||||
|
return new Response(
|
||||||
|
JSON.stringify({
|
||||||
|
error: 'Database not configured',
|
||||||
|
context: {
|
||||||
|
hasEnv: !!ctx.env,
|
||||||
|
hasRuntime: !!ctx.runtime,
|
||||||
|
envKeys: ctx.env ? Object.keys(ctx.env) : [],
|
||||||
|
runtimeEnvKeys: ctx.runtime?.env ? Object.keys(ctx.runtime.env) : []
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
status: 500,
|
||||||
|
headers: { 'Content-Type': 'application/json' }
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return new Response(JSON.stringify({ views }), {
|
try {
|
||||||
status: 200,
|
// Get current view count
|
||||||
headers: {
|
const { results } = await db
|
||||||
'Content-Type': 'application/json',
|
.prepare('SELECT views FROM pageviews WHERE slug = ?')
|
||||||
},
|
.bind(slug)
|
||||||
});
|
.all()
|
||||||
} catch (error) {
|
|
||||||
console.error('Error getting page views:', error);
|
const views = results.length > 0 ? results[0].views : 0
|
||||||
return new Response(
|
|
||||||
JSON.stringify({
|
return new Response(JSON.stringify({ views }), {
|
||||||
error: 'Failed to get page views',
|
status: 200,
|
||||||
details: error instanceof Error ? error.message : String(error)
|
headers: {
|
||||||
}), {
|
'Content-Type': 'application/json'
|
||||||
status: 500,
|
}
|
||||||
headers: {
|
})
|
||||||
'Content-Type': 'application/json',
|
} catch (error) {
|
||||||
},
|
console.error('Error getting page views:', error)
|
||||||
});
|
return new Response(
|
||||||
}
|
JSON.stringify({
|
||||||
};
|
error: 'Failed to get page views',
|
||||||
|
details: error instanceof Error ? error.message : String(error)
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
status: 500,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const POST: APIRoute = async ({ params, locals, request }) => {
|
export const POST: APIRoute = async ({ params, locals, request }) => {
|
||||||
// Handle multiple path segments and decode the URL-encoded slug
|
// Handle multiple path segments and decode the URL-encoded slug
|
||||||
const slugSegments = params.slug?.split('/') || [];
|
const slugSegments = params.slug?.split('/') || []
|
||||||
const slug = decodeURIComponent(slugSegments.join('/'));
|
const slug = decodeURIComponent(slugSegments.join('/'))
|
||||||
|
|
||||||
if (!slug) {
|
|
||||||
return new Response(JSON.stringify({ error: 'Slug is required' }), {
|
|
||||||
status: 400,
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Access runtime environment through context
|
if (!slug) {
|
||||||
const ctx = locals as any;
|
return new Response(JSON.stringify({ error: 'Slug is required' }), {
|
||||||
const env = ctx.env || ctx.runtime?.env;
|
status: 400,
|
||||||
|
headers: { 'Content-Type': 'application/json' }
|
||||||
console.log('Context:', {
|
})
|
||||||
hasEnv: !!ctx.env,
|
}
|
||||||
hasRuntime: !!ctx.runtime,
|
|
||||||
envKeys: ctx.env ? Object.keys(ctx.env) : [],
|
|
||||||
runtimeEnvKeys: ctx.runtime?.env ? Object.keys(ctx.runtime.env) : []
|
|
||||||
});
|
|
||||||
|
|
||||||
const db = env?.DB;
|
// Access runtime environment through context
|
||||||
if (!db) {
|
const ctx = locals as any
|
||||||
console.error('D1 database not available');
|
const env = ctx.env || ctx.runtime?.env
|
||||||
return new Response(
|
|
||||||
JSON.stringify({
|
|
||||||
error: 'Database not configured',
|
|
||||||
context: {
|
|
||||||
hasEnv: !!ctx.env,
|
|
||||||
hasRuntime: !!ctx.runtime,
|
|
||||||
envKeys: ctx.env ? Object.keys(ctx.env) : [],
|
|
||||||
runtimeEnvKeys: ctx.runtime?.env ? Object.keys(ctx.runtime.env) : []
|
|
||||||
}
|
|
||||||
}), {
|
|
||||||
status: 500,
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
console.log('Context:', {
|
||||||
// Get request information
|
hasEnv: !!ctx.env,
|
||||||
const ip_address = request.headers.get('cf-connecting-ip') || request.headers.get('x-forwarded-for') || 'unknown';
|
hasRuntime: !!ctx.runtime,
|
||||||
const user_agent = request.headers.get('user-agent') || 'unknown';
|
envKeys: ctx.env ? Object.keys(ctx.env) : [],
|
||||||
const referrer = request.headers.get('referer') || 'unknown';
|
runtimeEnvKeys: ctx.runtime?.env ? Object.keys(ctx.runtime.env) : []
|
||||||
|
})
|
||||||
|
|
||||||
// Start a transaction to ensure both operations succeed or fail together
|
const db = env?.DB
|
||||||
const stmt1 = db.prepare(
|
if (!db) {
|
||||||
`INSERT INTO pageviews (slug, views)
|
console.error('D1 database not available')
|
||||||
|
return new Response(
|
||||||
|
JSON.stringify({
|
||||||
|
error: 'Database not configured',
|
||||||
|
context: {
|
||||||
|
hasEnv: !!ctx.env,
|
||||||
|
hasRuntime: !!ctx.runtime,
|
||||||
|
envKeys: ctx.env ? Object.keys(ctx.env) : [],
|
||||||
|
runtimeEnvKeys: ctx.runtime?.env ? Object.keys(ctx.runtime.env) : []
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
status: 500,
|
||||||
|
headers: { 'Content-Type': 'application/json' }
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Get request information
|
||||||
|
const ip_address =
|
||||||
|
request.headers.get('cf-connecting-ip') || request.headers.get('x-forwarded-for') || 'unknown'
|
||||||
|
const user_agent = request.headers.get('user-agent') || 'unknown'
|
||||||
|
const referrer = request.headers.get('referer') || 'unknown'
|
||||||
|
|
||||||
|
// Start a transaction to ensure both operations succeed or fail together
|
||||||
|
const stmt1 = db
|
||||||
|
.prepare(
|
||||||
|
`INSERT INTO pageviews (slug, views)
|
||||||
VALUES (?, 1)
|
VALUES (?, 1)
|
||||||
ON CONFLICT(slug)
|
ON CONFLICT(slug)
|
||||||
DO UPDATE SET views = views + 1,
|
DO UPDATE SET views = views + 1,
|
||||||
updated_at = CURRENT_TIMESTAMP`
|
updated_at = CURRENT_TIMESTAMP`
|
||||||
).bind(slug);
|
)
|
||||||
|
.bind(slug)
|
||||||
|
|
||||||
const stmt2 = db.prepare(
|
const stmt2 = db
|
||||||
`INSERT INTO pageview_logs (slug, ip_address, user_agent, referrer)
|
.prepare(
|
||||||
|
`INSERT INTO pageview_logs (slug, ip_address, user_agent, referrer)
|
||||||
VALUES (?, ?, ?, ?)`
|
VALUES (?, ?, ?, ?)`
|
||||||
).bind(slug, ip_address, user_agent, referrer);
|
)
|
||||||
|
.bind(slug, ip_address, user_agent, referrer)
|
||||||
|
|
||||||
// Execute both statements in a transaction
|
// Execute both statements in a transaction
|
||||||
await db.batch([stmt1, stmt2]);
|
await db.batch([stmt1, stmt2])
|
||||||
|
|
||||||
// Get updated view count
|
// Get updated view count
|
||||||
const { results } = await db
|
const { results } = await db
|
||||||
.prepare('SELECT views FROM pageviews WHERE slug = ?')
|
.prepare('SELECT views FROM pageviews WHERE slug = ?')
|
||||||
.bind(slug)
|
.bind(slug)
|
||||||
.all();
|
.all()
|
||||||
|
|
||||||
return new Response(JSON.stringify({ views: results[0].views }), {
|
return new Response(JSON.stringify({ views: results[0].views }), {
|
||||||
status: 200,
|
status: 200,
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json'
|
||||||
},
|
}
|
||||||
});
|
})
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error updating page views:', error);
|
console.error('Error updating page views:', error)
|
||||||
return new Response(
|
return new Response(
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
error: 'Failed to update page views',
|
error: 'Failed to update page views',
|
||||||
details: error instanceof Error ? error.message : String(error)
|
details: error instanceof Error ? error.message : String(error)
|
||||||
}), {
|
}),
|
||||||
status: 500,
|
{
|
||||||
headers: {
|
status: 500,
|
||||||
'Content-Type': 'application/json',
|
headers: {
|
||||||
},
|
'Content-Type': 'application/json'
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
};
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -20,7 +20,10 @@ export const getStaticPaths = (async ({ paginate }) => {
|
|||||||
const allPostsByDate = sortMDByDate(allPosts)
|
const allPostsByDate = sortMDByDate(allPosts)
|
||||||
const uniqueTags = getUniqueTagsWithCount(allPosts)
|
const uniqueTags = getUniqueTagsWithCount(allPosts)
|
||||||
const uniqueCategories = getUniqueCategoriesWithCount(allPosts)
|
const uniqueCategories = getUniqueCategoriesWithCount(allPosts)
|
||||||
return paginate(allPostsByDate, { pageSize: 50, props: { uniqueTags, uniqueCategories } })
|
return paginate(allPostsByDate, {
|
||||||
|
pageSize: 50,
|
||||||
|
props: { uniqueTags, uniqueCategories }
|
||||||
|
})
|
||||||
}) satisfies GetStaticPaths
|
}) satisfies GetStaticPaths
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
|
@ -5,7 +5,6 @@ import type { GetStaticPaths, InferGetStaticPropsType } from 'astro'
|
|||||||
|
|
||||||
import PostLayout from '@/layouts/BlogPost.astro'
|
import PostLayout from '@/layouts/BlogPost.astro'
|
||||||
import { getAllPosts } from '@/utils'
|
import { getAllPosts } from '@/utils'
|
||||||
import GiscusComment from '@/components/GiscusComment'
|
|
||||||
|
|
||||||
export const getStaticPaths = (async () => {
|
export const getStaticPaths = (async () => {
|
||||||
const blogEntries = await getAllPosts()
|
const blogEntries = await getAllPosts()
|
||||||
@ -21,7 +20,6 @@ const { entry } = Astro.props
|
|||||||
const { Content } = await entry.render()
|
const { Content } = await entry.render()
|
||||||
---
|
---
|
||||||
|
|
||||||
<PostLayout post={entry}>
|
<PostLayout post={entry} backHref='/blog'>
|
||||||
<Content />
|
<Content />
|
||||||
<GiscusComment client:load />
|
|
||||||
</PostLayout>
|
</PostLayout>
|
||||||
|
@ -7,7 +7,7 @@ const allPosts = await getAllPosts()
|
|||||||
const allCategories = getUniqueCategoriesWithCount(allPosts)
|
const allCategories = getUniqueCategoriesWithCount(allPosts)
|
||||||
|
|
||||||
const meta = {
|
const meta = {
|
||||||
description: 'A list of all the topics I\'ve written about in my posts',
|
description: "A list of all the topics I've written about in my posts",
|
||||||
title: 'All Categories'
|
title: 'All Categories'
|
||||||
}
|
}
|
||||||
---
|
---
|
||||||
|
@ -4,7 +4,6 @@ export const prerender = true
|
|||||||
import type { GetStaticPaths, InferGetStaticPropsType } from 'astro'
|
import type { GetStaticPaths, InferGetStaticPropsType } from 'astro'
|
||||||
|
|
||||||
import PostLayout from '@/layouts/BlogPost.astro'
|
import PostLayout from '@/layouts/BlogPost.astro'
|
||||||
import GiscusComment from '@/components/GiscusComment'
|
|
||||||
import { getallDiaries } from 'src/utils'
|
import { getallDiaries } from 'src/utils'
|
||||||
|
|
||||||
export const getStaticPaths = (async () => {
|
export const getStaticPaths = (async () => {
|
||||||
@ -23,5 +22,4 @@ const { Content } = await entry.render()
|
|||||||
|
|
||||||
<PostLayout post={entry} simple={true} backHref='/diary'>
|
<PostLayout post={entry} simple={true} backHref='/diary'>
|
||||||
<Content />
|
<Content />
|
||||||
<GiscusComment client:load />
|
|
||||||
</PostLayout>
|
</PostLayout>
|
||||||
|
@ -202,7 +202,7 @@ const projects = [...directProjects, ...mdProjects].slice(0, MAX_PROJECTS)
|
|||||||
</div>
|
</div>
|
||||||
<div class='mt-4 flex justify-end'>
|
<div class='mt-4 flex justify-end'>
|
||||||
<a
|
<a
|
||||||
href={`/categories/${encodeURIComponent('项目-已结项')}`}
|
href={`/categories/${encodeURIComponent('项目')}`}
|
||||||
class='flex items-center gap-x-1 text-sm text-muted-foreground transition-colors hover:text-foreground'
|
class='flex items-center gap-x-1 text-sm text-muted-foreground transition-colors hover:text-foreground'
|
||||||
>
|
>
|
||||||
查看更多项目
|
查看更多项目
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import type { SiteConfig } from '@/types';
|
import type { SiteConfig } from '@/types'
|
||||||
import type { AstroExpressiveCodeOptions } from 'astro-expressive-code';
|
import type { AstroExpressiveCodeOptions } from 'astro-expressive-code'
|
||||||
|
|
||||||
export const siteConfig: SiteConfig = {
|
export const siteConfig: SiteConfig = {
|
||||||
// Used as both a meta property (src/components/BaseHead.astro L:31 + L:49) & the generated satori png (src/pages/og-image/[slug].png.ts)
|
// Used as both a meta property (src/components/BaseHead.astro L:31 + L:49) & the generated satori png (src/pages/og-image/[slug].png.ts)
|
||||||
@ -21,7 +21,7 @@ export const siteConfig: SiteConfig = {
|
|||||||
year: 'numeric'
|
year: 'numeric'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
site: 'https://blog.kazoottt.top',
|
site: 'https://blog.kazoottt.top'
|
||||||
}
|
}
|
||||||
|
|
||||||
export const menuLinks: Array<{ title: string; path: string }> = [
|
export const menuLinks: Array<{ title: string; path: string }> = [
|
||||||
|
@ -24,10 +24,9 @@ export type SiteMeta = {
|
|||||||
articleDate?: string | undefined
|
articleDate?: string | undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export interface CategoryHierarchy {
|
export interface CategoryHierarchy {
|
||||||
category: string
|
category: string
|
||||||
fullCategory: string
|
fullCategory: string
|
||||||
children: Record<string, CategoryHierarchy>
|
children: Record<string, CategoryHierarchy>
|
||||||
count: number
|
count: number
|
||||||
}
|
}
|
||||||
|
@ -3,11 +3,15 @@ export { elementHasClass, rootInDarkMode, toggleClass } from './domElement'
|
|||||||
export { generateToc } from './generateToc'
|
export { generateToc } from './generateToc'
|
||||||
export type { TocItem } from './generateToc'
|
export type { TocItem } from './generateToc'
|
||||||
export {
|
export {
|
||||||
getAllCategories, getAllPosts,
|
getAllCategories,
|
||||||
getAllSortedPosts, getUniqueCategories,
|
getAllPosts,
|
||||||
getUniqueCategoriesWithCount, getUniqueTags,
|
getAllSortedPosts,
|
||||||
getUniqueTagsWithCount, getAllDiaries as getallDiaries,
|
getUniqueCategories,
|
||||||
getAllDiariesSorted as getallDiariesSorted, sortMDByDate
|
getUniqueCategoriesWithCount,
|
||||||
|
getUniqueTags,
|
||||||
|
getUniqueTagsWithCount,
|
||||||
|
getAllDiaries as getallDiaries,
|
||||||
|
getAllDiariesSorted as getallDiariesSorted,
|
||||||
|
sortMDByDate
|
||||||
} from './post'
|
} from './post'
|
||||||
export { cn } from './tailwind'
|
export { cn } from './tailwind'
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user