chore: reorganize project into monorepo

This commit is contained in:
2026-03-28 10:40:22 +08:00
parent 60367a5f51
commit 1455d93246
201 changed files with 30081 additions and 93 deletions

View File

@@ -0,0 +1,208 @@
---
import BaseLayout from '../layouts/BaseLayout.astro';
import TerminalWindow from '../components/ui/TerminalWindow.astro';
import CommandPrompt from '../components/ui/CommandPrompt.astro';
import FilterPill from '../components/ui/FilterPill.astro';
import PostCard from '../components/PostCard.astro';
import FriendLinkCard from '../components/FriendLinkCard.astro';
import ViewMoreLink from '../components/ui/ViewMoreLink.astro';
import StatsList from '../components/StatsList.astro';
import TechStackList from '../components/TechStackList.astro';
import { terminalConfig } from '../lib/config/terminal';
import { api, DEFAULT_SITE_SETTINGS } from '../lib/api/client';
import type { AppFriendLink } from '../lib/api/client';
import type { Post } from '../lib/types';
export const prerender = false;
const url = new URL(Astro.request.url);
const selectedType = url.searchParams.get('type') || 'all';
let siteSettings = DEFAULT_SITE_SETTINGS;
let allPosts: Post[] = [];
let recentPosts: Post[] = [];
let pinnedPost: Post | null = null;
let tags: string[] = [];
let friendLinks: AppFriendLink[] = [];
let categories: Awaited<ReturnType<typeof api.getCategories>> = [];
let apiError: string | null = null;
try {
siteSettings = await api.getSiteSettings();
allPosts = await api.getPosts();
const filteredPosts = selectedType === 'all'
? allPosts
: allPosts.filter(post => post.type === selectedType);
recentPosts = filteredPosts.slice(0, 5);
pinnedPost = filteredPosts.find(post => post.pinned) || null;
tags = (await api.getTags()).map(tag => tag.name);
friendLinks = (await api.getFriendLinks()).filter(friend => friend.status === 'approved');
categories = await api.getCategories();
} catch (error) {
apiError = error instanceof Error ? error.message : 'API unavailable';
console.error('API Error:', error);
}
const systemStats = [
{ label: '文章', value: String(allPosts.length) },
{ label: '标签', value: String(tags.length) },
{ label: '分类', value: String(categories.length) },
{ label: '友链', value: String(friendLinks.length) },
];
const techStack = siteSettings.techStack.map(name => ({ name }));
const postTypeFilters = [
{ id: 'all', name: '全部', icon: 'fa-stream' },
{ id: 'article', name: terminalConfig.postTypes.article.label, icon: 'fa-file-alt' },
{ id: 'tweet', name: terminalConfig.postTypes.tweet.label, icon: 'fa-comment-dots' }
];
---
<BaseLayout title={siteSettings.siteTitle} description={siteSettings.siteDescription}>
<div class="max-w-5xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<TerminalWindow title={terminalConfig.title} class="w-full">
<div class="mb-6 px-4 overflow-x-auto">
<pre class="font-mono text-xs sm:text-sm text-[var(--primary)] whitespace-pre">{terminalConfig.asciiArt}</pre>
</div>
<div class="mb-8 px-4">
<CommandPrompt command="cat welcome.txt" />
<div class="ml-4">
<p class="text-lg font-bold text-[var(--primary)] mb-1">{siteSettings.heroTitle}</p>
<p class="text-[var(--text-secondary)]">{siteSettings.heroSubtitle}</p>
</div>
</div>
<div class="mb-8 px-4">
<CommandPrompt command="ls -l" />
<div class="ml-4 grid grid-cols-2 sm:grid-cols-3 md:grid-cols-6 gap-4">
{terminalConfig.navLinks.map(link => (
<a
href={link.href}
class="flex items-center gap-2 text-[var(--text)] hover:text-[var(--primary)] transition-colors py-2"
>
<i class={`fas ${link.icon}`}></i>
<span>{link.text}</span>
</a>
))}
</div>
</div>
{apiError && (
<div class="mb-8 px-4">
<div class="ml-4 p-4 rounded-lg border border-[var(--danger)]/20 bg-[var(--danger)]/10 text-[var(--danger)] text-sm">
{apiError}
</div>
</div>
)}
<div id="categories" class="mb-8 px-4">
<CommandPrompt command="ls -l ./categories" />
<div class="ml-4 flex flex-wrap gap-2">
{categories.map(category => (
<FilterPill tone="amber" href={`/articles?category=${encodeURIComponent(category.name)}`}>
<i class="fas fa-folder"></i>
<span>{category.name}</span>
</FilterPill>
))}
</div>
</div>
<div class="mb-4 px-4">
<CommandPrompt command="./filter_posts.sh" />
<div class="ml-4 flex flex-wrap gap-2">
{postTypeFilters.map(filter => (
<FilterPill tone="blue" active={selectedType === filter.id} href={`/?type=${filter.id}`}>
<i class={`fas ${filter.icon}`}></i>
<span>{filter.name}</span>
</FilterPill>
))}
</div>
</div>
{pinnedPost && (
<div class="mb-8 px-4">
<CommandPrompt command="cat ./pinned_post.md" />
<div class="ml-4">
<div
class="p-4 rounded-lg border border-[var(--border-color)] bg-[var(--header-bg)] cursor-pointer hover:border-[var(--primary)] transition-colors"
onclick={`window.location.href='/articles/${pinnedPost.slug}'`}
>
<div class="flex items-center gap-2 mb-2">
<span class="px-2 py-0.5 text-xs rounded bg-[var(--primary)] text-[var(--terminal-bg)] font-bold">置顶</span>
<span class="w-3 h-3 rounded-full" style={`background-color: ${pinnedPost.type === 'article' ? 'var(--primary)' : 'var(--secondary)'}`}></span>
<h3 class="text-lg font-bold">{pinnedPost.title}</h3>
</div>
<p class="text-sm text-[var(--text-secondary)] mb-2">{pinnedPost.date} | 阅读时间: {pinnedPost.readTime}</p>
<p class="text-[var(--text-secondary)]">{pinnedPost.description}</p>
</div>
</div>
</div>
)}
<div id="posts" class="mb-10 px-4">
<CommandPrompt command="find ./posts -type f -name *.md | head -n 5" />
<div class="ml-4">
<div class="divide-y divide-[var(--border-color)]">
{recentPosts.map(post => (
<PostCard post={post} />
))}
</div>
<div class="mt-4">
<ViewMoreLink href="/articles" text="查看所有文章" />
</div>
</div>
</div>
<div id="tags" class="mb-10 px-4">
<CommandPrompt command="cat ./tags.txt" />
<div class="ml-4 flex flex-wrap gap-2">
{tags.map(tag => (
<FilterPill tone="teal" href={`/tags?tag=${encodeURIComponent(tag)}`}>
<i class="fas fa-hashtag"></i>
<span>{tag}</span>
</FilterPill>
))}
</div>
</div>
<div class="border-t border-[var(--border-color)] my-10"></div>
<div id="friends" class="mb-10 px-4">
<CommandPrompt command="cat ./friends.txt" />
<div class="ml-4 grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
{friendLinks.map(friend => (
<FriendLinkCard friend={friend} />
))}
</div>
<div class="mt-6 ml-4">
<ViewMoreLink href="/friends" text="查看全部友链" />
</div>
</div>
<div class="border-t border-[var(--border-color)] my-10"></div>
<div id="about" class="px-4">
<CommandPrompt command="cat about_me.txt" />
<div class="ml-4">
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<h3 class="text-lg font-bold mb-3 text-[var(--title-color)]">关于我</h3>
<p class="text-[var(--text-secondary)] mb-4">{siteSettings.ownerBio}</p>
<h3 class="text-lg font-bold mb-3 text-[var(--title-color)]">技术栈</h3>
<TechStackList items={techStack} />
</div>
<div>
<h3 class="text-lg font-bold mb-3 text-[var(--title-color)]">系统状态</h3>
<StatsList stats={systemStats} />
</div>
</div>
</div>
</div>
</TerminalWindow>
</div>
</BaseLayout>