尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

TinaCMS 的 @tinacms/graphql 数据层演进:索引排序、安全加固与自托管数据库实战

TinaCMS 的 @tinacms/graphql 数据层演进:索引排序、安全加固与自托管数据库实战 TinaCMS 的 tinacms/graphql 数据层演进索引排序、安全加固与自托管数据库实战【免费下载链接】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/tinacmsTinaCMS 是开源的 headless CMS将仓库中的 Markdown / MDX / JSON / YAML 文件变为可用 GraphQL 查询的数据库。tinacms/graphql正是承载这一能力的核心包它负责 schema 编译、内容索引、GraphQL 解析与媒体 URL 处理。本文以该包的 CHANGELOG 为骨架结合源码剖析其版本演进中的关键技术点——从 GraphQL API 简化、数据层datalayer与自托管数据库 API到索引排序正确性修复、路径穿越安全加固、多仓multi-repo支持与分支感知的媒体解析读完你可以掌握该包的架构全貌、升级注意事项以及自托管部署的关键配置。一、包定位把文件仓库变成可查询的数据库根据包的 READMEtinacms/graphql的职责是把一组文件夹与文件变成一个可以用 GraphQL 查询的数据库。它提供的核心能力包括用 GraphQL 查询 Markdown、MDX、JSON、YAML 等内容格式支持文档之间的引用reference关系预生成 schema 与查询数据加速网站编译。从 入口文件 可以看到它导出的核心构件Database、createDatabase/createLocalDatabase、FilesystemBridge/IsomorphicBridge、resolve、buildSchema即buildDotTinaFiles的包装以及tinacms/schema-tools中的Schema类型。整个数据流可以概括为Bridge如FilesystemBridge负责底层文件 I/O决定内容从哪里读、写到哪里Level 存储如memory-level或 MongoDB 适配器存放索引数据Database把二者粘合提供indexContent、get、put、query等操作resolve接收 GraphQL 查询字符串与变量从数据库中取数并返回结果。二、快速上手最小可运行示例README 给出了一个完整的独立使用示例无需整个 TinaCMS 站点。先安装依赖pnpm install pnpm add tinacms/graphql还需要配套安装tinacms/schema-tools与memory-level。然后准备内容文件content/in.md--- title: Hello ---接着编写程序源自 READMEimport { MemoryLevel } from memory-level; import { Database, FilesystemBridge, buildSchema, resolve } from tinacms/graphql; import { Schema } from tinacms/schema-tools; const dir content; // Where to source content from const bridge new FilesystemBridge(dir); // Where to store the index data const indexStorage new MemoryLevelstring, Recordstring, string(); // Create the schema/structure of the database const rawSchema: Schema { collections: [ { name: post, path: , // Dont require content to be placed within a subdirectory fields: [ { type: string, name: title, isTitle: true, required: true } ] } ] }; const schema await buildSchema({ schema: rawSchema, build: { publicFolder: , outputFolder: } }); // Create the object for editing and querying your repository const database new Database({ bridge, level: indexStorage, tinaDirectory: tina }); // Generate the index data required to support querying await database.indexContent(schema) // Query the database and output the result const graphQLQuery query { document(collection: post, relativePath: in.md) { ...on Document { _values, _sys { title } } } } const result await resolve({ database, query: graphQLQuery, variables: {} }); // Output the result console.log(JSON.stringify(result))运行后输出{data:{document:{_values:{_collection:post,_template:post,title:Hello},_sys:{title:Hello}}}}注意这里展示的查询形态已经是 CHANGELOG 中 0.60.0 版本GraphQL API 简化之后的产物document查询配合_values、_sys等带下划线的元数据字段。三、从 CHANGELOG 看 API 演进GraphQL 查询形态的简化CHANGELOG 记录了包历史上两次重大 API 变革理解它们有助于读懂旧文档与迁移代码。0.60.0GraphQL API 全面简化在 0.60.0 版本CHANGELOG 0.60.0 一节中GraphQL 查询结构发生了大幅重构getcollection name→collection namegetPostDocument(relativePath: $relativePath)简化为post(relativePath: $relativePath)getcollection nameList→collection nameConnection列表查询遵循 Relay cursor 规范通过edges/node结构返回getCollection/getCollections→collection/collections移除data层级字段定义上移一层post { data { title } }变为post { title }文档类型名不再以 Document 结尾... on AuthorDocument { data { name } }变为... on Author { name }元数据字段加下划线sys→_sysvalues→_values同时移除了dataJSON、form、getDocumentList等接口。0.62.0生成客户端与新构建命令0.62.0 引入了自动生成的类型安全客户端。schema 配置从ApiURL转向clientId、branch、只读token通配符 token并且tinacms build成为生成client.ts/types.ts的标准路径。引用解析默认深度为 5可通过 schema 的config.client.referenceDepth调整例如设为 1 以还原旧行为。0.2.0defineSchema 的类型体系重构更早期0.2.0的变革奠定了今天的字段模型collection 可以只定义fields单一模板或templates多模板联合type描述字段的数据形态string / number / boolean / datetime / object / reference 等ui描述编辑器行为每种类型都可以是list: true的列表object类型统一了 group / group-list / blocks 的概念列表查询遵循 GraphQL connection 规范多态对象的模板标记统一为_templateisBody字段替代隐式的_body。这些约定至今仍是 TinaCMS schema 设计的基石。四、数据层与自托管数据库 API从 onPut/level 到 gitProvider/databaseAdapterCHANGELOG 中与数据层相关的能力演进是该包最核心的架构线索。0.58.0实验性数据层0.58.0 首次加入文件内容与 GraphQL API 之间的数据层文档被索引后CMS 可以像传统 CMS 一样对外键引用约束、过滤与分页提供支持。到 0.59.9 时查询已支持过滤、分页、排序。1.4.29自托管数据库 API 的重塑1.4.29 是一次面向自托管用户的重要变更核心是参数替换弃用onPut、onDelete、level新增databaseAdapter替代level、gitProvider替代onPut/onDelete并配套推出tinacms-gitprovider-github包导出GitHubProvider。源码 中createDatabase的实现印证了这套规则当仍传入onPut、onDelete与level且没有databaseAdapter/gitProvider时为向后兼容仍构造旧式Database否则必须提供gitProvider与databaseAdapter缺失时会抛出明确错误。同时onPut/onDelete会打印弃用警告database/index.ts。自托管database.ts的推荐形态源自 CHANGELOG 1.4.29 一节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, });非 GitHub 用户可实现GitProvider接口onPut(key, value)与onDelete(key)接入自有 Git 平台。自托管后端也从/tina/api/gql.ts迁移到单一入口/api/tina/[...routes].{ts,js}由TinaNodeBackend统一托管本地环境使用LocalBackendAuthProviderAuthJS 环境使用AuthJsBackendAuthProvider。2.2.0levelBatchSize2.2.0 为DatabaseArgs增加了levelBatchSize选项允许覆盖 Level 批次写入的默认大小 25常量定义见 database/index.ts默认值读取见 database/index.ts。2.1.0yamlMaxLineWidth2.1.0 让 YAML 最大行宽可配置。此前硬编码为 80现在默认为禁用-1可通过 collection 的yamlMaxLineWidth属性调整最终透传给stringifyFile的 YAML 序列化见 database/index.ts。五、索引排序的正确性datetime 与数字索引键修复CHANGELOG 的 2.4.3 与 2.4.4 是索引正确性的关键修复可以从 datalayer.ts 源码中得到印证。2.4.3datetime 索引键改用 ISO 8601此前 datetime 字段以 Unix 时间戳写入索引键导致 2001-09-10 之前位数边界与 1970 年之前负时间戳的日期排序错误。修复后改为 ISO 8601 字符串。源码中的parseDatetimeUTCdatalayer.ts在生成索引键前将任意 datetime 值规范化为toISOString()随后在makeKeyForField中对datetime类型走该分支datalayer.ts。2.4.4数字索引键固定小数精度此前数字索引键直接零填充如9→0009整数与小数混排时字典序错误。修复后先按固定小数精度格式化再填充9→0009.000、8.5→0008.500。源码中的DEFAULT_NUMERIC_PADdatalayer.ts定义了左填充 4 位、小数精度 3 位padValuedatalayer.ts用Number(val).toFixed(pad.decimalPrecision).padStart(pad.maxLength, fillString)完成格式化。⚠️ 索引重建要求这两次修复都改变了索引键格式。本地开发/构建用户无感索引自动重建但自托管且使用持久化存储的用户必须执行一次完整tinacms build重建索引否则旧格式索引会导致排序错误。六、安全加固史路径穿越与代码执行防护CHANGELOG 记录了多条安全通告GHSA的修复这也是该包历史上投入最密集的方向之一。路径穿越CWE-22与符号链接逃逸CWE-592.1.3为 GHSA-5hxf-c7j4-279c、GHSA-2f24-mg4x-534q 增加路径穿越防护与测试2.1.2GraphQL 解析器确保文档/文件夹操作中的../等序列被校验操作被限制在 collection 配置的根目录内2.2.2修复反斜杠绕过——POSIX 上path.normalize()把反斜杠当作普通字符攻击者可用x\..\..\package.json绕过检查GHSA-v9p7-gf3q-h7792.2.3修复符号链接/junction 绕过GHSA-g87c-r2jp-293w、GHSA-g9c2-gf25-3x672.4.6修复媒体上传/删除路径可访问mediaRoot之外存储键的问题。源码层面FilesystemBridge 通过assertWithinBasefilesystem.ts做纵深防御先拒绝 NUL 字节、把反斜杠规范化为/再用path.resolve判定解析后路径是否仍在 base 目录内随后用fs.realpathSync解析符号链接真实路径做二次校验对尚不存在的路径会向上逐级查找已存在祖先。注释明确要求所有接受路径的公开方法都必须经过该校验。Markdown frontmatter 任意代码执行2.0.3修复了gray-matter默认在解析---js、---javascript、---coffee分隔符时执行 JavaScript 代码的漏洞。修复后仅 YAML、TOML、JSON 格式的 frontmatter 正常工作JSX/HTML 作为字符串数据仍可保存。破坏性变更如果内容文件使用上述 JS 分隔符升级后会报错需迁移到 YAML/TOML/JSON。依赖安全2.4.10js-yaml升级到 3.15.1GHSA-5p4m-2wfm-xmqj2.1.2更新jsonpath-plus修复安全漏洞。七、多仓支持与生成文件路由2.4.01.1.0 版本起支持从独立的 Git 内容仓库提供服务本地开发通过localContentPath指向内容仓库根目录相对路径需相对于.tina/config文件生产环境使用与内容仓库关联的clientId、branch、token。2.4.0 进一步完善了多仓场景当设置了localContentPath后不再把生成文件_schema.json、_graphql.json、_lookup.json、tina-lock.json写入内容仓库而是只保存在生成方仓库的tina/__generated__/中内容仓库甚至可以不再包含tina/目录。源码中 FilesystemBridge 对此有清晰实现isGeneratedPath识别tina/__generated__/与.tina/__generated__/前缀baseFor让这些路径始终解析到rootPath生成方其余内容文件解析到outputPath内容根assertGeneratedSubtreefilesystem.ts则进一步防止tina/__generated__/../../.env这类逃出生成子目录的路径。2.4.0 的迁移要点源自 CHANGELOG内容仓库中遗留的tina/__generated__/*与tina/tina-lock.json已无人读写建议在单个清理提交中删除ConfigManager.generatedFolderPathContentRepo已移除改用generatedFolderPathConfigManager.getTinaFolderPath不再接受isContentRoot选项依赖tina/__generated__/路径解析到内容仓库的自定义 bridge 子类需要更新生成的client.ts/database-client.ts对 TypeScript 项目改为无扩展名导入./types避免要求allowImportingTsExtensions兼容 Next.js 15.5JS 项目仍导入./types.js。该版本还带有明确的上线门槛必须在 TinaCloud 生产端部署配套服务后才能发布到latestdist-tag否则会破坏既有多仓用户的索引。八、媒体 URL 解析云 URL 与相对路径的往返CHANGELOG 的 0.60.6 引入了resolveMediaCloudToRelative与resolveMediaRelativeToCloud一对函数位于 media-utils.ts用于在 TinaCloud 云 URL 与仓库相对路径之间转换预览时resolveFieldData/parseMDX提供数据保存时buildFieldMutations/stringifyMDX写回文档。2.3.0 / 2.4.0分支感知媒体2.3.0 为GraphQLConfig增加了可选的branch与mediaBranch字段当 host 传入的branch与mediaBranch不同时image 字段解析会在 CDN 路径前加/__staging/{encodedBranch}/前缀写回时由resolveMediaCloudToRelative剥离该段。2.4.0 把 staging URL 格式修正为/__staging/branch/__file/path分支可能包含/如feat/my-branch若做 URL 编码CloudFront 在读取前会先解码导致 S3 写键中的%2F与读取路径不一致__file分隔符让分支保留自然/段同时明确文件路径的起点。源码中的stagingPrefixmedia-utils.ts与STAGING_SEGMENT正则media-utils.ts正是这一设计的实现。注意2.3.0–2.3.1 产生的旧格式 staging URL 无法通过新版resolveMediaCloudToRelative往返若曾在测试中开启过分支感知媒体升级后需从编辑器重新生成受影响字段。2.4.1 / 2.4.2外部 URL 保留与多 host 兼容2.4.1image 类型解析器保留绝对外部 URL2.4.2resolveMediaCloudToRelative不再只匹配config.assetsHost而是以clientId/…路径前缀作为持久不变式host 段因 stage 而异从而支持 PR / stage / 个人开发等多 host 场景避免绝对 URL 被误提交进内容仓库。源码中的cloudUrlPatternmedia-utils.ts与ABSOLUTE_URL正则media-utils.ts分别承担这两项职责resolveMediaRelativeToCloud还会对剥离 mediaRoot 后露出绝对 URL的损坏数据做自愈media-utils.ts。九、引用reference能力与引用完整性引用是 TinaCMS 文档间关联的基础0.57.1新增reference字段类型1.5.8加入引用完整性——重命名文档时所有引用该文档的文档同步更新删除文档时给出警告并移除相关引用1.5.14重写引用逻辑支持深层嵌套引用查找并为 collection 增加引用索引以查询深层引用1.5.13正确处理 noop 重命名1.4.33getLookup返回值类型可选允许取回整个文件。源码中getIndexDefinitionsdatabase/index.ts为每个 collection 都构建了__refs__伪索引字段__tina_ref____tina_ref_path__常量见 datalayer.ts写入文档时通过makeRefOpsForDocument维护引用索引这正是深层引用查询与引用完整性校验的数据基础。十、工程化与依赖治理CHANGELOG 也体现了该包与 monorepo 生态同步的工程化投入2.0.0从 CommonJS 迁移到 ESM2.4.9内部依赖由workspace:*发布时展开为精确版本改为workspace:^发布为 caret 范围。此前精确固定版本导致 npm 无法去重在一个 Astro TinaCMS 博客中产生三份tinacms、三份mermaid186 MB、五份date-fns151 MB、四份typescript88 MB共约 320 MB 的重复依赖peerDependencies同样受影响next-tinacms-cloudinary、tinacms-authjs等包以精确版本声明 peer 依赖会导致ERESOLVE冲突2.4.6日期处理统一到 date-fns v4移除 moment 栈tinacms删除 moment、moment-timezone、react-datetime通过 moment→date-fns token 转换器让既有dateFormat/timeFormatschema 无感升级admin 首屏体积减少约 18.6 KB gzip2.4.7把硬编码的错误消息字符串匹配替换为tinacms/schema-tools中共享的错误标识符常量#67771.6.2移除 Lodash改用原生函数或 es-toolkit 等价实现2.4.10移除失效的 typedoc 文档工具其docs脚本从未声明 typedoc 依赖且在 pnpm 隔离 node_modules 下无法解析二进制2.2.4新增displayOnly字段类型仅展示的表单字段2.4.3为ui.component: checkbox-group增加 schema 级校验——未声明list: true的 checkbox-group 字段含嵌套 object/template 字段会直接校验失败避免运行时 GraphQL 类型不匹配。十一、测试与质量保障2.3.1 为 GraphQL 解析器补充了 115 个直接单元测试覆盖resolveFieldData、build*Mutations、resolveLegacyValues及所有从已废弃的resolveDocument拆分出的方法resolveFieldData因此被从解析器模块导出resolver/index.ts不改变包的公共 API。1.5.11 则将测试框架切到 vitest。仓库内 spec 测试夹具 下按 forestry-sample、movies、movies-with-datalayer 等场景组织了大量.gql查询与.json期望响应是理解各版本行为差异的第一手资料。十二、升级与维护速查索引重建2.4.3datetime 索引键与 2.4.4数字索引键均要求自托管持久化存储用户执行完整tinacms build本地用户自动重建、无需操作破坏性变更2.0.3 起---js/---javascript/---coffeefrontmatter 报错2.4.0 移除generatedFolderPathContentRepo、getTinaFolderPath的isContentRoot选项并调整 generated 路径路由2.0.0 迁移 ESM自托管参数createDatabase要求gitProviderdatabaseAdapterlevel/onPut/onDelete仅作向后兼容依赖治理2.4.9 起内部依赖发布为 caret 范围pnpm install后可正常去重。综上tinacms/graphql的版本史几乎就是 TinaCMS 数据层与安全模型的演进史从 GraphQL 形态简化、数据层引入、自托管 API 重塑到索引排序正确性、路径穿越加固与多仓路由每一步都有对应的源码实现与测试佐证。对于自托管 TinaCMS 的维护者这份 CHANGELOG 与本文梳理的源码路径是排查索引异常、安全升级与多仓配置问题的最佳起点。【免费下载链接】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),仅供参考
返回列表