lijiayang050403 57f964744f feat: up
2025-10-24 04:55:08 +00:00

22 KiB
Raw Permalink Blame History

好的!我理解了,你需要:

  1. 前端现代化框架React/Vue
  2. 后端:独立的轻量后端(不用 Next.js API Routes
  3. 部署:都要简单方便

让我重新设计一个最佳方案!🎯 最终推荐架构


技术选型

前端 (SPA):
  - Framework: Vite + React 18 + TypeScript
  - UI: Tailwind CSS + shadcn/ui
  - State: Zustand + TanStack Query
  - Charts: Recharts + D3.js
  - 部署: Cloudflare Pages / Vercel (静态托管)

后端 (API):
  - Framework: Fastify + TypeScript (比Express快2倍)
  - GitHub SDK: Octokit
  - Database: PostgreSQL (Supabase免费托管)
  - Cache: Upstash Redis (Serverless)
  - 部署: Railway.app ⭐ (一键部署,免费额度够用)

认证:
  - GitHub OAuth 2.0
  - JWT Token

实时通信:
  - Server-Sent Events (SSE) - 比WebSocket简单
  - 或 Pusher (免费版)

🏗️ 系统架构图

┌─────────────────────────────────────────────────────────────┐
│                        用户浏览器                             │
│                                                               │
│  ┌──────────────────────────────────────────────────────┐  │
│  │              React SPA (Vite)                        │  │
│  │                                                       │  │
│  │  ┌──────────┐  ┌───────────┐  ┌──────────────┐     │  │
│  │  │Dashboard │  │Repository │  │Analytics     │     │  │
│  │  │   Page   │  │Detail Page│  │   Page       │     │  │
│  │  └────┬─────┘  └─────┬─────┘  └──────┬───────┘     │  │
│  │       │              │                │              │  │
│  │       └──────────────┴────────────────┘              │  │
│  │                      │                                │  │
│  │            ┌─────────▼──────────┐                    │  │
│  │            │  TanStack Query    │  (数据获取层)      │  │
│  │            │  + Axios/Fetch     │                    │  │
│  │            └─────────┬──────────┘                    │  │
│  └──────────────────────┼───────────────────────────────┘  │
└─────────────────────────┼──────────────────────────────────┘
                          │ 
                   HTTPS (REST API)
                          │
┌─────────────────────────▼──────────────────────────────────┐
│                  后端服务器 (Railway)                        │
│                                                              │
│  ┌────────────────────────────────────────────────────┐   │
│  │           Fastify API Server (Node.js)             │   │
│  │                                                     │   │
│  │  ┌──────────┐  ┌──────────┐  ┌──────────────┐    │   │
│  │  │  Auth    │  │  Repos   │  │  Webhooks    │    │   │
│  │  │ /login   │  │ /repos/* │  │ /webhooks/*  │    │   │
│  │  └────┬─────┘  └────┬─────┘  └──────┬───────┘    │   │
│  │       │             │                │             │   │
│  │       └─────────────┴────────────────┘             │   │
│  │                     │                               │   │
│  │          ┌──────────▼──────────┐                   │   │
│  │          │  Service Layer      │                   │   │
│  │          │  • GitHubService    │                   │   │
│  │          │  • CacheService     │                   │   │
│  │          │  • WebhookService   │                   │   │
│  │          └──────────┬──────────┘                   │   │
│  └─────────────────────┼────────────────────────────────┘   │
│                        │                                     │
│         ┌──────────────┼──────────────┐                     │
│         │              │               │                     │
│    ┌────▼─────┐  ┌────▼────┐  ┌──────▼──────┐             │
│    │ Octokit  │  │ Prisma  │  │ Redis Cache │             │
│    │ GitHub   │  │   ORM   │  │  (Upstash)  │             │
│    │   API    │  └────┬────┘  └─────────────┘             │
│    └──────────┘       │                                     │
└───────────────────────┼─────────────────────────────────────┘
                        │
                 ┌──────▼──────┐
                 │ PostgreSQL  │
                 │ (Supabase)  │
                 └─────────────┘

┌────────────────────────────────────────────┐
│          外部服务                           │
│                                            │
│  ┌──────────────┐  ┌──────────────────┐  │
│  │ GitHub API   │  │ GitHub Webhooks  │  │
│  │  (公开接口)  │  │  (实时事件推送)  │  │
│  └──────────────┘  └──────────────────┘  │
└────────────────────────────────────────────┘

📂 项目结构

github-dashboard/
├── frontend/                      # 前端项目 (Vite)
│   ├── src/
│   │   ├── components/
│   │   │   ├── dashboard/
│   │   │   │   ├── StatsCard.tsx
│   │   │   │   ├── ActivityChart.tsx
│   │   │   │   └── RepoList.tsx
│   │   │   ├── charts/
│   │   │   └── ui/              # shadcn/ui 组件
│   │   ├── pages/
│   │   │   ├── Dashboard.tsx
│   │   │   ├── RepoDetail.tsx
│   │   │   └── Analytics.tsx
│   │   ├── hooks/
│   │   │   ├── useRepos.ts
│   │   │   └── useRepoStats.ts
│   │   ├── lib/
│   │   │   ├── api.ts           # API 请求封装
│   │   │   └── auth.ts          # 认证逻辑
│   │   ├── stores/
│   │   │   └── authStore.ts     # Zustand store
│   │   ├── App.tsx
│   │   └── main.tsx
│   ├── package.json
│   ├── vite.config.ts
│   └── tailwind.config.js
│
├── backend/                       # 后端项目 (Fastify)
│   ├── src/
│   │   ├── routes/
│   │   │   ├── auth.ts          # POST /auth/login
│   │   │   ├── repos.ts         # GET /repos, /repos/:id
│   │   │   ├── webhooks.ts      # POST /webhooks/github
│   │   │   └── stats.ts         # GET /stats/*
│   │   ├── services/
│   │   │   ├── github.service.ts
│   │   │   ├── cache.service.ts
│   │   │   └── webhook.service.ts
│   │   ├── middlewares/
│   │   │   ├── auth.middleware.ts
│   │   │   └── error.middleware.ts
│   │   ├── prisma/
│   │   │   └── schema.prisma
│   │   ├── types/
│   │   │   └── index.ts
│   │   ├── utils/
│   │   │   └── logger.ts
│   │   ├── app.ts               # Fastify 实例
│   │   └── server.ts            # 启动文件
│   ├── package.json
│   ├── tsconfig.json
│   └── .env.example
│
└── README.md

🚀 部署方案

前端部署Cloudflare Pages

# 1. 构建
cd frontend
npm run build

# 2. Cloudflare Pages 自动部署
# - 连接 GitHub 仓库
# - 构建命令: npm run build
# - 输出目录: dist
# - 自动部署: 推送到 main 分支即触发

优势:

  • 完全免费
  • 全球 CDN超快
  • 自动 HTTPS
  • 无限带宽
  • 支持自定义域名

后端部署Railway.app

# 1. 安装 Railway CLI
npm install -g @railway/cli

# 2. 登录
railway login

# 3. 初始化项目
cd backend
railway init

# 4. 部署
railway up

# 5. 添加数据库
railway add postgresql

# 6. 设置环境变量
railway variables set GITHUB_CLIENT_ID=xxx
railway variables set GITHUB_CLIENT_SECRET=xxx

Railway 配置文件 (railway.json): { "$schema": "https://railway.app/railway.schema.json", "build": { "builder": "NIXPACKS" }, "deploy": { "startCommand": "npm run start", "restartPolicyType": "ON_FAILURE", "restartPolicyMaxRetries": 10 } }

优势:

  • 免费额度:$5/月(足够小项目)
  • 一键部署,零配置
  • 自动 HTTPS
  • 内置 PostgreSQL
  • 环境变量管理
  • 自动 Git 同步部署

替代方案对比

平台 免费额度 部署难度 启动速度 推荐度
Railway $5/月
Render 750h/月 慢(冷启动)
Fly.io 3个免费实例
Heroku 无免费版 中等

💻 核心代码示例

后端 - Fastify 入口 (backend/src/app.ts)

import Fastify from 'fastify';
import cors from '@fastify/cors';
import jwt from '@fastify/jwt';
import { authRoutes } from './routes/auth';
import { repoRoutes } from './routes/repos';
import { webhookRoutes } from './routes/webhooks';

export function createApp() {
  const app = Fastify({
    logger: true,
  });

  // 插件
  app.register(cors, {
    origin: process.env.FRONTEND_URL || 'http://localhost:5173',
    credentials: true,
  });

  app.register(jwt, {
    secret: process.env.JWT_SECRET!,
  });

  // 路由
  app.register(authRoutes, { prefix: '/api/auth' });
  app.register(repoRoutes, { prefix: '/api/repos' });
  app.register(webhookRoutes, { prefix: '/api/webhooks' });

  // 健康检查
  app.get('/health', async () => {
    return { status: 'ok', timestamp: new Date().toISOString() };
  });

  return app;
}

后端 - GitHub Service (backend/src/services/github.service.ts)

import { Octokit } from '@octokit/rest';
import { Redis } from '@upstash/redis';

const redis = new Redis({
  url: process.env.UPSTASH_REDIS_URL!,
  token: process.env.UPSTASH_REDIS_TOKEN!,
});

export class GitHubService {
  private octokit: Octokit;

  constructor(accessToken: string) {
    this.octokit = new Octokit({ auth: accessToken });
  }

  async getRepositories() {
    const cacheKey = 'repos:list';

    // 尝试从缓存读取
    const cached = await redis.get(cacheKey);
    if (cached) return cached;

    // 从 GitHub API 获取
    const { data } = await this.octokit.repos.listForAuthenticatedUser({
      sort: 'updated',
      per_page: 100,
    });

    // 缓存5分钟
    await redis.set(cacheKey, data, { ex: 300 });

    return data;
  }

  async getRepositoryStats(owner: string, repo: string) {
    const cacheKey = `repo:${owner}/${repo}:stats`;
    const cached = await redis.get(cacheKey);
    if (cached) return cached;

    const [repoData, issues, pulls, commits] = await Promise.all([
      this.octokit.repos.get({ owner, repo }),
      this.octokit.issues.listForRepo({ owner, repo, state: 'open' }),
      this.octokit.pulls.list({ owner, repo, state: 'open' }),
      this.octokit.repos.listCommits({ 
        owner, 
        repo, 
        per_page: 100,
        since: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString()
      }),
    ]);

    const stats = {
      name: repoData.data.name,
      stars: repoData.data.stargazers_count,
      forks: repoData.data.forks_count,
      openIssues: issues.data.length,
      openPRs: pulls.data.length,
      commitsLastMonth: commits.data.length,
      language: repoData.data.language,
      updatedAt: repoData.data.updated_at,
    };

    await redis.set(cacheKey, stats, { ex: 300 });
    return stats;
  }
}

前端 - API 客户端 (frontend/src/lib/api.ts)

import axios from 'axios';

const api = axios.create({
  baseURL: import.meta.env.VITE_API_URL || 'http://localhost:3000/api',
  timeout: 10000,
});

// 请求拦截器:添加 token
api.interceptors.request.use((config) => {
  const token = localStorage.getItem('token');
  if (token) {
    config.headers.Authorization = `Bearer ${token}`;
  }
  return config;
});

// 响应拦截器:处理错误
api.interceptors.response.use(
  (response) => response,
  (error) => {
    if (error.response?.status === 401) {
      localStorage.removeItem('token');
      window.location.href = '/login';
    }
    return Promise.reject(error);
  }
);

export default api;

前端 - 数据获取 Hook (frontend/src/hooks/useRepos.ts)

import { useQuery } from '@tanstack/react-query';
import api from '@/lib/api';

interface Repository {
  id: number;
  name: string;
  full_name: string;
  description: string;
  stargazers_count: number;
  open_issues_count: number;
  language: string;
}

export function useRepositories() {
  return useQuery({
    queryKey: ['repositories'],
    queryFn: async () => {
      const { data } = await api.get<Repository[]>('/repos');
      return data;
    },
    staleTime: 5 * 60 * 1000, // 5分钟
    refetchOnWindowFocus: false,
  });
}

export function useRepositoryStats(owner: string, repo: string) {
  return useQuery({
    queryKey: ['repo-stats', owner, repo],
    queryFn: async () => {
      const { data } = await api.get(`/repos/${owner}/${repo}/stats`);
      return data;
    },
    enabled: !!owner && !!repo,
    staleTime: 5 * 60 * 1000,
  });
}

🔐 认证流程

┌────────┐                  ┌──────────┐                ┌─────────┐
│ 前端   │                  │  后端    │                │ GitHub  │
└───┬────┘                  └────┬─────┘                └────┬────┘
    │                            │                           │
    │ 1. 点击 "Login with GitHub" │                           │
    ├──────────────────────────>│                           │
    │                            │                           │
    │ 2. 重定向到 GitHub OAuth   │                           │
    ├───────────────────────────┼─────────────────────────>│
    │                            │                           │
    │ 3. 用户授权                 │                           │
    │                            │<──────────────────────────┤
    │                            │                           │
    │ 4. GitHub 回调 + code      │                           │
    │<───────────────────────────┤                           │
    │                            │                           │
    │ 5. 发送 code 到后端         │                           │
    ├──────────────────────────>│                           │
    │                            │                           │
    │                            │ 6. 用 code 换 access_token│
    │                            ├─────────────────────────>│
    │                            │                           │
    │                            │<──────────────────────────┤
    │                            │                           │
    │ 7. 返回 JWT token          │                           │
    │<───────────────────────────┤                           │
    │                            │                           │
    │ 8. 保存 token 到 localStorage                         │
    │                            │                           │

📊 数据流

前端 Dashboard 页面加载:

1. 检查 localStorage 是否有 token
   ├─ 无 → 跳转登录页
   └─ 有 → 继续

2. 发起并行请求TanStack Query:
   ├─ GET /api/repos (获取仓库列表)
   ├─ GET /api/stats/overview (总体统计)
   └─ GET /api/stats/activity (活动数据)

3. 后端处理:
   ├─ 验证 JWT token
   ├─ 检查 Redis 缓存
   │  ├─ 命中 → 直接返回
   │  └─ 未命中 → 调用 GitHub API + 写入缓存
   └─ 返回数据

4. 前端渲染:
   ├─ TanStack Query 自动管理加载/错误状态
   ├─ 渲染图表Recharts
   └─ 显示数据卡片

🎯 开发步骤

1. 初始化项目

# 创建项目目录
mkdir github-dashboard
cd github-dashboard

# 初始化前端
npm create vite@latest frontend -- --template react-ts
cd frontend
npm install
npm install @tanstack/react-query axios zustand
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p

# 初始化后端
cd ..
mkdir backend && cd backend
npm init -y
npm install fastify @fastify/cors @fastify/jwt
npm install @octokit/rest @upstash/redis @prisma/client
npm install -D typescript @types/node tsx prisma
npx prisma init

2. 配置 Railway 部署

cd backend

# 创建 railway.json
cat > railway.json << EOF
{
  "build": {
    "builder": "NIXPACKS"
  },
  "deploy": {
    "startCommand": "npm run start"
  }
}
EOF

# 部署
railway login
railway init
railway up

3. 配置 Cloudflare Pages

cd frontend

# 构建测试
npm run build

# 在 Cloudflare Pages Dashboard:
# 1. 连接 GitHub 仓库
# 2. 设置构建命令: npm run build
# 3. 设置输出目录: dist
# 4. 添加环境变量: VITE_API_URL=你的Railway后端URL

🎉 总结

这个架构的优势:

前后端分离 - 各自独立部署和扩展 轻量高效 - Fastify 性能优秀Vite 开发体验好 部署简单 - Railway + Cloudflare Pages 都是一键部署 成本低廉 - 免费额度足够个人/小团队使用 可扩展性 - 需要时可轻松升级到付费版 现代化 - TypeScript 全栈,类型安全

需要我提供:

  1. 完整的项目初始化脚本?
  2. Prisma Schema 数据库设计?
  3. 前端 Dashboard 组件代码?
  4. 详细的部署文档?

选择任意方向深入!🚀