Next.js 16 App Router documentation

Leon 技术博客技术栈教程

这份文档面向项目维护者,解释本仓库如何用 Next.js、Supabase、R2、AI provider 和 Vercel/Cloudflare 组成一个可发布、可管理、可扩展的技术博客系统。

Next.js

16.1

React

19.2

TypeScript

strict

Supabase

DB source

R2

assets

AI SDK

routing

Security

JWT + RLS

Performance

ISR + cache

配置总览

生产部署前请在 Vercel 和本地 .env.local 中补齐变量。下面只列变量名,不展示任何真实密钥。

站点与 Supabase

前台读取、后台管理和 SEO 都依赖这些基础变量。

NEXT_PUBLIC_SITE_URLSITE_URLNEXT_PUBLIC_SUPABASE_URLNEXT_PUBLIC_SUPABASE_ANON_KEYSUPABASE_SERVICE_ROLE_KEY

后台认证

管理员登录与 API 保护依赖 JWT cookie 和管理员密码。

JWT_SECRETADMIN_PASSWORDIKUN_PASSWORD

Cloudflare R2

图片上传、删除和后台素材管理使用 R2 S3 兼容接口。

R2_ENDPOINTR2_BUCKETR2_ACCESS_KEY_IDR2_SECRET_ACCESS_KEYR2_PUBLIC_URL

AI 与自动化

写作助手、翻译、Lab 自动识别、转载和定时同步按需启用。

OPENAI_CUSTOM_BASE_URLOPENAI_CUSTOM_API_KEYOPENAI_MODEL_PRIMARYOPENAI_MODEL_FALLBACKCHATANYWHERE_API_KEYTRANSLATION_MODEL_PRIMARYTRANSLATION_MODEL_FALLBACKSILICONFLOW_API_KEYNVIDIA_API_KEYCRON_SECRETTELEGRAM_SYNC_CRON_SECRETTELEGRAM_BOT_TOKENTELEGRAM_CHANNEL_IDTELEGRAPH_ACCESS_TOKEN

最快验证路径

如果你刚接手这个仓库,先跑完这一组命令,再进入下面的架构章节。

health checkbash
pnpm install
pnpm dev
pnpm type-check
pnpm lint
pnpm build

Getting Started

项目定位与快速启动

这个仓库是一个 Supabase-only 的 Next.js 技术博客系统,带双语前台、管理后台、R2 图床和 AI 辅助写作链路。

项目当前的真实运行时数据源是 Supabase。历史 Markdown 已经归档到 content/archive/posts,只用于人工核对和历史留存,不再作为前台渲染或后台编辑的回退来源。

  • Next.js 16 App Router 负责页面、API routes、metadata、ISR 和静态参数生成。
  • React 19 与 Server Components 是默认渲染模型,交互组件才显式使用 client boundary。
  • TypeScript 开启 strict、noImplicitAny、noUncheckedIndexedAccess 等较严格约束。
  • Supabase posts 表保存文章正文、元信息、发布状态、双语 shared_id 和阅读量。
  • Cloudflare R2 保存图片资产,后台上传接口返回公开 CDN URL。
本地开发最小命令bash
pnpm install
pnpm dev
pnpm type-check
pnpm lint
pnpm build

先处理密钥安全

仓库历史文档和脚本里出现过真实 R2 或默认管理员凭证的示例。上线前应轮换这些密钥,改用 .env.local 与 Vercel Environment Variables 注入,文档和代码示例只保留 your-... 占位符。

Architecture

Next.js App Router 架构

路由按前台多语言、后台管理、API 服务和共享领域库分层,适合把博客内容、管理操作和自动化任务放在同一个应用里。

根路径 app/page.tsx 会跳转到 /en;前台内容集中在 app/[locale],目前支持 en 与 zh。/docs 是独立公开文档入口,不参与 locale 前缀,便于形成 nodz.site/docs 这样的固定路径。

区域路径职责
前台页面app/[locale]面向读者的多语言页面,包含首页、文章、归档、分类、标签、AI、Lab 和关于页。
后台管理app/admin管理员入口,负责文章、图片、分类、标签、Lab 项目和设置管理。
API Routesapp/api提供文章 CRUD、认证、R2 上传、阅读量、AI 写作、翻译、转载和同步任务接口。
数据访问层lib/supabase集中封装 Supabase server/client、类型、文章和 Lab 项目的数据库操作。
内容读取层lib/posts.ts把 Supabase 行数据映射为前台 Post/PostMeta,并接入 Next.js Data Cache。
路由保护proxy.ts实现基础限流、可疑请求拦截、后台 JWT cookie 校验和 locale header 注入。
  • app/[locale]/layout.tsx 使用 next-intl 的 NextIntlClientProvider 提供多语言消息。
  • generateStaticParams 只生成配置中的 locale,dynamicParams=false 避免未知 locale 被当作合法页面。
  • 文章详情页使用 revalidate=3600,列表、标签、分类、Lab 等页面也通过 ISR 降低请求成本。
  • metadataBase、canonical、Open Graph、RSS、sitemap 和 robots 都依赖 NEXT_PUBLIC_SITE_URL 或 SITE_URL。
典型路由结构text
app/
  [locale]/          # 前台多语言页面
  admin/             # 管理后台
  api/               # Route Handlers
  docs/page.tsx      # 技术栈文档中心
lib/
  posts.ts           # 前台文章读取与缓存
  supabase/          # 数据库类型与读写
  r2/                # R2 素材管理
proxy.ts             # 限流、安全过滤、后台认证

Frontend

前端 UI 与交互栈

界面基于 Tailwind v4、shadcn new-york、Radix UI、lucide-react 和 next-themes,整体偏轻量、内容优先。

全局样式位于 app/globals.css,使用 Tailwind v4 的 @theme inline 把 CSS variables 映射为设计 token。主题色通过 data-color-theme 切换,暗色模式由 next-themes 管理。

能力主要依赖仓库位置
组件基础shadcn + Radix UIcomponents/ui
图标lucide-reactcomponents/layout、articles、admin
主题next-themes + Zustandcomponents/theme-provider.tsx、lib/store/theme-config.tsx
搜索动态加载对话框components/search
骨架屏Suspense fallbackcomponents/skeletons/site-skeletons.tsx
  • 默认优先使用 Server Components,搜索、主题切换、后台表单、编辑器等交互区域才使用 client components。
  • 重型视觉元素和搜索弹窗通过 dynamic import 延迟加载,减少首屏负担。
  • 前台 Header/Footer 依赖 next-intl,因此独立 /docs 页面使用自己的轻量文档导航。
  • 固定格式 UI 要有明确尺寸和响应式约束,代码块与表格在移动端横向滚动。

Content

Markdown、代码高亮与内容增强

文章正文从 Supabase 读取 Markdown 字符串,渲染时叠加代码高亮、视频嵌入、图片缩放、目录和评论。

Markdown 渲染相关组件集中在 components/articles。MarkdownContent 负责正文输出,LazyTableOfContents 和 LazyGiscusComments 负责按需加载目录与评论,ViewCounter 在客户端触发阅读量接口。

能力实现
Markdown 解析react-markdown、remark-gfm、rehype-raw
代码高亮rehype-pretty-code、shiki、app/code-block.css
视频嵌入lib/video-embed.ts、lib/markdown/remark-video-embed.ts
图片交互components/articles/image-zoom.tsx
评论Giscus 动态加载组件

内容源边界

content/archive/posts 是归档层,不参与线上读取。新增或修改文章应走后台和 Supabase,避免本地 Markdown 与数据库产生双源冲突。

Data

Supabase 数据模型与读取流

数据库层围绕 posts、lab_projects、telegram_sync_records 三类表展开,前台只读取已发布且双语配对完整的内容。

lib/supabase/server.ts 使用 service role key 创建服务端客户端,只应在 Server Components、API routes 和脚本中使用。lib/supabase/client.ts 使用 anon key,适合浏览器端只读或受 RLS 保护的场景。

  • posts 表通过 locale + slug 保证单语言唯一,通过 shared_id 关联中英文翻译。
  • 发布逻辑要求双语 pair 准备好后一起发布,避免前台语言切换指向缺失内容。
  • getAllPostsFromSupabase 读取 Supabase,lib/posts.ts 再用 unstable_cache 做 60 秒 Data Cache。
  • getPostBySlug 会把数据库字段映射为前台 Post,并补充阅读时长、摘要、封面、转载来源等展示字段。
  • 阅读量接口 app/api/posts/[slug]/view/route.ts 负责查询与自增 view_count。
文章数据流text
Supabase posts
  -> lib/supabase/posts.ts
  -> lib/posts.ts cached mapper
  -> app/[locale]/posts/page.tsx
  -> app/[locale]/posts/[slug]/page.tsx
  -> components/articles/*

性能策略

列表读取使用 Next.js Data Cache,页面使用 ISR;这让博客页面不必每次请求都打到 Supabase,同时仍能在短时间内看到后台更新。

Admin

后台、认证与 AI 工作流

后台负责内容生产全链路:登录、文章 CRUD、双语发布、图片上传、AI 写作翻译、转载草稿和 Lab 项目管理。

管理员登录使用 app/api/auth/login/route.ts 校验账号密码,成功后写入 HttpOnly admin_session cookie。proxy.ts 会拦截 /admin 路径并校验 JWT,有问题时重定向到 /admin/login。

功能主要入口说明
文章新建/编辑/删除app/admin/posts + app/api/admin/posts写入 Supabase posts 表,不同步本地 Markdown
双语发布app/api/admin/posts/publish-pair按 shared_id 检查中英文草稿并一起发布
AI 翻译草稿app/api/admin/posts/translate-draft调用 lib/llm/translation-simple.ts 的 fallback provider 链
AI 写作助手app/api/admin/ai-write流式生成,按可用 provider 选择模型
R2 图片上传app/api/admin/upload生成 R2 对象路径并返回公开 URL
Lab 管理app/admin/lab自动识别项目链接,必要时使用 AI 补齐简介

认证边界

后台页面靠 proxy.ts 保护,后台 API 也应继续在 route handler 内校验 admin_session,避免只依赖页面入口。

Deployment

部署、环境变量与外部服务

生产环境主要运行在 Vercel,图片和边缘防护依赖 Cloudflare,数据库使用 Supabase。

next.config.ts 开启 output=standalone、压缩、远程图片白名单、静态资源缓存和安全 headers。Vercel 负责构建部署,Cloudflare 可承担 DNS、R2 自定义域名、流量保护和 Worker 代理等能力。

.env.local 示例bash
NEXT_PUBLIC_SITE_URL=https://your-domain.com
SITE_URL=https://your-domain.com

NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-supabase-anon-key
SUPABASE_SERVICE_ROLE_KEY=your-supabase-service-role-key

JWT_SECRET=your-long-random-jwt-secret
ADMIN_PASSWORD=your-admin-password
IKUN_PASSWORD=your-second-admin-password

R2_ENDPOINT=https://your-account-id.r2.cloudflarestorage.com
R2_BUCKET=your-r2-bucket
R2_ACCESS_KEY_ID=your-r2-access-key-id
R2_SECRET_ACCESS_KEY=your-r2-secret-access-key
R2_PUBLIC_URL=https://assets.your-domain.com

OPENAI_CUSTOM_BASE_URL=https://your-openai-compatible-endpoint/v1
OPENAI_CUSTOM_API_KEY=your-openai-compatible-key
OPENAI_MODEL_PRIMARY=your-primary-model
OPENAI_MODEL_FALLBACK=your-fallback-model
服务用途必须程度
Supabase文章、Lab、同步记录数据库必需
VercelNext.js 构建与托管推荐
Cloudflare R2图片对象存储与公开访问后台图片能力必需
AI Provider写作、翻译、转载、Lab 自动补全可选增强
Telegram/Telegraph文章同步到频道可选自动化

Operations

日常开发与内容维护流程

维护这个项目时,建议把代码变更、数据库变更、内容发布和自动化脚本分开验证。

  1. 1本地拉起 pnpm dev,验证 /en、/zh、/docs 和 /admin/login。
  2. 2内容新增优先进入后台创建草稿,不再把 content/posts 当作生产入口。
  3. 3中英文草稿都准备好后,通过后台双语发布按钮一起发布。
  4. 4图片通过后台上传或 R2 脚本上传,正文中使用 R2_PUBLIC_URL 下的公开地址。
  5. 5上线前运行 pnpm type-check、pnpm lint、pnpm build。
常用脚本bash
pnpm dev                 # Turbopack 开发服务
pnpm dev:fast            # 增大 Node 内存的开发服务
pnpm type-check          # TypeScript 类型检查
pnpm lint                # ESLint
pnpm build               # 生产构建
pnpm analyze             # Bundle analyzer
pnpm translate:blog      # 翻译博客草稿
pnpm sync:supabase       # 同步历史内容到 Supabase
pnpm sync:telegram       # 同步文章到 Telegram/Telegraph

推荐发布心智模型

Supabase 是线上事实来源,后台是内容操作入口,archive 是历史证据库,脚本是批处理工具。只要这四个角色不混用,后续维护会轻很多。

Security

安全、缓存与流量保护

安全边界由环境变量、JWT cookie、RLS、proxy 限流、安全 headers 和 Cloudflare 共同承担。

  • SUPABASE_SERVICE_ROLE_KEY 只能在服务端使用,不能进入 client component 或 NEXT_PUBLIC 变量。
  • JWT_SECRET、ADMIN_PASSWORD、R2_SECRET_ACCESS_KEY、AI API key 必须只放在环境变量里。
  • proxy.ts 对 /api、/api/admin、/api/auth 和普通页面设置不同限流阈值。
  • next.config.ts 设置 CSP、HSTS、X-Frame-Options、Referrer-Policy、Permissions-Policy 等安全 headers。
  • R2 删除接口会扫描文章引用,删除素材前要确认没有被其他文章复用。

密钥轮换清单

如果任何真实密钥曾经提交到仓库或出现在文档中,应在 Cloudflare、Supabase、Vercel、AI provider 和后台账号处立即轮换,再清理示例文本。

Troubleshooting

排障路线图

遇到问题时先判断是构建、环境变量、数据库、缓存、认证还是外部服务,再定位到对应层。

现象优先检查相关位置
页面无文章Supabase 环境变量、posts.published、双语 pairlib/posts.ts、lib/supabase/posts.ts
后台无法登录ADMIN_PASSWORD、JWT_SECRET、cookie、proxy 重定向app/api/auth/login/route.ts、proxy.ts
图片上传失败R2 endpoint、bucket、access key、secret key、public URLapp/api/admin/upload/route.ts、lib/r2/client.ts
翻译污染或失败AI provider key、模型配置、fallback 日志、清洗规则lib/llm/translation-simple.ts、lib/utils.ts
构建失败TypeScript strict 错误、动态 API、Server/Client 边界pnpm type-check、pnpm build 输出
SEO 链接不对NEXT_PUBLIC_SITE_URL / SITE_URLapp/sitemap.ts、app/robots.ts、generateMetadata
排障命令bash
pnpm next info
pnpm type-check
pnpm lint
pnpm build
pnpm tsx scripts/verify-slugs.ts

缓存排查

文章列表经过 unstable_cache 和 ISR。后台刚更新后如果前台短时间未变化,先确认缓存 revalidate 窗口,再检查 API 写入是否成功。