| 类别 | 技术 |
|---|---|
| 框架 | Next.js 16 (App Router, Webpack) |
| 语言 | TypeScript 5 |
| 前端 | React 19 |
| 样式 | Tailwind CSS 4 |
| 动画 | Framer Motion 12 |
| 国际化 | next-intl 4 |
| 数据存储 | Upstash Redis |
| 文件存储 | Vercel Blob |
| AI 集成 | Vercel AI SDK 6 (智谱/OpenAI/Anthropic) |
| 认证 | JWT (jose) + bcryptjs |
| 邮件 | Nodemailer 8 |
| 表单 | React Hook Form 7 + Zod 4 |
| 富文本 | Tiptap 3(关于页) |
| Markdown | react-markdown + rehype-highlight + remark-gfm(博客/项目) |
| 图片处理 | Sharp |
| PDF 解析 | unpdf(简历文本提取) |
| PWA | Serwist 9(Service Worker) |
| 部署 | Vercel |
personal-portfolio/
├── app/ # Next.js App Router
│ ├── [locale]/ # 国际化路由
│ │ ├── page.tsx # 首页(各 Section 组合)
│ │ ├── blog/ # 博客列表 & 详情(含加载骨架屏、JetBrains Mono 字体按需加载)
│ │ ├── chat/ # 全屏 AI 聊天页
│ │ ├── projects/ # 项目列表 & 详情(含加载骨架屏)
│ │ ├── layout.tsx # 国际化布局
│ │ ├── error.tsx # 错误页面
│ │ ├── loading.tsx # 加载状态
│ │ └── not-found.tsx # 404 页面
│ ├── admin/ # 后台管理系统
│ │ ├── layout.tsx # 管理后台布局
│ │ ├── about/ # 关于管理
│ │ ├── blog/ # 博客管理
│ │ ├── contact/ # 联系管理
│ │ ├── experience/ # 经历管理
│ │ ├── hero/ # Hero 管理
│ │ ├── messages/ # 留言管理
│ │ ├── projects/ # 项目管理
│ │ ├── sensitive-words/ # 敏感词管理
│ │ ├── skills/ # 技能管理
│ │ ├── stats/ # 统计管理
│ │ └── change-password/ # 密码修改
│ ├── api/ # API 路由
│ │ ├── admin/ # 管理后台 API
│ │ ├── auth/ # 认证 API (登录/登出/验证/改密)
│ │ ├── chat/ # AI 聊天 API
│ │ ├── contact/ # 联系表单 API
│ │ └── warmup/ # 预热 API(Vercel Cron 定时调用)
│ ├── sw.ts # Serwist Service Worker 入口
│ ├── layout.tsx # 根布局
│ ├── page.tsx # 根页面(重定向到默认语言)
│ ├── globals.css # 全局样式入口
│ ├── manifest.ts # PWA Manifest(快捷方式、自适应图标)
│ ├── robots.ts # Robots.txt 生成
│ ├── sitemap.ts # Sitemap 生成
│ ├── error.tsx # 全局错误页面
│ └── global-error.tsx # 全局未捕获错误页面
├── components/
│ ├── admin/ # 管理后台组件
│ │ ├── AdminErrorBoundary.tsx # 管理后台错误边界
│ │ ├── AdminHeader.tsx # 管理后台头部
│ │ ├── AdminSidebar.tsx # 侧边栏导航
│ │ ├── ConfirmDialog.tsx # 确认弹窗
│ │ ├── DataTable.tsx # 数据表格
│ │ ├── ImageUpload.tsx # 文件上传(支持图片和PDF)
│ │ ├── MarkdownEditor.tsx # Markdown 编辑器
│ │ ├── MessageToast.tsx # 消息提示
│ │ ├── RichTextEditor.tsx # Tiptap 富文本编辑器(关于页)
│ │ ├── SensitiveWordsEditor.tsx # 敏感词编辑器
│ │ ├── SaveStatus.tsx # 保存状态指示
│ │ └── SortableList.tsx # 可排序列表
│ ├── ai/ # AI 聊天组件
│ │ ├── ChatInput.tsx # 聊天输入框(支持停止生成)
│ │ ├── ChatMessage.tsx # 消息气泡
│ │ ├── ChatWidget.tsx # 悬浮聊天窗口(移动端拖拽关闭)
│ │ ├── ClientChatWidget.tsx # 客户端聊天入口(空闲时延迟加载)
│ │ ├── LoginModal.tsx # 登录弹窗
│ │ ├── MarkdownRenderer.tsx # Markdown 渲染
│ │ ├── ModeToggle.tsx # 模式切换
│ │ ├── QuickPrompts.tsx # 快捷提示
│ │ ├── TypingIndicator.tsx # 输入中动画
│ │ └── VirtualMessageList.tsx # 虚拟滚动消息列表
│ ├── blog/ # 博客组件
│ │ ├── BlogDetailClient.tsx # 博客详情客户端
│ │ ├── BlogListClient.tsx # 博客列表客户端
│ │ ├── MDXComponents.tsx # Markdown 渲染 & 目录
│ │ └── PostCard.tsx # 文章卡片
│ ├── layout/ # 布局组件
│ │ ├── ClientPortfolioWrapper.tsx # 客户端页面包装器(含果冻滚动)
│ │ ├── ClientProviders.tsx # 客户端 Provider 组合
│ │ ├── DynamicFavicon.tsx # 动态 Favicon
│ │ ├── Footer.tsx # 页脚
│ │ ├── Header.tsx # 导航头部
│ │ ├── LanguageSwitcher.tsx # 语言切换
│ │ ├── Navigation.tsx # 导航菜单
│ │ ├── PWAInstallPrompt.tsx # PWA 安装提示组件
│ │ ├── PortfolioPageWrapper.tsx # 页面包装器
│ │ ├── ThemeProvider.tsx # 主题 Provider
│ │ └── ThemeToggle.tsx # 主题切换
│ ├── sections/ # 首页各区域组件
│ │ ├── HeroSection.tsx # Hero 区域
│ │ ├── HeroSectionWithData.tsx # Hero 区域(服务端组件,支持 Suspense)
│ │ ├── AboutSection.tsx # 关于区域
│ │ ├── AboutSectionWithData.tsx # 关于区域(服务端组件,支持 Suspense)
│ │ ├── SkillsSection.tsx # 技能区域
│ │ ├── SkillsSectionWithData.tsx # 技能区域(服务端组件,支持 Suspense)
│ │ ├── StatsSection.tsx # 统计区域
│ │ ├── StatsSectionWithData.tsx # 统计区域(服务端组件,支持 Suspense)
│ │ ├── ProjectsSection.tsx # 项目区域
│ │ ├── ProjectsSectionWithData.tsx # 项目区域(服务端组件,支持 Suspense)
│ │ ├── ExperienceSection.tsx # 经历区域
│ │ ├── ExperienceSectionWithData.tsx # 经历区域(服务端组件,支持 Suspense)
│ │ ├── BlogSection.tsx # 博客区域
│ │ ├── BlogSectionWithData.tsx # 博客区域(服务端组件,支持 Suspense)
│ │ ├── ContactSection.tsx # 联系区域
│ │ ├── ContactSectionWithData.tsx # 联系区域(服务端组件,支持 Suspense)
│ │ ├── ProjectCard.tsx # 项目卡片
│ │ ├── ProjectDetailContent.tsx # 项目详情内容
│ │ ├── SectionSkeleton.tsx # 骨架屏(Hero、About、Skills、Stats、Projects、Experience、Blog、Contact)
│ │ └── TimelineItem.tsx # 时间线项
│ ├── seo/ # SEO 组件
│ │ └── JsonLd.tsx # JSON-LD 结构化数据(Person、WebSite、Article、Breadcrumb)
│ └── ui/ # 通用 UI 组件
│ ├── AnimatedCounter.tsx # 动画计数器
│ ├── AnimatedLine.tsx # 动画线条
│ ├── Badge.tsx # 徽章
│ ├── Button.tsx # 按钮
│ ├── ContactForm.tsx # 联系表单
│ ├── DatePicker.tsx # 日期选择器
│ ├── Dialog.tsx # 对话框
│ ├── FloatingParticles.tsx # 浮动粒子
│ ├── Input.tsx # 输入框
│ ├── MagneticButton.tsx # 磁性按钮
│ ├── MarkdownRenderer.tsx # Markdown 渲染器
│ ├── MouseGlow.tsx # 鼠标光晕
│ ├── OptimizedJellyScroll.tsx # 果冻滚动组件
│ ├── ScrollToTopButton.tsx # 回到顶部
│ ├── Select.tsx # 选择器
│ ├── Skeleton.tsx # 骨架屏
│ ├── Textarea.tsx # 文本域
│ └── TiltCard.tsx # 3D 倾斜卡片
├── hooks/ # 自定义 Hooks
│ ├── useChat.ts # AI 聊天逻辑
│ └── usePWAInstall.ts # PWA 安装 Hook
├── i18n/ # 国际化
│ ├── zh-CN.json # 中文翻译
│ ├── en-US.json # 英文翻译
│ ├── routing.ts # 路由配置
│ ├── navigation.ts # 导航工具
│ ├── request.ts # 请求配置(含消息缓存)
│ └── server.ts # 服务端国际化工具
├── lib/ # 工具库
│ ├── ai/ # AI 相关
│ │ ├── providers/ # AI 提供商实现
│ │ │ ├── zhipu.ts # 智谱 AI(SSE 流式解析、超时保护、缓冲区溢出处理)
│ │ │ ├── openai.ts # OpenAI
│ │ │ ├── anthropic.ts # Anthropic
│ │ │ ├── types.ts # 类型定义
│ │ │ └── index.ts # 提供商注册
│ │ ├── context.ts # 上下文构建(含简历内容集成)
│ │ ├── context-compressor.ts # 上下文压缩(精简格式,约 60% token 削减)
│ │ ├── redact.ts # 简历内容脱敏(用户配置的敏感词映射替换)
│ │ ├── extractor.ts # 用户偏好/需求提取
│ │ ├── summarizer.ts # 对话摘要(长对话自动压缩)
│ │ ├── tokenizer.ts # Token 估算与上下文窗口管理
│ │ ├── utils.ts # AI 工具函数(流收集等)
│ │ ├── rate-limit.ts # 速率限制
│ │ ├── resume-parser.ts # 简历文本获取
│ │ └── system-prompts.ts # 系统提示词(含 30 秒缓存、管理后台安全警告)
│ ├── auth/ # 认证
│ │ ├── index.ts # 认证工具(含 withAuth 包装器)
│ │ ├── jwt.ts # JWT 工具(含 Token 缓存、LRU 淘汰策略)
│ │ └── middleware.ts # 认证中间件
│ ├── i18n/ # 国际化工具
│ │ └── server.ts # 服务端翻译与语言解析
│ ├── api.ts # 服务端数据获取层
│ ├── api-client.ts # 客户端 API 请求工具
│ ├── constants.ts # 共享常量(默认技能分类等)
│ ├── data.ts # Upstash Redis 数据读写(含 LRU 缓存、原子追加、开发模式性能日志)
│ ├── framer-optimizations.tsx # Framer Motion 性能优化(LazyMotion、OptimizedMotionDiv)
│ ├── notification.ts # 通知推送
│ ├── rate-limit.ts # 通用速率限制(Redis Lua 原子脚本)
│ ├── redis.ts # Redis 单例(统一连接管理)
│ ├── revalidate.ts # 缓存重新验证工具
│ ├── scroll-session.ts # 滚动会话恢复(跨页面锚点导航,TTL 过期)
│ └── utils.ts # 通用工具(getLocaleKey、getClientIp、errorResponse、formatDateTime)
├── types/ # TypeScript 类型
│ └── index.ts # 全局类型定义
├── styles/ # 样式
│ └── globals.css # 全局样式(CSS 变量、动画、滚动条等)
├── public/ # 静态资源
│ ├── icons/ # 图标目录(UniApp 等自定义图标)
│ └── images/ # 图片目录
├── scripts/ # 脚本
│ └── generate-password-hash.js # 密码哈希生成工具
├── next.config.ts # Next.js 配置
├── proxy.ts # Next.js 中间件(i18n 路由 + 管理后台认证保护)
├── vercel.json # Vercel 部署配置
├── .env.example # 环境变量模板
└── package.json # 项目依赖
# 克隆项目
git clone https://github.com/Adou377/personal-portfolio.git
cd personal-portfolio
# 安装依赖
pnpm install
# 复制环境变量
cp .env.example .env.local
# 编辑 .env.local 填入实际配置
复制 .env.example 到 .env.local,然后根据以下说明填写:
| 变量 | 说明 | 示例 |
|---|---|---|
NEXT_PUBLIC_SITE_URL | 网站域名 | https://trend-flow.top |
NEXT_PUBLIC_DEFAULT_LOCALE | 默认语言 | zh-CN |
| 变量 | 说明 | 获取方式 |
|---|---|---|
UPSTASH_REDIS_REST_URL | Upstash Redis REST URL | Upstash Console |
UPSTASH_REDIS_REST_TOKEN | Upstash Redis Token | Upstash Console |
BLOB_READ_WRITE_TOKEN | Vercel Blob 读写令牌 | Vercel Storage |
| 变量 | 说明 |
|---|---|
AI_PROVIDER | AI 提供商:zhipu / openai / anthropic |
ZHIPU_API_KEY | 智谱 AI API Key |
ZHIPU_MODEL | 智谱模型名(如 glm-4.7-flash) |
ZHIPU_BASE_URL | 智谱 API 地址 |
OPENAI_API_KEY | OpenAI API Key |
OPENAI_MODEL | OpenAI 模型名 |
ANTHROPIC_API_KEY | Anthropic API Key |
ANTHROPIC_MODEL | Anthropic 模型名 |
| 变量 | 说明 | 默认值 |
|---|---|---|
AI_RATE_LIMIT_HOURLY | 每小时请求限制 | 20 |
AI_RATE_LIMIT_DAILY | 每天请求限制 | 100 |
AI_DAILY_BUDGET_TOKENS | 每日 Token 预算 | 100000 |
| 变量 | 说明 |
|---|---|
SMTP_HOST | SMTP 服务器地址 |
SMTP_PORT | SMTP 端口 |
SMTP_USER | SMTP 用户名 |
SMTP_PASS | SMTP 授权码 |
CONTACT_EMAIL | 接收通知的邮箱 |
BARK_SERVER_URL | Bark 推送地址 |
BARK_ICON_URL | Bark 图标 URL |
BARK_SOUND | Bark 提示音 |
SERVERCHAN_SENDKEY | Server酱 SendKey |
SERVERCHAN_CHANNEL | Server酱推送渠道 |
| 变量 | 说明 |
|---|---|
ADMIN_PASSWORD_HASH | 管理员密码的 bcrypt 哈希 |
JWT_SECRET | JWT 签名密钥(至少 32 字符) |
JWT_EXPIRES_IN | Token 有效期(如 24h、7d) |
ADMIN_LOGIN_MAX_ATTEMPTS | 最大登录尝试次数 |
ADMIN_LOGIN_LOCKOUT_MINUTES | 登录锁定时长(分钟) |
| 变量 | 说明 | 默认值 |
|---|---|---|
UPLOAD_MAX_SIZE | 上传文件最大尺寸 (MB) | 5 |
node scripts/generate-password-hash.js
运行后会生成密码哈希和 JWT 密钥,将其填入 .env.local。
pnpm dev
访问 http://localhost:3000 查看网站。
pnpm build
pnpm lint
UPSTASH_REDIS_REST_URL 和 UPSTASH_REDIS_REST_TOKENBLOB_READ_WRITE_TOKEN项目已包含 vercel.json 配置,设置了安全响应头(含 CSP)、日本区域部署和 Cron 定时任务(每 5 分钟预热服务器)。
项目通过 vercel.json 配置了以下安全头:
Cache-Control: public, max-age=0, must-revalidate - HTML 页面禁止缓存X-Content-Type-Options: nosniffX-Frame-Options: DENYX-XSS-Protection: 1; mode=blockReferrer-Policy: strict-origin-when-cross-originContent-Security-Policy - 限制脚本、样式、图片、连接等来源(支持 Vercel Blob、SimpleIcons CDN)Cache-Control: no-storeCache-Control: public, max-age=31536000, immutable访问 /admin 进入管理后台,使用配置的密码登录。
项目使用 Upstash Redis 存储结构化数据(JSON),使用 Vercel Blob 存储上传的文件(图片、PDF 等)。这种架构适合 Serverless 部署环境,数据持久化且可扩展。
上传 PDF 简历时,系统会自动提取文本内容并存储到 contact.json,AI 聊天简历模式会优先使用提取的简历文本作为上下文。
编辑 styles/globals.css 中的 CSS 变量:
:root {
--primary: #8FB2C9;
--accent: #E94560;
--ai-glow: #7C3AED;
/* ... */
}
.dark {
--primary: #8FB2C9;
--accent: #E94560;
--ai-glow: #A78BFA;
/* ... */
}
通过管理后台修改所有个人数据。
i18n/routing.ts 中添加新语言到 localesi18n/ja-JP.json)types/index.ts 中的 Locale 类型