优化结构

This commit is contained in:
2026-06-18 21:44:37 +08:00 Unverified
parent 3051fe2d6a
commit cb244eb983
74 changed files with 94 additions and 144 deletions
+10 -13
View File
@@ -1,4 +1,4 @@
name: 自动部署 name: 鑷姩閮ㄧ讲
on: on:
push: push:
branches: branches:
@@ -13,11 +13,11 @@ jobs:
deploy: deploy:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: 检查分支 - name: 妫€鏌ュ垎鏀?
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
ref: master ref: master
- name: 缓存项目 npm - name: 缂撳瓨椤圭洰 npm 鍖?
id: cache-node-modules id: cache-node-modules
uses: actions/cache@v3 uses: actions/cache@v3
with: with:
@@ -25,31 +25,28 @@ jobs:
key: ${{ runner.os }}-nodeModules-${{ hashFiles('package-lock.json') }}-${{ hashFiles('package.json') }} key: ${{ runner.os }}-nodeModules-${{ hashFiles('package-lock.json') }}-${{ hashFiles('package.json') }}
restore-keys: | restore-keys: |
${{ runner.os }}-nodeModules- ${{ runner.os }}-nodeModules-
- name: 安装 Node - name: 瀹夎 Node
uses: actions/setup-node@v4 uses: actions/setup-node@v4
with: with:
node-version: "22.x" node-version: "22.x"
- name: 安装 Hexo - name: 瀹夎渚濊禆
run: |
npm install hexo-cli --global
- name: 安装依赖
if: steps.cache-node-modules.outputs.cache-hit != 'true' if: steps.cache-node-modules.outputs.cache-hit != 'true'
run: | run: |
npm install npm install
- name: 清理文件树 - name: 娓呯悊鏂囦欢鏍?
run: | run: |
npm run clean npm run clean
- name: 生成静态文件并压缩 - name: 鐢熸垚闈欐€佹枃浠跺苟鍘嬬缉
run: | run: |
npm run build npm run build
- name: 部署 - name: 閮ㄧ讲
run: | run: |
cd ./public cd ./dist
git init git init
git config user.name "${{ gitea.actor }}" git config user.name "${{ gitea.actor }}"
git config user.email "${{ gitea.actor }}@noreply.gitea.io" git config user.email "${{ gitea.actor }}@noreply.gitea.io"
git add . git add .
git commit -m "${{ gitea.event.head_commit.message }}··[$(date +"%Z %Y-%m-%d %A %H:%M:%S")]" git commit -m "${{ gitea.event.head_commit.message }}路路[$(date +"%Z %Y-%m-%d %A %H:%M:%S")]"
git push --force --quiet "https://${{ gitea.actor }}:${{ secrets.DEPLOY_TOKEN }}@git.biss.click/biss/blog.git" master:page git push --force --quiet "https://${{ gitea.actor }}:${{ secrets.DEPLOY_TOKEN }}@git.biss.click/biss/blog.git" master:page
- name: Deploy to Server - name: Deploy to Server
run: | run: |
+1 -1
View File
@@ -4,7 +4,7 @@ db.json
*.log *.log
node_modules/ node_modules/
.astro/ .astro/
public/ dist/
.deploy*/ .deploy*/
_multiconfig.yml _multiconfig.yml
.hintrc .hintrc
-2
View File
@@ -8,8 +8,6 @@ import remarkHexoLinkCards from './src/lib/remarkHexoLinkCards.mjs';
export default defineConfig({ export default defineConfig({
site: 'https://blog.biss.click', site: 'https://blog.biss.click',
output: 'static', output: 'static',
outDir: './public',
publicDir: './static',
integrations: [sitemap()], integrations: [sitemap()],
markdown: { markdown: {
processor: unified({ processor: unified({

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Before

Width:  |  Height:  |  Size: 457 KiB

After

Width:  |  Height:  |  Size: 457 KiB

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Before

Width:  |  Height:  |  Size: 269 KiB

After

Width:  |  Height:  |  Size: 269 KiB

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Before

Width:  |  Height:  |  Size: 64 KiB

After

Width:  |  Height:  |  Size: 64 KiB

+1 -1
View File
@@ -1,7 +1,7 @@
import { readdir, readFile, writeFile } from 'node:fs/promises'; import { readdir, readFile, writeFile } from 'node:fs/promises';
import { basename, extname, join, relative } from 'node:path'; import { basename, extname, join, relative } from 'node:path';
const POSTS_DIR = join(process.cwd(), 'posts'); const POSTS_DIR = join(process.cwd(), 'src', 'content', 'posts');
const CRC32_TABLE = new Uint32Array(256); const CRC32_TABLE = new Uint32Array(256);
+19
View File
@@ -0,0 +1,19 @@
import { defineCollection, z } from 'astro:content';
import { glob } from 'astro/loaders';
const posts = defineCollection({
loader: glob({ pattern: '**/*.md', base: './src/content/posts' }),
schema: z.object({
title: z.any().optional(),
date: z.any().optional(),
abbrlink: z.any().optional(),
tags: z.any().optional(),
categories: z.any().optional(),
summary: z.any().optional(),
description: z.any().optional(),
comments: z.any().optional(),
cover: z.any().optional(),
}).passthrough(),
});
export const collections = { posts };
-37
View File
@@ -1,37 +0,0 @@
type PageModule = {
frontmatter: Record<string, unknown>;
Content: unknown;
rawContent?: () => string;
};
export type SitePage = PageModule & {
title: string;
slug: string;
};
const modules = import.meta.glob<PageModule>('/source/**/index.md', { eager: true });
export function getSitePages(): SitePage[] {
return Object.entries(modules)
.filter(([path]) => {
if (path.includes('/_posts/') || path.includes('/_drafts/')) return false;
return ![
'/source/about/index.md',
'/source/cc/index.md',
'/source/cookie/index.md',
'/source/link/index.md',
'/source/privacy/index.md',
'/source/categories/index.md',
'/source/tags/index.md',
'/source/shuoshuo/index.md',
].includes(path);
})
.map(([path, module]) => {
const slug = path.replace('/source/', '').replace('/index.md', '');
return {
...module,
title: String(module.frontmatter.title ?? slug),
slug,
};
});
}
+29 -31
View File
@@ -1,11 +1,8 @@
type MarkdownModule = { import type { CollectionEntry } from 'astro:content';
frontmatter: Record<string, unknown>; import { getCollection } from 'astro:content';
Content: unknown;
rawContent?: () => string;
file?: string;
};
export type BlogPost = MarkdownModule & { export type BlogPost = {
entry: CollectionEntry<'posts'>;
title: string; title: string;
slug: string; slug: string;
date: Date; date: Date;
@@ -16,10 +13,9 @@ export type BlogPost = MarkdownModule & {
comments: boolean; comments: boolean;
cover?: string; cover?: string;
href: string; href: string;
body: string;
}; };
const modules = import.meta.glob<MarkdownModule>('/posts/**/*.md', { eager: true });
function asArray(value: unknown): string[] { function asArray(value: unknown): string[] {
if (Array.isArray(value)) return value.map(String).filter(Boolean); if (Array.isArray(value)) return value.map(String).filter(Boolean);
if (typeof value === 'string' && value.trim()) return [value.trim()]; if (typeof value === 'string' && value.trim()) return [value.trim()];
@@ -43,20 +39,19 @@ function formatDate(date: Date): string {
}).format(date); }).format(date);
} }
function fallbackSlug(path: string): string { function fallbackSlug(id: string): string {
return path return id
.split('/') .split('/')
.pop() .pop()
?.replace(/\.md$/, '') ?.replace(/\.md$/, '')
.toLowerCase() ?? 'post'; .toLowerCase() ?? 'post';
} }
function makeSummary(module: MarkdownModule): string { function makeSummary(entry: CollectionEntry<'posts'>): string {
const explicit = module.frontmatter.summary ?? module.frontmatter.description; const explicit = entry.data.summary ?? entry.data.description;
if (typeof explicit === 'string' && explicit.trim()) return explicit.trim(); if (typeof explicit === 'string' && explicit.trim()) return explicit.trim();
const raw = module.rawContent?.() ?? ''; return entry.body
return raw
.replace(/```[\s\S]*?```/g, '') .replace(/```[\s\S]*?```/g, '')
.replace(/<[^>]+>/g, '') .replace(/<[^>]+>/g, '')
.replace(/[#>*_`~\-\[\]\(\)]/g, '') .replace(/[#>*_`~\-\[\]\(\)]/g, '')
@@ -65,34 +60,37 @@ function makeSummary(module: MarkdownModule): string {
.slice(0, 120); .slice(0, 120);
} }
export function getAllPosts(): BlogPost[] { export async function getAllPosts(): Promise<BlogPost[]> {
return Object.entries(modules) const entries = await getCollection('posts');
.map(([path, module]) => {
const date = toDate(module.frontmatter.date); return entries
const slug = String(module.frontmatter.abbrlink ?? fallbackSlug(path)); .map((entry) => {
const title = String(module.frontmatter.title ?? slug); const date = toDate(entry.data.date);
const slug = String(entry.data.abbrlink ?? fallbackSlug(entry.id));
const title = String(entry.data.title ?? slug);
return { return {
...module, entry,
title, title,
slug, slug,
date, date,
dateText: formatDate(date), dateText: formatDate(date),
tags: asArray(module.frontmatter.tags), tags: asArray(entry.data.tags),
categories: asArray(module.frontmatter.categories), categories: asArray(entry.data.categories),
summary: makeSummary(module), summary: makeSummary(entry),
comments: module.frontmatter.comments !== false, comments: entry.data.comments !== false,
cover: typeof module.frontmatter.cover === 'string' ? module.frontmatter.cover : undefined, cover: typeof entry.data.cover === 'string' ? entry.data.cover : undefined,
href: `/posts/${slug}/`, href: `/posts/${slug}/`,
body: entry.body,
}; };
}) })
.sort((a, b) => b.date.valueOf() - a.date.valueOf()); .sort((a, b) => b.date.valueOf() - a.date.valueOf());
} }
export function getAllTags(): string[] { export async function getAllTags(): Promise<string[]> {
return [...new Set(getAllPosts().flatMap((post) => post.tags))].sort((a, b) => a.localeCompare(b, 'zh-CN')); return [...new Set((await getAllPosts()).flatMap((post) => post.tags))].sort((a, b) => a.localeCompare(b, 'zh-CN'));
} }
export function getAllCategories(): string[] { export async function getAllCategories(): Promise<string[]> {
return [...new Set(getAllPosts().flatMap((post) => post.categories))].sort((a, b) => a.localeCompare(b, 'zh-CN')); return [...new Set((await getAllPosts()).flatMap((post) => post.categories))].sort((a, b) => a.localeCompare(b, 'zh-CN'));
} }
-26
View File
@@ -1,26 +0,0 @@
---
import BaseLayout from '@/layouts/BaseLayout.astro';
import PageHeaderCard from '@/components/PageHeaderCard.astro';
import { getSitePages } from '@/lib/pages';
export function getStaticPaths() {
return getSitePages().map((page) => ({
params: { slug: page.slug },
props: { page },
}));
}
const { page } = Astro.props;
const { Content } = page;
---
<BaseLayout title={page.title}>
<section class="content-wrap narrow page-with-card">
<PageHeaderCard title={page.title} />
</section>
<article class="article page-article">
<div class="prose">
<Content />
</div>
</article>
</BaseLayout>
+2 -2
View File
@@ -1,7 +1,7 @@
import { getAllPosts } from '@/lib/posts'; import { getAllPosts } from '@/lib/posts';
export function GET() { export async function GET() {
const posts = getAllPosts().map((post) => ({ const posts = (await getAllPosts()).map((post) => ({
title: post.title, title: post.title,
href: post.href, href: post.href,
})); }));
+1 -1
View File
@@ -5,7 +5,7 @@ import PageHeaderCard from '@/components/PageHeaderCard.astro';
import { getAllPosts } from '@/lib/posts'; import { getAllPosts } from '@/lib/posts';
import { getPageSlice } from '@/lib/pagination'; import { getPageSlice } from '@/lib/pagination';
const posts = getAllPosts(); const posts = await getAllPosts();
const pageData = getPageSlice(posts, 1); const pageData = getPageSlice(posts, 1);
--- ---
+3 -3
View File
@@ -5,8 +5,8 @@ import PageHeaderCard from '@/components/PageHeaderCard.astro';
import { getAllPosts } from '@/lib/posts'; import { getAllPosts } from '@/lib/posts';
import { getPageSlice, getPaginationPaths } from '@/lib/pagination'; import { getPageSlice, getPaginationPaths } from '@/lib/pagination';
export function getStaticPaths() { export async function getStaticPaths() {
const posts = getAllPosts(); const posts = await getAllPosts();
return getPaginationPaths(posts).map((page) => ({ return getPaginationPaths(posts).map((page) => ({
params: { page: String(page) }, params: { page: String(page) },
@@ -15,7 +15,7 @@ export function getStaticPaths() {
} }
const { page } = Astro.props; const { page } = Astro.props;
const posts = getAllPosts(); const posts = await getAllPosts();
const pageData = getPageSlice(posts, page); const pageData = getPageSlice(posts, page);
--- ---
+3 -3
View File
@@ -4,15 +4,15 @@ import PageHeaderCard from '@/components/PageHeaderCard.astro';
import PostCard from '@/components/PostCard.astro'; import PostCard from '@/components/PostCard.astro';
import { getAllCategories, getAllPosts } from '@/lib/posts'; import { getAllCategories, getAllPosts } from '@/lib/posts';
export function getStaticPaths() { export async function getStaticPaths() {
return getAllCategories().map((category) => ({ return (await getAllCategories()).map((category) => ({
params: { category }, params: { category },
props: { category }, props: { category },
})); }));
} }
const { category } = Astro.props; const { category } = Astro.props;
const posts = getAllPosts().filter((post) => post.categories.includes(category)); const posts = (await getAllPosts()).filter((post) => post.categories.includes(category));
--- ---
<BaseLayout title={category}> <BaseLayout title={category}>
+2 -2
View File
@@ -3,8 +3,8 @@ import BaseLayout from '@/layouts/BaseLayout.astro';
import PageHeaderCard from '@/components/PageHeaderCard.astro'; import PageHeaderCard from '@/components/PageHeaderCard.astro';
import { getAllCategories, getAllPosts } from '@/lib/posts'; import { getAllCategories, getAllPosts } from '@/lib/posts';
const posts = getAllPosts(); const posts = await getAllPosts();
const categories = getAllCategories(); const categories = await getAllCategories();
--- ---
<BaseLayout title="分类"> <BaseLayout title="分类">
+3 -3
View File
@@ -6,9 +6,9 @@ import PostCard from '@/components/PostCard.astro';
import { getAllCategories, getAllPosts, getAllTags } from '@/lib/posts'; import { getAllCategories, getAllPosts, getAllTags } from '@/lib/posts';
import { getPageSlice } from '@/lib/pagination'; import { getPageSlice } from '@/lib/pagination';
const posts = getAllPosts(); const posts = await getAllPosts();
const tags = getAllTags(); const tags = await getAllTags();
const categories = getAllCategories(); const categories = await getAllCategories();
const pageData = getPageSlice(posts, 1); const pageData = getPageSlice(posts, 1);
--- ---
+5 -5
View File
@@ -6,8 +6,8 @@ import BaseLayout from '@/layouts/BaseLayout.astro';
import { getAllCategories, getAllPosts, getAllTags } from '@/lib/posts'; import { getAllCategories, getAllPosts, getAllTags } from '@/lib/posts';
import { getPageSlice, getPaginationPaths } from '@/lib/pagination'; import { getPageSlice, getPaginationPaths } from '@/lib/pagination';
export function getStaticPaths() { export async function getStaticPaths() {
const posts = getAllPosts(); const posts = await getAllPosts();
return getPaginationPaths(posts).map((page) => ({ return getPaginationPaths(posts).map((page) => ({
params: { page: String(page) }, params: { page: String(page) },
@@ -16,9 +16,9 @@ export function getStaticPaths() {
} }
const { page } = Astro.props; const { page } = Astro.props;
const posts = getAllPosts(); const posts = await getAllPosts();
const tags = getAllTags(); const tags = await getAllTags();
const categories = getAllCategories(); const categories = await getAllCategories();
const pageData = getPageSlice(posts, page); const pageData = getPageSlice(posts, page);
--- ---
+7 -6
View File
@@ -3,19 +3,20 @@ import ArticleSidebar from '@/components/ArticleSidebar.astro';
import PostCopyrightCard from '@/components/PostCopyrightCard.astro'; import PostCopyrightCard from '@/components/PostCopyrightCard.astro';
import TwikooComments from '@/components/TwikooComments.astro'; import TwikooComments from '@/components/TwikooComments.astro';
import BaseLayout from '@/layouts/BaseLayout.astro'; import BaseLayout from '@/layouts/BaseLayout.astro';
import { render } from 'astro:content';
import { getAllPosts } from '@/lib/posts'; import { getAllPosts } from '@/lib/posts';
export function getStaticPaths() { export async function getStaticPaths() {
return getAllPosts().map((post) => ({ return (await getAllPosts()).map((post) => ({
params: { slug: post.slug }, params: { slug: post.slug },
props: { post }, props: { post },
})); }));
} }
const { post } = Astro.props; const { post } = Astro.props;
const { Content } = post; const { Content } = await render(post.entry);
const allPosts = getAllPosts(); const allPosts = await getAllPosts();
const rawContent = post.rawContent?.() ?? ''; const rawContent = post.body;
const textContent = rawContent const textContent = rawContent
.replace(/```[\s\S]*?```/g, '') .replace(/```[\s\S]*?```/g, '')
.replace(/<[^>]+>/g, '') .replace(/<[^>]+>/g, '')
@@ -69,7 +70,7 @@ function makeToc(raw: string) {
}); });
} }
const toc = makeToc(post.rawContent?.() ?? ''); const toc = makeToc(post.body);
const relatedPosts = allPosts const relatedPosts = allPosts
.filter((item) => item.slug !== post.slug) .filter((item) => item.slug !== post.slug)
.filter((item) => item.categories.some((category) => post.categories.includes(category))) .filter((item) => item.categories.some((category) => post.categories.includes(category)))
+2 -2
View File
@@ -1,8 +1,8 @@
import rss from '@astrojs/rss'; import rss from '@astrojs/rss';
import { getAllPosts } from '@/lib/posts'; import { getAllPosts } from '@/lib/posts';
export function GET(context) { export async function GET(context) {
const posts = getAllPosts(); const posts = await getAllPosts();
return rss({ return rss({
title: "Bi's Blog", title: "Bi's Blog",
+3 -3
View File
@@ -4,15 +4,15 @@ import PageHeaderCard from '@/components/PageHeaderCard.astro';
import PostCard from '@/components/PostCard.astro'; import PostCard from '@/components/PostCard.astro';
import { getAllPosts, getAllTags } from '@/lib/posts'; import { getAllPosts, getAllTags } from '@/lib/posts';
export function getStaticPaths() { export async function getStaticPaths() {
return getAllTags().map((tag) => ({ return (await getAllTags()).map((tag) => ({
params: { tag }, params: { tag },
props: { tag }, props: { tag },
})); }));
} }
const { tag } = Astro.props; const { tag } = Astro.props;
const posts = getAllPosts().filter((post) => post.tags.includes(tag)); const posts = (await getAllPosts()).filter((post) => post.tags.includes(tag));
--- ---
<BaseLayout title={tag}> <BaseLayout title={tag}>
+2 -2
View File
@@ -3,8 +3,8 @@ import BaseLayout from '@/layouts/BaseLayout.astro';
import PageHeaderCard from '@/components/PageHeaderCard.astro'; import PageHeaderCard from '@/components/PageHeaderCard.astro';
import { getAllPosts, getAllTags } from '@/lib/posts'; import { getAllPosts, getAllTags } from '@/lib/posts';
const posts = getAllPosts(); const posts = await getAllPosts();
const tags = getAllTags(); const tags = await getAllTags();
--- ---
<BaseLayout title="标签"> <BaseLayout title="标签">