ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

使用 Next.js 与 Sanity CMS 构建 Corsair 官网博客:配置、内容模型与按需重新验证全解析

使用 Next.js 与 Sanity CMS 构建 Corsair 官网博客:配置、内容模型与按需重新验证全解析 使用 Next.js 与 Sanity CMS 构建 Corsair 官网博客配置、内容模型与按需重新验证全解析【免费下载链接】corsairConnect your users to their apps项目地址: https://gitcode.com/GitHub_Trending/corsa/corsair本篇技术指南以 Corsair 开源仓库中的官网www/目录为对象完整讲解其基于 Next.js 15 Sanity CMS 的博客架构从本地环境搭建、Sanity 项目初始化、环境变量体系到post内容模型、GROQ 数据读取、按需重新验证On-demand Revalidation以及 Markdown 种子文章迁移流程。读完本文你将掌握一套可直接复用的Next.js Sanity Portable Text内容中台搭建方案并能在自己的项目中落地同样的配置与工作流。项目概览官网技术构成Corsair 官网位于仓库的 www 目录是一个 Next.js 应用其 README 明确说明落地页视觉风格深受 twenty.com 启发而博客内容全部托管在 Sanity CMS 中正文使用 Portable Text 结构化富文本存储。从 www/package.json 的依赖可以看到完整技术栈Next.js 15^15.3.2 React 19开发服务器通过next dev --turbopack启动Sanity 4^4.22.0作为 CMS配合next-sanity^11.6.13做数据读取与 webhook 签名解析Drizzle ORM^0.44.5管理自身数据库认证、集成目录、OSS 认领等业务表Inngest^3.54.0承担后台任务better-auth承担认证tRPC v11承担 API 层Tailwind CSS 4负责样式搭配styled-components与 shadcn 组件体系。其中博客链路Sanity与业务链路Drizzle 数据库相互独立Sanity 只承载博客文章官网的集成目录、OSS 认领等功能则落在 Postgres Drizzle 上这一点从 www/src/db 目录下的多个 schema 文件可以看出。本地开发环境搭建README 给出的三步启动流程如下cp .env.example .env pnpm install pnpm devpnpm dev实际执行的是next dev --turbopack见 www/package.json即以 Turbopack 为打包器启动 Next.js 开发服务器。仓库使用 pnpm workspace 管理多包因此必须在仓库根目录安装依赖后进入www子包运行脚本corsair与corsair-dev/*系列包均以workspace:*形式引用。环境变量一览官网的环境变量由 www/sanity/env.ts 统一定义并提供默认值变量用途默认值NEXT_PUBLIC_SANITY_PROJECT_IDSanity 项目 ID必填缺失时assertSanityEnv()会直接抛错无NEXT_PUBLIC_SANITY_DATASETSanity 数据集名称productionNEXT_PUBLIC_SANITY_API_VERSIONSanity API 版本号2025-01-01SANITY_API_WRITE_TOKEN写操作 API Token迁移、写入用无SANITY_REVALIDATE_SECRET按需重新验证 Webhook 的签名密钥无在 www/src/lib/sanity/client.ts 中读写客户端是分离的getSanityClient()用于公开读取在生产环境NODE_ENV production自动启用 CDNgetSanityWriteClient()仅在配置了SANITY_API_WRITE_TOKEN时返回且强制useCdn: false确保写入基于最新数据。Sanity 首次配置从零到可发布README 给出了完整的首次配置八步流程下面结合源码逐项展开创建 Sanity 项目在 Sanity Manage 控制台新建项目。填入 Project ID将项目 ID 写入.env的NEXT_PUBLIC_SANITY_PROJECT_ID。创建 API Token在 Project → API → Tokens 中新建 Token权限选Editor写入SANITY_API_WRITE_TOKEN。迁移脚本 www/scripts/migrate-blog-to-sanity.ts 的错误提示中特别强调这里的 Editor 指 API Token 的权限类型而不是项目管理成员角色Manager 是给人用的项目成员角色不能作为 API Token 类型使用。配置 CORS 白名单在 Project → API → CORS 中依次添加http://localhost:3000本地开发https://corsair.dev生产环境部署 Schemapnpm sanity:schema该命令等价于sanity schema deploy把 www/sanity/schemaTypes/post.ts 中定义的post文档类型推送到远端项目。迁移种子文章可选pnpm blog:migrate将www/scripts/seed-data/下的 Markdown 文件导入 Sanity详见下文迁移章节。打开本地 Studio访问http://localhost:3000/studio。Studio 的本地配置在 www/sanity.config.ts 中basePath设为/studio启用structureTool并挂载自定义结构开发模式下额外启用visionTool方便调试 GROQ 查询。Sanity CLI 配置 www/sanity.cli.ts 指定了项目 ID、数据集以及托管 Studio 的域名前缀corsair-www。邀请编辑以 SEO 经理为例在 Sanity Manage → 项目 →Members中邀请协作者角色选择Editor将 Studio 地址https://corsair.dev/studio分享给对方。编辑无需接触代码仓库即可撰写、修改文章这正是把内容交给 CMS 而非 git 的核心收益。内容模型post 文档类型详解博客的内容契约定义在 www/sanity/schemaTypes/post.ts。post文档被划分为两个分组groupscontent内容title必填标题、slug由标题自动生成最长 96 字符注释标明最终 URL 形如/resources/blog/slug、description摘要3 行文本框必填且不超过 300 字符用作博客列表与 meta description 回退、author默认值Corsair Team、publishedAt发布时间、coverImage封面图开启 hotspot 裁剪带 alt 字段、body正文必填、faqs可选 FAQ 数组用于生成 FAQPage JSON-LD。seoSEO 与元数据metaTitle浏览器标签与搜索结果标题约 60 字符、metaDescription搜索结果摘要约 155 字符、targetKeywords目标关键词标签数组、socialShareImage社交分享图、jsonLd原始 JSON-LD可注入一个或多个结构化数据对象如 Article FAQPage。Portable Text 正文约束body字段通过defineArrayMember严格限制了可用的富文本能力与 README 中内容模型一节完全对应块级样式Normal、H2、H3、blockquote引用列表bullet无序、number有序行内标记strong加粗、em斜体、code行内代码以及 link 注解href允许相对路径scheme 限定http、https、mailto行内图片支持 alt 文本必填与可选 caption 说明。这些约束在渲染端由 www/src/components/blog/portable-text-components.tsx 一一映射为 Next.js 组件h2/h3/blockquote/normal各自拥有排版类名link组件会根据 href 是否为绝对地址自动决定是否添加target_blank与noreferrer noopener图片通过urlForImage走 Sanity 图片 CDN。也就是说编辑在 Studio 里能用的样式就是渲染端保证有对应 UI 的样式两端契约天然一致。博客数据读取GROQ 查询与页面渲染博客的数据读取集中在 www/src/lib/sanity/queries.ts使用next-sanity的defineQuery定义了三组 GROQ 查询allPostsQuery按publishedAt降序拉取全部文章字段含_id、title、slug.current、description、author、publishedAt、bodypostBySlugQuery按 slug 精确取单篇文章allPostSlugsQuery仅取 slug 列表用于生成静态路径。博客首页 www/src/app/blog/page.tsx 调用getAllPosts()渲染文章卡片列表并内置了空态处理当posts.length 0时展示No articles yet的虚线占位框。文章详情页位于 www/src/app/blog/[slug]/page.tsx正文部分由 Portable Text 渲染组件消费。按需重新验证发布即生效由于博客页面在构建时静态生成Sanity 中发布新文章后需要触发重新验证Revalidate这正是 README 中On-demand revalidation一节的用途。实现位于 www/src/app/api/revalidate/route.ts其流程如下从环境变量读取SANITY_REVALIDATE_SECRET缺失则返回 500使用next-sanity/webhook的parseBody解析请求体并校验签名签名不合法返回 401仅当body._type post时执行revalidateTag(blog)与revalidatePath(/blog)若载荷携带slug.current再额外重新验证/blog/slug详情页返回{ revalidated: true, now: Date.now() }。在 Sanity 中配置 Webhook生成一个随机密钥写入.env与生产环境的SANITY_REVALIDATE_SECRET在 Sanity Manage → API → Webhooks 创建 WebhookURLhttps://corsair.dev/api/revalidateDatasetproductionTrigger onCreate、Update、DeleteFilter_type postSecret与SANITY_REVALIDATE_SECRET相同的值这样编辑在 Sanity 中发布、更新或删除文章后官网无需重新部署即可在请求时增量刷新对应页面实现了内容即改即生效。将 Markdown 种子文章迁移到 Sanity仓库在 www/scripts/seed-data 中维护了 Markdown 格式的种子文章如anthropic-introduces-claude-fable-5.md带title、description、publishedAt、author的 frontmatter迁移脚本 www/scripts/migrate-blog-to-sanity.ts 负责将其转换为 Portable Text 并写入 Sanity转换流水线为frontmatter 解析用gray-matter读取 Markdown 头部元数据正文转 HTML用marked将 Markdown 编译为 HTMLHTML 转 Block用portabletext/block-tools的htmlToBlocks配合jsdom的 DOM 解析器把 HTML 转换为符合postschema 的 Portable Text 块数组图片上传正则提取alt形式的图片引用将public/下对应文件通过client.assets.upload上传为 Sanity 资产并生成带alt的 image block文件缺失或上传失败时仅打印警告并跳过提示后续可在 Studio 手动补充文档写入以post-slug作为稳定的_id调用client.createOrReplace实现幂等迁移重复执行不会产生重复文档。脚本在启动时会校验NEXT_PUBLIC_SANITY_PROJECT_ID与SANITY_API_WRITE_TOKEN缺失即退出并输出明确提示。常用命令速查README 与 www/package.json 共同定义了官网可用的脚本命令说明pnpm dev以 Turbopack 启动 Next.js 开发服务器pnpm build/pnpm start生产构建与启动pnpm sanity:schema部署 Schema 类型到 Sanitypnpm sanity:deploy部署托管 Studio 至corsair-www.sanity.studiopnpm blog:migrate将www/scripts/seed-data的 Markdown 种子文章导入 Sanitypnpm db:migrate/db:push/db:studioDrizzle 数据库迁移与可视化操作pnpm test运行src/**/*.test.ts下的 Node 测试pnpm inngest:dev以http://localhost:3000/api/inngest为端点本地调试 Inngest 后台任务实践要点与踩坑提示写 Token 权限易混淆迁移脚本的报错信息明确指出SANITY_API_WRITE_TOKEN必须是Editor 权限的 API Token而非项目成员角色把 Manager 成员角色误当作 API Token 类型是最常见的配置错误。CORS 必须双环境配齐本地与生产各需要一个 CORS 来源缺一不可否则对应环境下 Studio 无法正常读写。重新验证链路环环相扣SANITY_REVALIDATE_SECRET必须在 Sanity Webhook 的 Secret 与官网环境变量中保持一致签名校验失败会直接返回 401。空库有兜底 UI博客首页在无文章时渲染尚未发布占位状态新项目初始化后不会出现白屏。至此从 Sanity 项目初始化、post内容模型定义、GROQ 查询、Studio 嵌入到 Webhook 按需重新验证与 Markdown 迁移一套完整的 Next.js Sanity 博客内容管线已全部打通。这套方案既适合 Corsair 这类需要编辑自助发文 官网自动刷新的开源项目也可作为任意 Next.js 站点接入 Sanity CMS 的参考模板。【免费下载链接】corsairConnect your users to their apps项目地址: https://gitcode.com/GitHub_Trending/corsa/corsair创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表