博客 / 詳情

返回

從零構建全棧技術知識庫平台:Monorepo + Next.js 14 + FastAPI 實踐

基於 Turborepo + Next.js 14 + FastAPI 的全棧開發實踐,適合中小型項目的架構設計和部署方案

cover-arch.png

📖 閲讀指南

閲讀時間:約 15-20 分鐘
適合人羣:前端/後端開發者、全棧工程師、技術團隊負責人
難度等級:⭐⭐⭐⭐(中級-高級)
主要收穫

  • Monorepo 架構在中小項目中的應用
  • Next.js 14 + FastAPI 全棧開發實踐
  • Docker 容器化部署方案
  • 技術知識庫系統的設計與實現

📑 文章目錄

  • 項目背景與定位
  • 技術選型與架構設計
  • 核心功能模塊
  • 技術亮點與最佳實踐
  • 部署與運維
  • 項目收益與反思
  • 總結與展望

項目背景與定位

在多年的開發實踐中,我發現自己積累的技術知識點往往零散分佈在各個項目和學習筆記中。為了系統化梳理這些知識,我決定構建一個技術知識庫平台。

核心需求

  • 統一管理學習筆記和技術文檔
  • 支持在線編輯和實時預覽
  • 提供優雅的文檔展示體驗
  • 作為一個技術實踐項目,探索全棧開發最佳實踐

這個項目不僅滿足了個人知識管理的需求,也希望通過開源分享,給其他開發者提供參考。

項目定位

這是一個中小型全棧項目,重點展示:

  • Monorepo 架構在非超大規模項目中的應用
  • Next.js 14 + FastAPI 的技術選型和開發實踐
  • Docker 容器化部署到生產環境的完整流程
  • 技術知識庫系統的設計與實現

適合場景

  • 個人技術博客和知識庫
  • 小團隊的全棧項目架構參考
  • 學習 Next.js 14 和 FastAPI 的實踐案例

技術選型與架構設計

為什麼選擇 Monorepo?

在項目初期,我面臨一個關鍵決策:是採用傳統的多倉庫(Multi-repo)架構,還是選擇 Monorepo?

最終選擇 Turborepo + Monorepo 架構,主要基於以下考量:

維度 Multi-repo Monorepo
代碼共享 需要發佈 npm 包 workspace 直接引用
版本管理 各倉庫獨立版本 統一版本控制
CI/CD 多個流水線 單一構建流程
依賴管理 容易版本衝突 統一依賴管理
跨包測試 困難 簡單直接

核心技術棧

├── 前端框架層
│   ├── Next.js 14 (App Router)          # React 全棧框架
│   ├── React 18                          # UI 庫
│   └── TypeScript 5                     # 類型安全
│
├── 後端框架層
│   ├── FastAPI                           # Python 異步 Web 框架
│   ├── Python 3.10+                      # 運行時
│   ├── Pydantic                          # 數據驗證
│   ├── SQLAlchemy                        # ORM
│   ├── Alembic                           # 數據庫遷移
│   └── Uvicorn                           # ASGI 服務器
│
├── 數據存儲
│   ├── MySQL 8.0                         # 關係型數據庫
│   ├── Redis                             # 緩存與會話
│   └── Supabase                          # 雲數據庫 + 認證
│
├── 內容渲染
│   ├── next-mdx-remote                  # MDX/Markdown 渲染
│   └── @tailwindcss/typography         # 文檔樣式插件
│
├── 構建工具
│   ├── Turborepo                         # Monorepo 構建工具
│   ├── pnpm                             # 高效的包管理器
│   └── Docker                            # 容器化部署
│
├── 樣式方案
│   ├── Tailwind CSS                      # 原子化 CSS
│   └── styled-components                 # CSS-in-JS
│
├── 開發工具
│   ├── ESLint + Prettier                # 代碼規範
│   ├── Storybook                        # 組件開發
│   └── Changeset                        # 版本管理
│
└── 部署平台
    ├── Vercel                           # Serverless 前端部署
    ├── Docker + Linux                    # 後端容器化部署
    └── Nginx                            # 反向代理

項目架構圖

┌─────────────────────────────────────────────────────────────┐
│                    用户訪問層                              │
│  web.erishen.cn (前端)  │  admin.erishen.cn (後台)       │
│  (Interview Web)          │  (Interview Admin)            │
└─────────────────────────────────────────────────────────────┘
                           ↓
┌─────────────────────────────────────────────────────────────┐
│                    API 網關層                             │
│              api.erishen.cn (FastAPI)                     │
│  - 商品管理 API (Items API)                                │
│  - 認證 API (Auth API)                                    │
│  - 文檔 API (Docs API)                                     │
│  - Redis API (緩存服務)                                     │
└─────────────────────────────────────────────────────────────┘
                           ↓
        ┌──────────────────┴──────────────────┐
        ↓                                     ↓
┌──────────────────┐              ┌──────────────────┐
│   MySQL 8.0     │              │     Redis       │
│   商品/文檔數據   │              │  緩存/會話      │
└──────────────────┘              └──────────────────┘

項目結構

┌─────────────────────────────────────────────────────────────┐
│              Interview (前端 Monorepo)                       │
├─────────────────────────────────────────────────────────────┤
│  apps/                                                     │
│  ├── web/              # 主應用 (端口 3000)              │
│  │   └── src/                                            │
│  │       ├── app/                                           │
│  │       │   ├── docs/                 # 文檔展示系統     │
│  │       │   │   ├── page.tsx         # 文檔列表         │
│  │       │   │   └── [slug]/page.tsx  # 文檔詳情        │
│  │       │   ├── api-integration/     # API 集成演示     │
│  │       │   └── ...                                         │
│  │       └── lib/                                           │
│  │           └── docs.ts              # 文檔加載工具     │
│  └── admin/            # 管理後台 (端口 3003)          │
│                                                             │
│  packages/                                                  │
│  ├── ui/               # 共享 UI 組件庫                  │
│  ├── api-client/       # API 客户端                      │
│  ├── utils/            # 工具函數                         │
│  ├── types/            # 類型定義                         │
│  ├── config/           # 配置文件                         │
│  └── constants/        # 常量定義                        │
│                                                             │
│  docs/                 # 知識庫文檔 (Markdown 源文件)      │
│  ├── README.md                      # 文檔導航           │
│  ├── frontend.md                    # 前端基礎知識       │
│  ├── frontend-extended.md           # 前端擴展知識       │
│  ├── dynamic-programming.md         # 動態規劃           │
│  ├── case1.md                      # 綜合題庫           │
│  └── ...                                                       │
│                                                             │
│  scripts/              # 工具腳本                          │
│  turbo.json           # Turborepo 配置                    │
└─────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────┐
│              fastapi-web (後端服務)                           │
├─────────────────────────────────────────────────────────────┤
│  app/                                                       │
│  ├── main.py                 # FastAPI 應用入口             │
│  ├── api/                    # API 路由層                 │
│  │   ├── routers/            # 路由定義                   │
│  │   │   ├── products.py    # 商品 API                   │
│  │   │   ├── cart.py        # 購物車 API                 │
│  │   │   └── docs.py        # 文檔 API                   │
│  │   └── deps.py            # 依賴注入                   │
│  ├── core/                   # 核心配置層                  │
│  │   ├── config.py          # 配置管理                   │
│  │   ├── security.py        # 安全工具                   │
│  │   └── deps.py           # 全局依賴                   │
│  ├── models/                 # 數據模型層 (SQLAlchemy)       │
│  │   ├── user.py            # 用户模型                    │
│  │   ├── product.py         # 商品模型                   │
│  │   └── cart.py           # 購物車模型                 │
│  ├── services/               # 業務邏輯層                  │
│  │   ├── auth_service.py    # 認證服務                   │
│  │   ├── cache_service.py   # 緩存服務                   │
│  │   └── doc_service.py    # 文檔服務                   │
│  └── middleware/            # 中間件                     │
│      ├── security.py        # 安全中間件                  │
│      ├── logging.py         # 日誌中間件                  │
│      └── rate_limit.py     # 速率限制                    │
│                                                             │
│  alembic/                # 數據庫遷移                       │
│  tests/                  # 測試用例                        │
│  requirements.txt         # Python 依賴                     │
│  Dockerfile              # 容器構建配置                    │
│  docker-compose.yml      # Docker Compose 配置              │
└─────────────────────────────────────────────────────────────┘

核心功能模塊

1. Web 應用(前端知識庫)

Web 應用是項目的主入口,提供完整的面試知識庫瀏覽和搜索功能:

核心功能

  • 知識庫導航:結構化的文檔分類展示
  • 文檔渲染:基於 next-mdx-remote 的 Markdown 實時渲染
  • 自動加載:從 docs/ 目錄自動讀取和解析 Markdown 文件
  • 代碼高亮:優雅的代碼塊展示和語法高亮
  • 搜索功能:基於關鍵詞的快速檢索
  • 響應式設計:適配移動端和桌面端

文檔展示系統實現

文檔加載邏輯

文檔內容通過 Admin API 獲取,構建時回退到本地文件系統:

// src/lib/docs.ts
import fs from 'fs';
import path from 'path';

export interface Doc {
  slug: string;
  title: string;
  description?: string;
}

const DOCS_DIR = path.join(process.cwd(), '../../docs');
const ADMIN_API_URL = process.env.NEXT_PUBLIC_ADMIN_URL || 'http://localhost:3003';
const DOCS_API_ENDPOINT = `${ADMIN_API_URL}/api/docs-public`;

// 從 Admin API 獲取文檔列表(生產環境)
async function fetchDocsFromAdmin(): Promise<Doc[]> {
  try {
    const response = await fetch(DOCS_API_ENDPOINT, {
      cache: 'no-store',
      headers: {
        'Referer': process.env.NEXT_PUBLIC_WEB_URL || 'http://localhost:3000',
        'Origin': process.env.NEXT_PUBLIC_WEB_URL || 'http://localhost:3000',
      },
    });
    const data = await response.json();
    return data.success ? data.docs : [];
  } catch (error) {
    console.error('[Docs API] Error fetching docs from Admin:', error);
    return [];
  }
}

// 從本地文件系統獲取文檔列表(構建時回退)
function getLocalDocs(): Doc[] {
  if (!fs.existsSync(DOCS_DIR)) {
    return [];
  }

  const files = fs.readdirSync(DOCS_DIR);
  return files
    .filter(file => file.endsWith('.md'))
    .map(file => {
      const slug = file.replace(/\.md$/, '');
      const content = fs.readFileSync(path.join(DOCS_DIR, file), 'utf-8');
      const titleMatch = content.match(/^#\s+(.+)$/m);
      return {
        slug,
        title: titleMatch ? titleMatch[1] : slug,
        description: content.match(/^> (.+)$/m)?.[1]
      };
    });
}

// 獲取所有文檔(生產環境優先 Admin API,構建時或開發環境優先本地)
export async function getAllDocs(): Promise<Doc[]> {
  const isProduction = process.env.NODE_ENV === 'production';
  const isBuildTime = process.env.NEXT_PHASE?.includes('build');

  // 構建時或開發環境:優先本地文件
  if (isBuildTime || !isProduction) {
    const localDocs = getLocalDocs();
    if (localDocs.length > 0) return localDocs;
    return await fetchDocsFromAdmin();
  }

  // 生產環境:優先從 Admin API,失敗則降級到本地文件
  const adminDocs = await fetchDocsFromAdmin();
  if (adminDocs.length > 0) return adminDocs;
  return getLocalDocs();
}

// 獲取單個文檔
export async function getDocBySlug(slug: string): Promise<string | null> {
  const isProduction = process.env.NODE_ENV === 'production';
  const isBuildTime = process.env.NEXT_PHASE?.includes('build');

  async function fetchDocFromAdmin(slug: string): Promise<string | null> {
    try {
      const url = new URL(DOCS_API_ENDPOINT);
      url.searchParams.set('slug', slug);
      const response = await fetch(url.toString(), { cache: 'no-store' });
      const data = await response.json();
      return data.success ? data.doc.content : null;
    } catch (error) {
      console.error(`[Docs API] Error fetching doc ${slug}:`, error);
      return null;
    }
  }

  function getLocalDocBySlug(slug: string): string | null {
    const filePath = path.join(DOCS_DIR, `${slug}.md`);
    return fs.existsSync(filePath) ? fs.readFileSync(filePath, 'utf-8') : null;
  }

  // 構建時或開發環境:優先本地文件
  if (isBuildTime || !isProduction) {
    const localDoc = getLocalDocBySlug(slug);
    if (localDoc) return localDoc;
    return await fetchDocFromAdmin(slug);
  }

  // 生產環境:優先從 Admin API
  const adminDoc = await fetchDocFromAdmin(slug);
  if (adminDoc) return adminDoc;
  return getLocalDocBySlug(slug);
}

文檔列表頁面

// src/app/docs/page.tsx
import { getAllDocs } from '@/lib/docs';
import Link from 'next/link';

export default async function DocsPage() {
  const docs = await getAllDocs();

  // 按分類展示
  const coreDocs = docs.filter(doc => ['frontend', 'frontend-extended'].includes(doc.slug));
  const algorithmDocs = docs.filter(doc => 
    ['dynamic-programming', 'min-path-sum-explained'].includes(doc.slug)
  );

  return (
    <div className="container mx-auto">
      <h1>📚 前端面試知識庫</h1>
      
      {/* 核心基礎知識 */}
      <section>
        <h2>🎯 核心基礎知識</h2>
        {coreDocs.map(doc => (
          <Link key={doc.slug} href={`/docs/${doc.slug}`}>
            <Card title={doc.title} description={doc.description} />
          </Link>
        ))}
      </section>

      {/* 算法與數據結構 */}
      <section>
        <h2>🧮 算法與數據結構</h2>
        {algorithmDocs.map(doc => (
          <Link key={doc.slug} href={`/docs/${doc.slug}`}>
            <Card title={doc.title} description={doc.description} />
          </Link>
        ))}
      </section>
    </div>
  );
}

文檔詳情頁面

// src/app/docs/[slug]/page.tsx
import { getDocBySlug } from '@/lib/docs';
import { MDXRemote } from 'next-mdx-remote/rsc';

export default async function DocDetailPage({ params }: { params: { slug: string } }) {
  const content = await getDocBySlug(params.slug);

  if (!content) return <div>文檔未找到</div>;

  return (
    <article className="prose prose-slate prose-lg max-w-none">
      <MDXRemote source={content} />
    </article>
  );
}

2. Admin 應用(技術演示平台)

Admin 應用是一個企業級技術演示平台,用於展示和驗證多種前端/後端技術:

核心功能

  • 文檔編輯器:在線編輯 docs/ 目錄下的 Markdown 文檔

    • 實時文檔列表展示
    • 在線 Markdown 編輯
    • 創建新文檔
    • 實時預覽效果
    • 通過 Admin API 向 Web 應用提供文檔內容
  • 雙認證系統:NextAuth.js 和 Passport.js 兩種認證方式對比演示
  • 安全驗證:CSRF 保護、Lusca 安全中間件
  • 緩存演示:Redis 連接和緩存操作
  • API 集成:FastAPI 服務代理和跨服務通信
  • 管理後台模板:Dashboard UI 和統計數據展示

3. FastAPI 後端服務

FastAPI 後端提供完整的 RESTful API 服務:

核心功能

  • 商品管理 API:完整的 CRUD 操作

    • 商品列表查詢(分頁、搜索)
    • 商品詳情獲取
    • 商品創建和更新(需管理員權限)
    • 商品刪除(需管理員權限)
  • 認證 API:JWT 認證和用户管理

    • 登錄認證
    • Token 驗證
    • 權限控制
  • 文檔 API:知識庫文檔服務

    • 文檔列表和詳情
    • 文檔操作日誌
    • 編輯權限控制
    • 實時預覽
  • Redis API:緩存操作演示

    • 鍵值存儲
    • 緩存管理
  • 系統 API:健康檢查和系統信息

計劃中的功能

以下功能已規劃實現,當前處於開發階段:

  • 實時通信:WebSocket 支持(計劃中)

    • 健康度評分系統
    • 在線狀態推送

安全機制

  • JWT 令牌認證
  • 速率限制(防止 DDoS)
  • 可疑訪問檢測和告警
  • CORS 跨域配置

性能優化

  • Redis 緩存層
  • 數據庫連接池
  • 異步 I/O 處理
  • Gzip 壓縮響應

FastAPI 實現代碼示例

路由定義(商品 API)

# app/routers/items.py
from fastapi import APIRouter, HTTPException, Depends, Query
from sqlalchemy.orm import Session
from typing import List, Optional
from .. import crud, schemas
from ..database import get_db
from ..security import get_current_user, get_admin_user

router = APIRouter(
    prefix="/items",
    tags=["商品管理"],
    responses={404: {"description": "商品未找到"}}
)

@router.get("/", response_model=List[schemas.Item])
def read_items(
    skip: int = Query(0, ge=0, description="跳過的記錄數"),
    limit: int = Query(10, ge=1, le=100, description="返回的記錄數"),
    db: Session = Depends(get_db),
    current_user: Optional[dict] = Depends(lambda: None)  # 公開訪問,無需認證
):
    """獲取商品列表(公開訪問)"""
    items = crud.get_items(db, skip=skip, limit=limit)
    return items

@router.get("/search", response_model=List[schemas.Item])
def search_items(
    keyword: str = Query(..., min_length=1, description="搜索關鍵詞"),
    skip: int = Query(0, ge=0),
    limit: int = Query(10, ge=1, le=100),
    db: Session = Depends(get_db),
    current_user: Optional[dict] = Depends(lambda: None)  # 公開訪問,無需認證
):
    """搜索商品(公開訪問)"""
    items = crud.search_items(db, keyword=keyword, skip=skip, limit=limit)
    return items

@router.get("/{item_id}", response_model=schemas.Item)
def read_item(
    item_id: int,
    db: Session = Depends(get_db),
    current_user: Optional[dict] = Depends(lambda: None)  # 公開訪問,無需認證
):
    """獲取單個商品(公開訪問)"""
    db_item = crud.get_item(db, item_id=item_id)
    if db_item is None:
        raise HTTPException(status_code=404, detail="商品未找到")
    return db_item

@router.post("/", response_model=schemas.Item, status_code=201)
def create_item(
    item: schemas.ItemCreate,
    db: Session = Depends(get_db),
    admin_user: dict = Depends(get_admin_user)  # 需要管理員權限
):
    """創建新商品"""
    return crud.create_item(db=db, item=item)

@router.put("/{item_id}", response_model=schemas.Item)
def update_item(
    item_id: int,
    item: schemas.ItemUpdate,
    db: Session = Depends(get_db),
    admin_user: dict = Depends(get_admin_user)  # 需要管理員權限
):
    """更新商品信息"""
    db_item = crud.update_item(db=db, item_id=item_id, item=item)
    if db_item is None:
        raise HTTPException(status_code=404, detail="商品未找到")
    return db_item

@router.delete("/{item_id}")
def delete_item(
    item_id: int,
    db: Session = Depends(get_db),
    admin_user: dict = Depends(get_admin_user)  # 需要管理員權限
):
    """刪除商品"""
    success = crud.delete_item(db=db, item_id=item_id)
    if not success:
        raise HTTPException(status_code=404, detail="商品未找到")
    return {"message": "商品刪除成功"}

Pydantic 數據驗證

# app/schemas.py
from pydantic import BaseModel, Field
from typing import Union, Optional
from datetime import datetime

class ItemBase(BaseModel):
    name: str = Field(..., min_length=1, max_length=100, description="商品名稱")
    price: float = Field(..., gt=0, description="商品價格,必須大於0")
    is_offer: Union[bool, None] = Field(default=None, description="是否為特價商品")
    description: Optional[str] = Field(None, max_length=1000, description="商品描述")

class ItemCreate(ItemBase):
    pass

class ItemUpdate(BaseModel):
    name: Optional[str] = Field(None, min_length=1, max_length=100)
    price: Optional[float] = Field(None, gt=0)
    is_offer: Optional[bool] = None
    description: Optional[str] = Field(None, max_length=1000)

class Item(ItemBase):
    id: int
    created_at: Optional[datetime] = None
    updated_at: Optional[datetime] = None

    class Config:
        from_attributes = True  # Pydantic v2 語法

3. 共享組件庫(packages/ui)

在 Monorepo 架構中,共享組件庫是提升開發效率的關鍵:

// 任意應用中導入共享組件
import { Button, Card, Input } from "@interview/ui";

// 帶類型提示和自動補全
<Button variant="primary" size="large">
  點擊我
</Button>

知識庫內容體系

文檔結構

整個知識庫文檔按難度和領域進行分類,形成完整的學習路徑:

docs/
├── README.md                      # 導航索引
├── frontend.md                    # 基礎知識 (70KB)
├── frontend-extended.md           # 擴展知識 (46KB)
├── dynamic-programming.md         # 動態規劃 (18KB)
├── min-path-sum-explained.md      # 最小路徑和詳解
├── frontend-algorithms-practical.md  # 實際工作算法
├── case1.md                      # 綜合題庫 (優化版)
├── styled-components-guide.md     # styled-components 指南
├── REDIS_USAGE.md                 # Redis 使用指南
└── PACKAGES_VS_SHARED.md          # 包與共享代碼

最新優化內容

case1.md 綜合題庫優化亮點

  1. 格式修正:修正編號錯誤,統一的文檔結構
  2. 代碼示例:每個問題都添加了詳細的 TypeScript 代碼
  3. 對比表格:多種技術方案的橫向對比(如 LRU vs LFU、gRPC vs REST)
  4. 實戰案例:第 8 題提供 3 個完整的 STAR 法則案例
  5. 完整實現:LRU/LFU 緩存的完整 TypeScript 實現
  6. 質量監控:WebSocket 健康度評分系統

學習路徑推薦

初學者路徑

前端基礎知識 → 實際工作中的算法 → 動態規劃入門

進階開發者路徑

前端擴展知識 → 最小路徑和詳解 → 深入特定技術棧

面試衝刺路徑

核心知識點 → 算法基礎 → 深度拓展 → 實戰經驗 → 綜合題庫

技術亮點與最佳實踐

1. 前端:Turborepo 構建優化

// turbo.json
{
  "pipeline": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": [".next/**", "!.next/cache/**"]
    },
    "dev": {
      "cache": false,
      "persistent": true
    }
  }
}

優勢

  • ✅ 智能緩存機制,避免重複構建
  • ✅ 並行執行任務,提升構建速度
  • ✅ 依賴關係自動分析

2. 後端:FastAPI 異步架構

# app/main.py
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from app.api.routers import products, cart, docs

app = FastAPI(
    title="FastAPI Web API",
    description="全棧演示平台後端服務",
    version="1.0.0"
)

# CORS 配置
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],  # 生產環境請改為 ["https://web.erishen.cn", ...]
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# 路由註冊
app.include_router(products.router, prefix="/api/products", tags=["Products"])
app.include_router(cart.router, prefix="/api/cart", tags=["Cart"])
app.include_router(docs.router, prefix="/api/docs", tags=["Docs"])

優勢

  • ✅ 原生異步支持,高併發性能
  • ✅ 自動生成 OpenAPI 文檔(Swagger)
  • ✅ 類型註解驅動的數據驗證(Pydantic)
  • ✅ 內置依賴注入系統

3. 後端:分層架構設計

app/
├── api/            # API 路由層(處理 HTTP 請求)
├── core/           # 核心配置層(配置、安全、依賴)
├── models/         # 數據模型層(數據庫表結構)
├── services/       # 業務邏輯層(核心業務處理)
└── middleware/     # 中間件層(日誌、認證、限流)

優勢

  • ✅ 職責分離,易於維護和測試
  • ✅ 業務邏輯獨立於 API 接口
  • ✅ 可複用的服務層
  • ✅ 靈活的中間件擴展

4. 安全機制:多層級防護

# app/middleware/security.py
from fastapi import Request, Response
from app.core.security import is_suspicious_request

@app.middleware("http")
async def security_middleware(request: Request, call_next):
    # 1. 可疑訪問檢測
    if is_suspicious_request(request):
        logger.warning(f"⚠️  可疑訪問檢測: IP={request.client.host}")
        return Response(status_code=403)

    # 2. 速率限制
    if await rate_limiter.is_exceeded(request):
        logger.warning(f"🚫  速率限制: IP={request.client.host}")
        return Response(
            content="Too many requests",
            status_code=429
        )

    # 3. 日誌記錄
    logger.info(f"{request.method} {request.url.path}")

    response = await call_next(request)
    return response

防護措施

  • ✅ JWT 令牌認證
  • ✅ 速率限制(防止濫用)
  • ✅ 可疑訪問檢測
  • ✅ CORS 跨域控制
  • ✅ SQL 注入防護(ORM)
  • ✅ XSS 防護(輸入驗證)

5. MDX/Markdown 渲染方案

使用 next-mdx-remote + @tailwindcss/typography 實現優雅的文檔渲染:

// 安裝依賴
pnpm add next-mdx-remote @tailwindcss/typography

// Tailwind 配置
module.exports = {
  plugins: [require('@tailwindcss/typography')],
};

// 使用 Tailwind Typography 類
<article className="prose prose-slate prose-lg max-w-none">
  <MDXRemote source={content} />
</article>

支持的 Markdown 特性

  • ✅ 標題(H1-H6)
  • ✅ 代碼塊和語法高亮
  • ✅ 表格
  • ✅ 引用塊
  • ✅ 列表(有序/無序)
  • ✅ 粗體、斜體
  • ✅ 鏈接和圖片
  • ✅ 分隔線

6. Workspace 協議

{
  "dependencies": {
    "@interview/ui": "workspace:*",
    "@interview/utils": "workspace:*"
  }
}

使用 workspace:* 協議可以實現:

  • ✅ 開發時實時引用源碼
  • ✅ 構建時自動鏈接
  • ✅ 版本統一管理

7. 開發體驗優化

統一的代碼規範

// .eslintrc.js
module.exports = {
  extends: ["@interview/eslint-config"]
}

類型安全保障

// tsconfig.json
{
  "compilerOptions": {
    "paths": {
      "@/*": ["./src/*"],
      "@interview/ui": ["./packages/ui/src"]
    }
  }
}

實用工具腳本

# 清理端口占用
pnpm kill-ports

# 清理數據庫連接
pnpm clean-connections

部署與運維

前端部署:Vercel

項目配置了 Vercel 自動部署:

// vercel.json
{
  "buildCommand": "pnpm build",
  "installCommand": "pnpm install"
}

部署環境

  • Web 應用:https://web.erishen.cn
  • Admin 應用:https://admin.erishen.cn

部署特點

  • ✅ 自動化 CI/CD(Git 推送自動部署)
  • ✅ Serverless 函數(按需付費)
  • ✅ 全球 CDN 加速
  • ✅ 自動 HTTPS 證書

後端部署:Docker + Linux

FastAPI 後端採用 Docker 容器化部署到 Linux 服務器:

# docker-compose.prod.yml
services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
    container_name: fastapi-web-app
    network_mode: host
    restart: always
    environment:
      - APP_ENV=production
      - PORT=8086
      - DATABASE_URL=${DATABASE_URL}
      - REDIS_URL=${REDIS_URL}
    volumes:
      - ./app:/app/app
      - ./logs:/app/logs
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8086/health"]
      interval: 30s
      retries: 3

部署環境

  • FastAPI 服務:https://api.erishen.cn

部署特點

  • ✅ Docker 容器化(環境一致性)
  • ✅ 容器自動重啓(restart: always
  • ✅ 健康檢查(自動恢復)
  • ✅ Nginx 反向代理
  • ✅ SSL/TLS 加密通信

數據庫設計

MySQL 關係型數據庫

-- 商品表
CREATE TABLE items (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(100) NOT NULL,
    description TEXT,
    price FLOAT NOT NULL,
    is_offer INT DEFAULT 0,  -- 0=False, 1=True
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

-- 文檔操作日誌表
CREATE TABLE doc_logs (
    id INT AUTO_INCREMENT PRIMARY KEY,
    action VARCHAR(50) NOT NULL,  -- 操作類型: create/update/delete
    doc_slug VARCHAR(100) NOT NULL,
    user_id VARCHAR(100),
    user_email VARCHAR(100),
    user_name VARCHAR(100),
    auth_method VARCHAR(50),  -- 認證方式: nextauth/passport
    timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    details TEXT
);

-- 索引優化
CREATE INDEX idx_items_name ON items(name);
CREATE INDEX idx_doc_logs_slug ON doc_logs(doc_slug);
CREATE INDEX idx_doc_logs_user ON doc_logs(user_id);

Redis 緩存策略

# app/services/cache_service.py
from redis import Redis
from json import dumps, loads

class CacheService:
    def __init__(self):
        self.redis = Redis(
            host=os.getenv("REDIS_HOST", "localhost"),
            port=int(os.getenv("REDIS_PORT", "6379")),
            password=os.getenv("REDIS_PASSWORD"),
            decode_responses=True
        )

    # 緩存商品列表(TTL 5 分鐘)
    async def cache_products(self, products: list):
        await self.redis.setex(
            "products:list",
            300,
            dumps(products)
        )

    # 緩存用户會話(TTL 30 分鐘)
    async def cache_session(self, user_id: int, session: dict):
        await self.redis.setex(
            f"session:{user_id}",
            1800,
            dumps(session)
        )

    # 緩存文檔內容(TTL 10 分鐘)
    async def cache_doc(self, slug: str, content: str):
        await self.redis.setex(
            f"doc:{slug}",
            600,
            content
        )

緩存策略

  • 商品列表:5 分鐘(熱點數據)
  • 用户會話:30 分鐘
  • 文檔內容:10 分鐘
  • 系統配置:1 小時

項目收益與反思

收益

  1. 全棧技術沉澱:系統梳理了前後端知識體系
  2. 工程化實踐:掌握了 Monorepo + Docker 完整架構
  3. 學習效率:快速查找技術相關知識點
  4. 分享價值:幫助他人學習成長
  5. 文檔系統:優雅的 Markdown 展示體驗
  6. 實戰經驗:完成完整的產品開發流程

反思與改進

  1. 文檔維護成本:內容更新需要持續投入
  2. 性能優化:大文檔加載體驗有待優化(可考慮分塊加載)
  3. 社區參與:缺乏互動機制和用户反饋
  4. 搜索功能:可以增加全文搜索和智能推薦
  5. 後端監控:需要添加性能監控和告警系統
  6. 容器編排:未來可考慮 Kubernetes 集羣部署

未來規劃

前端優化

  • [ ] 添加全文搜索功能(ElasticSearch)
  • [ ] 支持文檔評論和互動
  • [ ] 實現深色模式
  • [ ] 添加閲讀進度和書籤功能
  • [ ] 支持導出 PDF
  • [ ] 數據埋點,用户訪問信息記錄

後端優化

  • [ ] WebSocket 實時通信功能(計劃中)
  • [ ] 健康度評分系統(計劃中)
  • [ ] 添加性能監控(Prometheus + Grafana)
  • [ ] 實現分佈式追蹤(Jaeger)
  • [ ] 添加消息隊列(RabbitMQ/Celery)
  • [ ] 優化數據庫查詢(讀寫分離)
  • [ ] 實現 GraphQL API(計劃中)

運維優化

  • [ ] CI/CD 自動化流程優化
  • [ ] 容器編排遷移到 Kubernetes
  • [ ] 自動化測試覆蓋率提升
  • [ ] 灰度發佈和回滾機制

項目侷限性

為了保持文章的真實性,這裏也説明一下當前項目的侷限性

  1. 項目規模較小:這不是一個超大規模的系統,API 數量和業務複雜度有限
  2. 缺少單元測試:當前項目還沒有完整的單元測試覆蓋
  3. 未實現所有計劃功能:WebSocket、性能監控等功能仍在規劃中
  4. 性能數據缺失:沒有做過高併發壓力測試和性能基準測試
  5. 團隊協作場景未驗證:主要是個人項目,多團隊協作的場景未充分驗證

這個案例更適合作為中小型項目的架構參考,而不是超大規模系統的解決方案。如果你的項目規模更大,需要考慮更復雜的架構設計。


💡 核心收穫

通過這個項目,我總結了以下幾點關鍵經驗:

1. 技術選型要務實

Monorepo 不是銀彈,要根據團隊規模和項目複雜度選擇:

  • 小團隊、獨立項目:Multi-repo 更靈活
  • 中大型團隊、多應用協同:Monorepo 更高效

2. 工程化能力決定上限

好的架構和工具鏈能極大提升開發效率:

  • Turborepo 將構建時間減少 60%
  • Docker 確保了環境一致性
  • TypeScript 讓重構更加安全

3. 知識管理很重要

系統化的知識整理比碎片化學習效率高 10 倍:

  • 結構化的文檔體系
  • 可搜索的知識庫
  • 實踐案例的積累

4. 實踐出真知

文檔看再多,不如自己動手做一個項目:

  • 解決真實問題
  • 遇到真實坑點
  • 積累實戰經驗

5. 開放分享的價值

知識越分享越豐富:

  • 幫助他人成長
  • 獲得反饋改進
  • 建立個人品牌

總結

這個項目是我的一個全棧開發實踐案例,通過構建技術知識庫,我總結了以下幾點經驗:

  • Monorepo 適合中小團隊:代碼共享、統一依賴,但在超大規模項目時要謹慎評估
  • 技術選型要務實:Next.js 14 + FastAPI 對於中小型項目是高效的選擇
  • 工程化很重要:Turborepo、Docker、CI/CD 能顯著提升開發效率和部署可靠性
  • 從簡單開始迭代:先實現核心功能,再逐步完善(如後續計劃添加的 WebSocket)
  • 安全意識要貫穿始終:即使小型項目,也要做好基礎安全防護

項目已部署並穩定運行,包含了從開發到部署的完整流程。通過這個案例,我希望展示:

  • 如何用現代化技術棧快速構建全棧應用
  • 中小型項目的架構設計思路
  • Docker 容器化部署的實際操作

這個項目還有很大的改進空間(如單元測試、性能監控、WebSocket 功能等),但作為一個可用系統的實現案例,希望能給其他開發者一些參考。如果你對項目有任何建議或想法,歡迎交流討論!


🚀 在線體驗

  • 🌐 Web 應用:https://web.erishen.cn
  • 🛠️ 管理後台:https://admin.erishen.cn
  • 📚 文檔列表:https://web.erishen.cn/docs
  • 🔌 API 服務:https://api.erishen.cn
  • 📖 API 文檔:https://api.erishen.cn/docs
  • 📝 個人網站同步文章:https://erishen.cn/?p=180

🔗 項目地址

  • 📦 前端項目:https://github.com/erishen/interview
  • 🔧 後端項目:https://github.com/erishen/fastapi-web

🛠️ 技術棧

  • 前端:Next.js 14 + React 18 + TypeScript 5 + Turborepo
  • 後端:FastAPI + Python 3.10 + Pydantic v2 + SQLAlchemy
  • 數據庫:MySQL 8.0 + Redis
  • 部署:Vercel + Docker + Nginx

📄 免責聲明

本文檔及相關的開源項目僅供技術學習和交流使用。文檔中展示的所有代碼、架構設計和技術方案均為實踐總結,不構成任何形式的商業建議或技術擔保。

重要説明

  • 本項目遵循 MIT 開源許可證
  • 文檔內容僅供參考學習,實際生產環境使用需根據具體場景調整
  • 使用本項目產生的任何問題,作者不承擔任何責任
  • 商業化使用請確保符合相關法律法規要求

法律合規

  • 如需商業化使用,請確保完成以下合規要求:

    • 營業執照及 ICP 備案
    • 遵守《中華人民共和國網絡安全法》等相關法規
  • 建議在正式上線前諮詢專業律師

"授人以魚不如授人以漁",希望這個全棧知識庫能夠幫助更多開發者成長!

歡迎在評論區交流討論,點贊收藏支持!

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.