本文档详细介绍 Personal Blog 博客系统的技术架构、设计决策和实现细节。
版本: v1.4.0
更新日期: 2026-02-17
Personal Blog 采用现代化的前后端分离架构,基于 Cloudflare 边缘计算平台构建,实现全球低延迟访问。
┌─────────────────────────────────────────────────────────────────────────────┐
│ 客户端层 │
├─────────────────────────────────────────────────────────────────────────────┤
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │ 桌面浏览器 │ │ 移动浏览器 │ │ 搜索引擎爬虫 │ │
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ Cloudflare 边缘层 │
├─────────────────────────────────────────────────────────────────────────────┤
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ Cloudflare CDN (全球300+节点) │ │
│ │ - 静态资源缓存 │ │
│ │ - DDoS 防护 │ │
│ │ - SSL 终止 │ │
│ │ - 智能路由 │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ┌───────────────────┴───────────────────┐ │
│ ▼ ▼ │
│ ┌───────────────────────────┐ ┌───────────────────────────┐ │
│ │ Cloudflare Pages │ │ Cloudflare Workers │ │
│ │ (前端应用) │ │ (后端 API) │ │
│ │ │ │ │ │
│ │ - React 18 SPA │ │ - Hono 4.x 框架 │ │
│ │ - Vite 构建 │ │ - TypeScript │ │
│ │ - Tailwind CSS │ │ - 边缘计算 │ │
│ │ - Zustand 状态管理 │ │ - JWT 认证 │ │
│ └───────────────────────────┘ └───────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ 数据存储层 │
├─────────────────────────────────────────────────────────────────────────────┤
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │ D1 数据库 │ │ R2 存储 │ │ KV 缓存 │ │
│ │ (SQLite) │ │ (对象存储) │ │ (键值存储) │ │
│ │ │ │ │ │ │ │
│ │ - 用户数据 │ │ - 图片文件 │ │ - 会话缓存 │ │
│ │ - 文章内容 │ │ - 附件文件 │ │ - 热点数据 │ │
│ │ - 评论数据 │ │ - 头像图片 │ │ - 限流计数 │ │
│ │ - 通知私信 │ │ │ │ │ │
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ 第三方服务层 │
├─────────────────────────────────────────────────────────────────────────────┤
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ Resend │ │ GitHub OAuth │ │
│ │ (邮件服务) │ │ (第三方登录) │ │
│ └─────────────────┘ └─────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
- 边缘优先: 所有计算在离用户最近的边缘节点执行
- 无服务器架构: 无需管理服务器,自动扩缩容
- 成本优化: 免费额度足够个人博客使用
- 安全默认: 内置 DDoS 防护、SSL 加密
- 开发体验: TypeScript 全栈,类型安全
| 技术 | 版本 | 用途 | 选择理由 |
|---|---|---|---|
| React | 18.2.0 | UI 框架 | 生态丰富、社区活跃、性能优秀 |
| TypeScript | 5.2.2 | 类型系统 | 类型安全、开发体验好 |
| Vite | 7.3.1 | 构建工具 | 快速冷启动、HMR、优化构建 |
| Tailwind CSS | 3.4.0 | 样式框架 | 原子化 CSS、快速开发 |
| Zustand | 4.4.7 | 状态管理 | 轻量、简单、TypeScript 友好 |
| React Router | 6.21.0 | 路由管理 | 声明式路由、代码分割 |
| React Markdown | 9.0.1 | Markdown 渲染 | 安全、可扩展 |
| Framer Motion | 12.34.0 | 动画效果 | 声明式动画、性能优秀 |
| date-fns | 3.0.6 | 日期处理 | 轻量、模块化 |
| 技术 | 版本 | 用途 | 选择理由 |
|---|---|---|---|
| Hono | 4.11.9 | Web 框架 | 轻量、快速、TypeScript 原生支持 |
| Cloudflare Workers | - | 运行时 | 边缘计算、全球部署、免费额度 |
| Cloudflare D1 | - | 数据库 | SQLite 兼容、边缘部署 |
| Cloudflare R2 | - | 对象存储 | S3 兼容、无出站费用 |
| Cloudflare KV | - | 缓存 | 边缘缓存、低延迟 |
| bcryptjs | 3.0.3 | 密码哈希 | 安全、兼容性好 |
| jose | 5.9.6 | JWT 处理 | 现代化、支持多种算法 |
frontend/src/
├── pages/ # 页面组件(路由级别)
│ ├── HomePage.tsx # 首页
│ ├── PostPage.tsx # 文章详情
│ ├── LoginPage.tsx # 登录页
│ ├── AdminPage.tsx # 管理后台
│ ├── SearchPage.tsx # 搜索页
│ ├── ProfilePage.tsx # 个人中心
│ ├── ReadingHistoryPage.tsx # 阅读历史
│ ├── AccountSettingsPage.tsx # 账号设置
│ ├── ColumnPage.tsx # 专栏详情
│ ├── CategoryPage.tsx # 分类页
│ ├── TagPage.tsx # 标签页
│ ├── AboutPage.tsx # 关于页面
│ ├── ConfigPage.tsx # 配置页面
│ ├── NotificationCenter.tsx # 通知中心
│ ├── NotificationSettings.tsx # 通知设置
│ ├── MessageSettings.tsx # 私信设置
│ ├── MessagesPage.tsx # 私信列表
│ ├── NewMessagePage.tsx # 发起新私信
│ ├── ThreadPage.tsx # 私信会话
│ ├── ApiTestPage.tsx # API测试页
│ ├── NotFoundPage.tsx # 404页面
│ └── admin/ # 管理页面
│ └── SystemNotificationPage.tsx
├── components/ # 可复用组件
│ ├── Layout.tsx # 布局组件
│ ├── Navbar.tsx # 导航栏
│ ├── Footer.tsx # 页脚
│ ├── PostCard.tsx # 文章卡片
│ ├── CommentSection.tsx # 评论组件
│ ├── Sidebar.tsx # 侧边栏
│ ├── HotPostsWidget.tsx # 热门文章组件(v1.4.0新增)
│ ├── PostNavigation.tsx # 上下篇导航(v1.4.0新增)
│ ├── RecommendedPosts.tsx # 推荐文章(v1.4.0新增)
│ └── ...
├── stores/ # Zustand 状态管理
│ ├── authStore.ts # 认证状态
│ ├── themeStore.ts # 主题状态(v1.4.0增强)
│ └── notificationStore.ts # 通知状态
├── hooks/ # 自定义 Hooks
│ ├── useAuth.ts # 认证 Hook
│ ├── useTheme.ts # 主题 Hook
│ └── ...
├── utils/ # 工具函数
│ ├── api.ts # API 请求封装
│ ├── helpers.ts # 通用工具
│ └── constants.ts # 常量定义
├── types/ # TypeScript 类型定义
│ ├── index.ts # 类型导出
│ └── ...
├── App.tsx # 应用入口
└── main.tsx # 渲染入口
使用 Zustand 进行状态管理,主要包含以下 Store:
interface AuthState {
user: User | null;
isAuthenticated: boolean;
isLoading: boolean;
login: (email: string, password: string) => Promise<void>;
logout: () => void;
checkAuth: () => Promise<void>;
}interface ThemeState {
mode: 'light' | 'dark' | 'system';
primaryColor: string; // v1.4.0新增:自定义主色调
fontSize: 'small' | 'medium' | 'large'; // v1.4.0新增:字体大小
setMode: (mode: 'light' | 'dark' | 'system') => void;
setPrimaryColor: (color: string) => void;
setFontSize: (size: 'small' | 'medium' | 'large') => void;
}const routes = [
{ path: '/', element: <HomePage /> },
{ path: '/posts/:slug', element: <PostPage /> },
{ path: '/login', element: <LoginPage /> },
{ path: '/admin/*', element: <AdminPage />, protected: true },
{ path: '/search', element: <SearchPage /> },
{ path: '/profile', element: <ProfilePage />, protected: true },
{ path: '/reading-history', element: <ReadingHistoryPage />, protected: true },
{ path: '/account-settings', element: <AccountSettingsPage />, protected: true },
{ path: '/columns/:slug', element: <ColumnPage /> },
{ path: '/categories/:slug', element: <CategoryPage /> },
{ path: '/tags/:slug', element: <TagPage /> },
{ path: '/about', element: <AboutPage /> },
{ path: '/notifications', element: <NotificationCenter />, protected: true },
{ path: '/messages', element: <MessagesPage />, protected: true },
{ path: '/messages/:threadId', element: <ThreadPage />, protected: true },
{ path: '*', element: <NotFoundPage /> },
];- 单一职责: 每个组件只负责一件事
- 组合优于继承: 使用组合模式构建复杂组件
- 受控组件: 表单组件使用受控模式
- 性能优化: 使用 memo、useMemo、useCallback 优化渲染
backend/src/
├── index.ts # 应用入口
├── routes/ # API 路由
│ ├── auth.ts # 认证路由
│ ├── posts.ts # 文章路由
│ ├── columns.ts # 专栏路由
│ ├── comments.ts # 评论路由
│ ├── admin.ts # 管理路由
│ ├── categories.ts # 分类路由
│ ├── config.ts # 配置路由
│ ├── upload.ts # 上传路由
│ ├── analytics.ts # 统计路由
│ ├── notifications.ts # 通知路由
│ ├── adminNotifications.ts # 管理员通知路由
│ ├── messages.ts # 私信路由
│ ├── users.ts # 用户路由
│ └── users/ # 用户子路由
│ ├── notificationSettings.ts
│ └── messageSettings.ts
├── middleware/ # 中间件
│ ├── auth.ts # 认证中间件
│ ├── rateLimit.ts # 限流中间件
│ └── requestLogger.ts # 请求日志
├── services/ # 业务服务层
│ ├── digestService.ts # 邮件汇总服务
│ ├── doNotDisturb.ts # 免打扰服务
│ ├── emailVerificationService.ts
│ ├── mentionService.ts # @提及服务
│ ├── messageService.ts # 私信服务
│ ├── messageSettingsService.ts
│ ├── notificationService.ts
│ ├── notificationSettingsService.ts
│ └── postService.ts # 文章服务
├── utils/ # 工具函数
│ ├── cache.ts # 缓存工具
│ ├── jwt.ts # JWT 工具
│ ├── queryOptimizer.ts # 查询优化
│ ├── resend.ts # 邮件发送
│ ├── response.ts # 响应格式化
│ ├── softDeleteHelper.ts # 软删除助手
│ └── validation.ts # 数据验证
├── types/ # 类型定义
│ └── index.ts
└── config/ # 配置文件
└── index.ts
GET /api/posts # 获取文章列表
GET /api/posts/:id # 获取单篇文章
POST /api/posts # 创建文章
PUT /api/posts/:id # 更新文章
DELETE /api/posts/:id # 删除文章
GET /api/posts/hot # 获取热门文章(v1.4.0新增)
GET /api/posts/:id/related # 获取相关文章(v1.4.0新增)
GET /api/posts/:id/neighbors # 获取上下篇(v1.4.0新增)
// 请求处理流程
app.use('*', cors()) // CORS 处理
app.use('*', logger()) // 日志记录
app.use('*', rateLimit()) // 限流
app.use('/api/*', auth()) // 认证(需要认证的路由)
app.route('/api', routes) // 业务路由
app.notFound(notFoundHandler) // 404 处理
app.onError(errorHandler) // 错误处理服务层封装业务逻辑,提供可复用的业务方法:
// postService.ts
export class PostService {
// 获取热门文章(v1.4.0新增)
static async getHotPosts(limit: number): Promise<Post[]>
// 获取相关推荐文章(v1.4.0新增)
static async getRelatedPosts(postId: number, limit: number): Promise<Post[]>
// 获取上下篇文章(v1.4.0新增)
static async getNeighbors(postId: number): Promise<{ prev: Post | null, next: Post | null }>
// 文章置顶(v1.4.0新增)
static async togglePin(postId: number, isPinned: boolean): Promise<void>
}| 表名 | 说明 | 主要字段 |
|---|---|---|
| users | 用户表 | id, email, password, role, is_deleted |
| posts | 文章表 | id, title, content, is_pinned, view_count |
| columns | 专栏表 | id, name, description, post_count |
| categories | 分类表 | id, name, slug |
| tags | 标签表 | id, name, slug |
| comments | 评论表 | id, post_id, user_id, content, parent_id |
| post_likes | 点赞表 | id, post_id, user_id |
| post_favorites | 收藏表 | id, post_id, user_id |
| reading_history | 阅读历史 | id, user_id, post_id, read_at |
| notifications | 通知表 | id, user_id, type, content, is_read |
| messages | 私信表 | id, thread_id, sender_id, content, is_recalled |
| message_threads | 会话表 | id, participant_ids, last_message_at |
| notification_settings | 通知设置 | id, user_id, settings |
| message_settings | 私信设置 | id, user_id, allow_strangers |
| system_notifications | 系统通知 | id, title, content, is_active |
| config | 系统配置 | id, key, value |
CREATE TABLE posts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL,
slug TEXT UNIQUE NOT NULL,
content TEXT NOT NULL,
excerpt TEXT,
cover_image TEXT,
author_id INTEGER NOT NULL,
column_id INTEGER,
category_id INTEGER,
status TEXT DEFAULT 'draft',
is_pinned INTEGER DEFAULT 0, -- v1.4.0新增:置顶标记
is_password_protected INTEGER DEFAULT 0,
password_hash TEXT,
view_count INTEGER DEFAULT 0,
like_count INTEGER DEFAULT 0,
comment_count INTEGER DEFAULT 0,
seo_title TEXT,
seo_description TEXT,
seo_keywords TEXT,
created_at TEXT DEFAULT (datetime('now')),
updated_at TEXT DEFAULT (datetime('now')),
published_at TEXT,
deleted_at TEXT,
FOREIGN KEY (author_id) REFERENCES users(id),
FOREIGN KEY (column_id) REFERENCES columns(id),
FOREIGN KEY (category_id) REFERENCES categories(id)
);
-- 索引
CREATE INDEX idx_posts_slug ON posts(slug);
CREATE INDEX idx_posts_status ON posts(status);
CREATE INDEX idx_posts_is_pinned ON posts(is_pinned); -- v1.4.0新增
CREATE INDEX idx_posts_view_count ON posts(view_count DESC); -- 热门文章
CREATE INDEX idx_posts_published_at ON posts(published_at DESC);CREATE TABLE users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
email TEXT UNIQUE NOT NULL,
password TEXT,
username TEXT,
avatar TEXT,
bio TEXT,
role TEXT DEFAULT 'user',
is_email_verified INTEGER DEFAULT 0,
github_id TEXT,
is_deleted INTEGER DEFAULT 0,
created_at TEXT DEFAULT (datetime('now')),
updated_at TEXT DEFAULT (datetime('now'))
);
CREATE INDEX idx_users_email ON users(email);
CREATE INDEX idx_users_github_id ON users(github_id);| 版本 | 迁移文件 | 变更内容 |
|---|---|---|
| v1.1 | schema-v1.1-base.sql | 基础表结构 |
| v1.3 | schema-v1.3-notification-messaging.sql | 通知和私信系统 |
| v1.4 | migration-v1.4-message-recall.sql | 消息撤回功能 |
| v1.9 | migration-v1.9-post-pinning.sql | 文章置顶功能 |
// 缓存策略
const CACHE_TTL = {
posts: 300, // 文章列表:5分钟
post: 3600, // 单篇文章:1小时
hotPosts: 600, // 热门文章:10分钟(v1.4.0新增)
categories: 3600, // 分类:1小时
tags: 3600, // 标签:1小时
config: 86400, // 配置:24小时
};
// 缓存键命名规范
const CACHE_KEYS = {
posts: 'posts:list',
post: (id: number) => `post:${id}`,
hotPosts: 'posts:hot',
relatedPosts: (id: number) => `post:${id}:related`,
categories: 'categories:all',
tags: 'tags:all',
};- 写时失效: 数据更新时主动清除相关缓存
- 定时刷新: 热点数据定时刷新
- LRU 淘汰: KV 自动淘汰最少使用的数据
┌─────────────────────────────────────────────────────────────┐
│ 认证流程 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. 用户登录 │
│ │ │
│ ▼ │
│ 2. 验证邮箱/密码 或 GitHub OAuth │
│ │ │
│ ▼ │
│ 3. 生成 JWT Token(有效期7天) │
│ │ │
│ ▼ │
│ 4. 返回 Token 给客户端 │
│ │ │
│ ▼ │
│ 5. 客户端存储 Token(localStorage) │
│ │ │
│ ▼ │
│ 6. 后续请求携带 Token(Authorization: Bearer xxx) │
│ │ │
│ ▼ │
│ 7. 服务端验证 Token 有效性 │
│ │
└─────────────────────────────────────────────────────────────┘
| 安全措施 | 实现方式 |
|---|---|
| 密码存储 | bcrypt 哈希(10轮) |
| JWT 签名 | HS256 算法,密钥长度 ≥ 32 字符 |
| CORS | 白名单机制,仅允许指定域名 |
| 限流 | KV 计数,IP 维度限流 |
| 文件上传 | 魔数验证,限制文件类型和大小 |
| SQL 注入 | 参数化查询 |
| XSS | React 自动转义 + DOMPurify |
| CSRF | SameSite Cookie + Token 验证 |
// 文件类型验证(魔数)
const ALLOWED_TYPES = {
'image/jpeg': [0xFF, 0xD8, 0xFF],
'image/png': [0x89, 0x50, 0x4E, 0x47],
'image/gif': [0x47, 0x49, 0x46, 0x38],
'image/webp': [0x52, 0x49, 0x46, 0x46],
};
// 文件大小限制
const MAX_FILE_SIZE = 5 * 1024 * 1024; // 5MB- 代码分割: 路由级别懒加载
- 资源优化: 图片懒加载、WebP 格式
- 缓存策略: Service Worker 缓存静态资源
- 渲染优化: 虚拟列表、防抖节流
- 查询优化: 索引优化、避免 N+1 查询
- 缓存策略: KV 缓存热点数据
- 响应压缩: Brotli/Gzip 压缩
- 边缘计算: Workers 在边缘节点执行
-- 关键索引
CREATE INDEX idx_posts_status_published ON posts(status, published_at DESC);
CREATE INDEX idx_posts_view_count ON posts(view_count DESC); -- 热门文章
CREATE INDEX idx_posts_is_pinned ON posts(is_pinned DESC, published_at DESC); -- 置顶文章
CREATE INDEX idx_reading_history_user ON reading_history(user_id, read_at DESC);
CREATE INDEX idx_notifications_user ON notifications(user_id, is_read, created_at DESC);系统采用模块化设计,便于扩展:
// 路由模块化
import authRoutes from './routes/auth';
import postRoutes from './routes/posts';
import commentRoutes from './routes/comments';
app.route('/api/auth', authRoutes);
app.route('/api/posts', postRoutes);
app.route('/api/comments', commentRoutes);系统配置存储在数据库中,支持动态修改:
interface SystemConfig {
siteName: string;
siteDescription: string;
postsPerPage: number;
enableComments: boolean;
enableRegistration: boolean;
// ...
}- 多语言支持: i18n 国际化
- 插件系统: 支持第三方插件
- API 开放: 开放 API 供第三方调用
- 数据分析: 更详细的数据分析功能
- AI 功能: AI 辅助写作、智能推荐
版本: v1.4.0 | 更新日期: 2026-02-17