
TinaCMS 自托管实战指南基于 Next.js 14 Pages Router 的完整参考实现【免费下载链接】tinacmsTinaCMS is the leading open-source headless CMS that supports Markdown and Visual Editing. Your content is stored in your own GitHub repo ❤️项目地址: https://gitcode.com/GitHub_Trending/ti/tinacms本指南基于 TinaCMS 仓库中的官方参考示例 examples/next/tina-self-hosted-demo系统讲解如何在不依赖 TinaCloud 的情况下用 Next.js 14 Pages Router 自托管 TinaCMS 的完整后端由 Next.js API 路由承载 GraphQL 与认证流程、MongoDB生产/ 文件系统开发双模式存储、tinacms-authjsnext-auth的用户名密码认证以及可选的 GitHub 版本控制集成。读完本文你将掌握TinaNodeBackend的接线方式、数据库与认证的双模式切换原理、contentApiUrlOverride的作用以及从本地开发到生产部署的完整迁移路径。示例工程定位与总体架构tina-self-hosted-demo是 TinaCMS 仓库内专门用于演示自托管后端的参考实现reference implementation。与仓库中其他 kitchen-sink 示例不同它不共享examples/shared/中的内容而是自带独立的content/目录从而可以在真实内容上端到端验证「认证 数据库」的接线是否正确。该工程的核心架构由四部分组成Pages Router 承载后端所有路由位于pages/下自托管 GraphQL 端点位于pages/api/tina/[...routes].ts由TinaNodeBackend处理所有 GraphQL 查询、变更mutation与认证流程都经由这一个 catch-all 路由。双模式认证运行时检测TINA_PUBLIC_IS_LOCALtrue为真时切换为LocalAuthProvider无需凭据仅限开发/演示未设置时由tinacms-authjs提供的UsernamePasswordAuthJSProvider在next-auth背后运行。双模式数据库tina/database.ts 在本地模式下返回createLocalDatabase()基于文件系统的 LevelDB生产模式下返回由MongodbLevel支撑的createDatabase()连接串取MONGODB_URI数据库名tinacms集合名tinacms设置环境变量后还会接入tinacms-gitprovider-github实现 GitHub 版本控制。静态管理后台由tinacms build构建到public/admin/通过 Next.js rewrites 重写到/admin。后端 API 路由TinaNodeBackend 的单一路由接线自托管与 TinaCloud 模式的本质区别在于后端不再由 Tina 云端托管而是运行在你自己的 Node 服务里。示例通过一个 catch-all API 路由完成全部接线见 pages/api/tina/[...routes].tsimport { TinaNodeBackend, LocalBackendAuthProvider } from tinacms/datalayer; import { TinaAuthJSOptions, AuthJsBackendAuthProvider } from tinacms-authjs; import databaseClient from ../../../tina/__generated__/databaseClient; const isLocal process.env.TINA_PUBLIC_IS_LOCAL true; const handler TinaNodeBackend({ authProvider: isLocal ? LocalBackendAuthProvider() : AuthJsBackendAuthProvider({ authOptions: TinaAuthJSOptions({ databaseClient: databaseClient, secret: process.env.NEXTAUTH_SECRET, }), }), databaseClient, }); export default (req, res) { // Modify the request here if you need to return handler(req, res); };几个关键点databaseClient来自tina/__generated__/databaseClient这是 TinaCMS 根据 tina/config.tsx 中的 schema 在构建阶段自动生成的客户端。TinaNodeBackend接受authProvider与databaseClient两个核心参数GraphQL 执行与认证握手全部收口到这一个 handler 中。默认导出的函数保留了req, res透传层若需要自定义中间件如日志、请求改写可以在注释位置插入逻辑。数据库双模式文件系统 LevelDB 与 MongoDB 生产适配器examples/next/tina-self-hosted-demo/tina/database.ts 完整展示了生产级自托管数据库的接线import { createDatabase, createLocalDatabase } from tinacms/datalayer; import { MongodbLevel } from mongodb-level; import { GitHubProvider } from tinacms-gitprovider-github; const isLocal process.env.TINA_PUBLIC_IS_LOCAL true; export default isLocal ? createLocalDatabase() : createDatabase({ gitProvider: new GitHubProvider({ branch: process.env.GITHUB_BRANCH, owner: process.env.GITHUB_OWNER, repo: process.env.GITHUB_REPO, token: process.env.GITHUB_PERSONAL_ACCESS_TOKEN, }), databaseAdapter: new MongodbLevelstring, Recordstring, any({ collectionName: tinacms, dbName: tinacms, mongoUri: process.env.MONGODB_URI, }), namespace: process.env.GITHUB_BRANCH, });逐项拆解其底层含义createLocalDatabase()返回基于文件系统的 LevelDB开发时无需任何外部服务即可启动内容变更直接落到本地目录适合迭代 schema 与快速原型。createDatabase()生产模式入口需要两个关键依赖databaseAdapterMongodbLevel适配器mongoUri对应MONGODB_URI环境变量dbName与collectionName均为tinacms即内容索引存放在 MongoDB 的tinacms数据库中、tinacms集合内。gitProviderGitHubProvider负责把内容变更写回 GitHub 仓库需要owner、repo、branch与个人访问令牌token四个参数分别对应四个GITHUB_*环境变量。namespace取GITHUB_BRANCH用于按分支隔离数据命名空间使不同分支的内容互不串扰。认证双模式本地免登录与用户名密码认证认证是自托管场景下最容易出错的部分示例给出了两套可切换方案本地模式TINA_PUBLIC_IS_LOCALtrue前端使用 tina/config.tsx 中的LocalAuthProvider后端使用LocalBackendAuthProvider不校验任何凭据直接放行仅用于开发与演示。生产模式不设置该变量前端使用UsernamePasswordAuthJSProvider来自tinacms-authjs后端使用AuthJsBackendAuthProvider并将TinaAuthJSOptions传入databaseClient与NEXTAUTH_SECRET会话签名密钥作为 next-auth 的 authOptions。会话由 next-auth 隐式管理示例没有显式的/auth/*登录页登录/登出都经由TinaNodeBackend的请求处理器完成。用户数据即内容集合在自托管模式下TinaCMS 把用户当作一个受管集合来处理用户记录存放在 content/users/index.json通过tinacms-authjs导出的TinaUserCollection注入 schema{ users: [ { name: Tina User, email: usertina.io, username: tinauser, password: { value: tinarocks, passwordChangeRequired: true } } ] }示例预置了种子用户tinauser/tinarocks并设置了passwordChangeRequired: true因此首次登录会被强制要求修改密码——这是自托管多用户站点必须保留的安全习惯。TinaCMS 配置指向本地后端的 contentApiUrlOverrideexamples/next/tina-self-hosted-demo/tina/config.tsx 是管理后台的 schema 定义其核心切换点在于const config defineStaticConfig({ contentApiUrlOverride: /api/tina/gql, clientId: process.env.NEXT_PUBLIC_TINA_CLIENT_ID!, authProvider: isLocal ? new LocalAuthProvider() : new UsernamePasswordAuthJSProvider(), branch: process.env.NEXT_PUBLIC_TINA_BRANCH! || // custom branch env override process.env.NEXT_PUBLIC_VERCEL_GIT_COMMIT_REF! || // Vercel branch env process.env.HEAD!, // Netlify branch env token: process.env.TINA_TOKEN!, media: { // 如需 Cloudinary 媒体存储取消注释并使用 next-tinacms-cloudinary // loadCustomStore: async () { // const pack await import(next-tinacms-cloudinary); // return pack.TinaCloudCloudinaryMediaStore; // }, tina: { publicFolder: public, mediaRoot: uploads, static: true, }, }, build: { publicFolder: public, outputFolder: admin, }, schema: { collections: [TinaUserCollection, /* ... */] }, });contentApiUrlOverride: /api/tina/gql这是自托管的关键一行它把管理后台的 GraphQL 请求从 TinaCloud 改指向同域下的本地后端路由即上一节的 catch-all API 路由。branch的三级回退优先使用自定义的NEXT_PUBLIC_TINA_BRANCH其次 Vercel 注入的NEXT_PUBLIC_VERCEL_GIT_COMMIT_REF最后是 Netlify 注入的HEAD从而在不同平台部署时都能正确识别当前分支。clientId与token在自托管模式下仅为兼容性保留对应NEXT_PUBLIC_TINA_CLIENT_ID/TINA_TOKEN实际认证不依赖它们。媒体存储默认使用本地媒体驱动上传文件落入public/uploads/配置中注释掉的loadCustomStore块展示了切换到 Cloudinary 的替代路径——取消注释并设置 Cloudinary 相关环境变量即可。声明的内容集合该示例的 schema 声明了四个集合不含 kitchen-sink 示例中的 Blog、Tag 与共享 schema集合格式内容路径说明postmdxcontent/posts博客文章含 rich-text 正文、作者引用reference、日期与 hero 图authormdcontent/authors作者信息name、avatarpagemdcontent/pages基于 blocks 的页面hero / features / content / testimonial开启visualSelectorglobaljsoncontent/global全局配置header、footer、theme标记为global: truepage集合还通过ui.router将home、about文件映射到前台路由home→/方便在管理后台直接跳转预览。环境变量总览来自 .env.example变量用途MONGODB_URIMongoDB 连接串生产数据库GITHUB_OWNER/GITHUB_REPO/GITHUB_BRANCH/GITHUB_PERSONAL_ACCESS_TOKENGitHub 版本控制集成TINA_PUBLIC_IS_LOCAL设为true时启用LocalAuthProvider 文件系统数据库NEXTAUTH_SECRETnext-auth 会话签名密钥NEXT_PUBLIC_TINA_CLIENT_ID/TINA_TOKEN为兼容性保留自托管时实际不使用注意TINA_PUBLIC_IS_LOCAL是贯穿全局的开关——它同时决定后端认证 Provider、数据库适配器、前端认证 Provider 三个层面的行为务必保证运行时环境一致。前台路由结构示例采用 Next.js Pages Router各路由职责如下与 pages/ 目录对应路由用途/首页rewrites 到/home/[filename]动态页面渲染home、about/posts文章列表/posts/[filename]文章详情/api/tina/[...routes]TinaCMS GraphQL 认证后端/admin管理后台rewrites 到/admin/index.html/404404 页面其中两条 rewrites 定义在 next.config.js/→/home与/admin→/admin/index.html。由于不存在显式的/auth/*登录页next-auth 的会话逻辑完全由TinaNodeBackend隐式接管。常用命令与开发工作流以下命令定义在 package.json 中pnpm dev— 等价于cross-env TINA_PUBLIC_IS_LOCALtrue tinacms dev -c next dev以本地认证模式启动开发服务无需 MongoDB 与 GitHub 凭据即可体验完整编辑流程。pnpm dev:prod— 按生产认证/数据库配置进行开发会重建后端。pnpm build—tinacms build next build先构建管理后台静态产物再构建 Next.js 应用。pnpm start—tinacms build next start生产启动每次启动前重新构建 admin 产物。pnpm export— 静态导出。pnpm lint/pnpm format— 基于 Biome 的仓库级代码规范检查与格式化。编码规范与本示例的取舍示例遵循仓库级的 Biome TypeScript 规范见根目录 AGENTS.md但有两处刻意为之的 demo-only 偏差next.config.js 中通过eslint.ignoreDuringBuilds与typescript.ignoreBuildErrors关闭了构建期的 ESLint 与类型检查。文件内注释明确警告这会让「带错误的生产构建」通过——仅供演示切勿复制到生产项目。pages/_app.tsx 使用客户端渲染守卫useEffect挂载后setIsClient(true)在挂载完成前组件返回null以此规避 TinaCMS Provider 引发的 SSR/CSR 水合hydration不一致。部署与常见坑Gotchas切换到生产模式与部署时以下细节直接影响成败切换生产模式需要两步设置MONGODB_URI以及可选的GITHUB_*变量之外必须重新运行tinacms build可通过pnpm dev:prod触发让 admin 静态包拾取更新后的后端配置。平台分支识别示例未附带vercel.json或 Dockerfile部署按标准 Node 服务处理部署到 Vercel/Netlify 时分支从VERCEL_GIT_COMMIT_REF或HEAD环境变量读取见 tina/database.ts 中的namespace取值逻辑前端 branch 的回退链与之呼应。无 E2E 测试该 demo 未随附端到端测试仓库中其他 kitchen-sink 示例则有 Playwright 用例验证手段以手动流程为主。内容隔离此处的编辑不会影响examples/shared/与其它 kitchen-sink 应用因为content/是独立目录。构建期类型检查被关闭任何真实应用都应重新开启避免问题延迟到运行时才暴露。延伸阅读本示例自身的 AGENTS.mdexamples/next/tina-self-hosted-demo/AGENTS.md本文所有命令、环境变量与路由信息均以该文档为骨架。tinacms-authjs包源码与说明packages/tinacms-authjs包含TinaUserCollection、UsernamePasswordAuthJSProvider、TinaAuthJSOptions、AuthJsBackendAuthProvider的实现。后端支撑包tinacms/datalayerpackages/tinacms/datalayer提供TinaNodeBackend、createLocalDatabase、createDatabase。GitHub 版本控制 Providerpackages/tinacms-gitprovider-github对应GitHubProvider的实现。自托管数据库适配器mongodb-level为外部 npm 依赖见 package.jsonMongoDB 连接串参数mongoUri、dbName、collectionName的用法可在本示例的 tina/database.ts 中直接对照。小结自托管 TinaCMS 的本质是把「GraphQL 后端 认证 内容数据库」三件事全部收编进你自己的 Node 应用。本示例通过一个 catch-all API 路由TinaNodeBackend、一个双模式数据库适配器createLocalDatabase/createDatabaseMongodbLevel、一套双模式认证LocalBackendAuthProvider/AuthJsBackendAuthProvider以及一行contentApiUrlOverride把整个架构压缩到寥寥几个文件里可作为任何 Next.js 项目落地自托管 TinaCMS 的最小可运行模板。从本地开发到生产部署只需理解TINA_PUBLIC_IS_LOCAL这一个开关的贯穿语义再补上MONGODB_URI、NEXTAUTH_SECRET与GITHUB_*环境变量并重建 admin 产物即可。【免费下载链接】tinacmsTinaCMS is the leading open-source headless CMS that supports Markdown and Visual Editing. Your content is stored in your own GitHub repo ❤️项目地址: https://gitcode.com/GitHub_Trending/ti/tinacms创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考