GitBook/Notion 集成技能
与托管文档平台的集成。
功能
- GitBook 空间管理
- Notion 数据库集成文档
- 内容与 Git 同步
- 导出/导入格式间转换
- 嵌入和块管理
- API 文档托管
- 分析数据检索
- Webhook 配置
使用方法
当你需要以下操作时,调用此技能:
- 同步文档到 GitBook 或 Notion
- 管理托管文档空间
- 导出内容以进行迁移
- 配置发布工作流
- 检索文档分析数据
输入
| 参数 | 类型 | 必需 | 描述 |
|---|---|---|---|
| platform | string | 是 | gitbook, notion |
| action | string | 是 | sync, export, import, analytics |
| spaceId | string | 否 | GitBook 空间或 Notion 数据库 ID |
| sourcePath | string | 否 | 源内容路径 |
| outputPath | string | 否 | 导出内容路径 |
输入示例
{
"platform": "gitbook",
"action": "sync",
"spaceId": "abc123",
"sourcePath": "./docs"
}
GitBook 集成
GitBook 配置
# .gitbook.yaml
root: ./docs
structure:
readme: README.md
summary: SUMMARY.md
redirects:
/old-page: /new-page
/api/v1: /api/v2
SUMMARY.md 结构
# 摘要
## 快速开始
* [介绍](README.md)
* [安装](getting-started/installation.md)
* [快速开始](getting-started/quickstart.md)
## 用户指南
* [配置](user-guide/configuration.md)
* [功能](user-guide/features.md)
* [认证](user-guide/features/auth.md)
* [数据管理](user-guide/features/data.md)
## API 参考
* [概述](api/README.md)
* [认证](api/authentication.md)
* [端点](api/endpoints/README.md)
* [用户](api/endpoints/users.md)
* [项目](api/endpoints/projects.md)
## 资源
* [FAQ](resources/faq.md)
* [更新日志](CHANGELOG.md)
GitBook API 集成
const GitBook = require('gitbook-api');
class GitBookManager {
constructor(token) {
this.client = new GitBook({ token });
}
// 列出空间
async listSpaces(organizationId) {
return await this.client.spaces.list({
organizationId
});
}
// 获取空间内容
async getContent(spaceId) {
const pages = await this.client.spaces.listPages(spaceId);
return pages;
}
// 更新页面
async updatePage(spaceId, pageId, content) {
return await this.client.pages.update(spaceId, pageId, {
document: {
markdown: content
}
});
}
// 创建页面
async createPage(spaceId, title, content, parentId = null) {
return await this.client.pages.create(spaceId, {
title,
parent: parentId,
document: {
markdown: content
}
});
}
// 从 Git 同步
async syncFromGit(spaceId, repoUrl, branch = 'main') {
return await this.client.spaces.sync(spaceId, {
source: 'github',
url: repoUrl,
branch
});
}
// 获取分析数据
async getAnalytics(spaceId, period = '30d') {
return await this.client.spaces.getAnalytics(spaceId, {
period
});
}
}
GitBook CI/CD 同步
# .github/workflows/gitbook-sync.yml
name: 同步到 GitBook
on:
push:
branches: [main]
paths:
- 'docs/**'
jobs:
sync:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: 同步到 GitBook
uses: gitbook/github-action-sync@v1
with:
token: ${{ secrets.GITBOOK_TOKEN }}
space: ${{ secrets.GITBOOK_SPACE_ID }}
Notion 集成
Notion 数据库模式
const notionSchema = {
database_id: 'abc123',
properties: {
'Title': {
type: 'title',
title: {}
},
'Slug': {
type: 'rich_text',
rich_text: {}
},
'Category': {
type: 'select',
select: {
options: [
{ name: '指南', color: 'blue' },
{ name: '参考', color: 'green' },
{ name: '教程', color: 'purple' }
]
}
},
'Status': {
type: 'status',
status: {
options: [
{ name: '草稿', color: 'gray' },
{ name: '审核', color: 'yellow' },
{ name: '已发布', color: 'green' }
]
}
},
'Last Updated': {
type: 'last_edited_time',
last_edited_time: {}
},
'Author': {
type: 'people',
people: {}
},
'Tags': {
type: 'multi_select',
multi_select: {
options: []
}
}
}
};
Notion API 集成
const { Client } = require('@notionhq/client');
class NotionDocsManager {
constructor(token) {
this.notion = new Client({ auth: token });
}
// 查询文档页面
async queryDocs(databaseId, filter = {}) {
const response = await this.notion.databases.query({
database_id: databaseId,
filter: {
and: [
{ property: 'Status', status: { equals: 'Published' } },
...Object.entries(filter).map(([prop, value]) => ({
property: prop,
[typeof value === 'string' ? 'rich_text' : 'select']: {
equals: value
}
}))
]
},
sorts: [
{ property: 'Last Updated', direction: 'descending' }
]
});
return response.results;
}
// 获取页面内容
async getPageContent(pageId) {
const blocks = await this.notion.blocks.children.list({
block_id: pageId
});
return this.blocksToMarkdown(blocks.results);
}
// 创建文档页面
async createDocPage(databaseId, title, content, properties = {}) {
const blocks = this.markdownToBlocks(content);
return await this.notion.pages.create({
parent: { database_id: databaseId },
properties: {
'Title': {
title: [{ text: { content: title } }]
},
'Slug': {
rich_text: [{ text: { content: this.slugify(title) } }]
},
'Status': {
status: { name: 'Draft' }
},
...properties
},
children: blocks
});
}
// 更新页面
async updatePage(pageId, content) {
// 清除现有块
const existing = await this.notion.blocks.children.list({
block_id: pageId
});
for (const block of existing.results) {
await this.notion.blocks.delete({ block_id: block.id });
}
// 添加新块
const blocks = this.markdownToBlocks(content);
await this.notion.blocks.children.append({
block_id: pageId,
children: blocks
});
}
// 将 Notion 块转换为 Markdown
blocksToMarkdown(blocks) {
let markdown = '';
for (const block of blocks) {
switch (block.type) {
case 'paragraph':
markdown += this.richTextToMarkdown(block.paragraph.rich_text) + '
';
break;
case 'heading_1':
markdown += '# ' + this.richTextToMarkdown(block.heading_1.rich_text) + '
';
break;
case 'heading_2':
markdown += '## ' + this.richTextToMarkdown(block.heading_2.rich_text) + '
';
break;
case 'heading_3':
markdown += '### ' + this.richTextToMarkdown(block.heading_3.rich_text) + '
',
break;
case 'bulleted_list_item':
markdown += '- ' + this.richTextToMarkdown(block.bulleted_list_item.rich_text) + '
';
break;
case 'numbered_list_item':
markdown += '1. ' + this.richTextToMarkdown(block.numbered_list_item.rich_text) + '
';
break;
case 'code':
markdown += '```' + block.code.language + '
';
markdown += this.richTextToMarkdown(block.code.rich_text);
markdown += '
'; break; case ‘quote’: markdown += '> ’ + this.richTextToMarkdown(block.quote.rich_text) + ’
'; break; case ‘callout’: markdown += '> ’ + block.callout.icon?.emoji + ’ '; markdown += this.richTextToMarkdown(block.callout.rich_text) + ’
'; break; } }
return markdown;
}
// 将 Markdown 转换为 Notion 块 markdownToBlocks(markdown) { const blocks = []; const lines = markdown.split(’ ');
let i = 0;
while (i < lines.length) {
const line = lines[i];
if (line.startsWith('# ')) {
blocks.push({
type: 'heading_1',
heading_1: { rich_text: [{ text: { content: line.slice(2) } }] }
});
} else if (line.startsWith('## ')) {
blocks.push({
type: 'heading_2',
heading_2: { rich_text: [{ text: { content: line.slice(3) } }] }
});
} else if (line.startsWith('### ')) {
blocks.push({
type: 'heading_3',
heading_3: { rich_text: [{ text: { content: line.slice(4) } }] }
});
} else if (line.startsWith('```')) {
const lang = line.slice(3);
let code = '';
i++;
while (i < lines.length && !lines[i].startsWith('```')) {
code += lines[i] + '
‘; i++; } blocks.push({ type: ‘code’, code: { language: lang || ‘plain text’, rich_text: [{ text: { content: code.trim() } }] } }); } else if (line.startsWith(’- ')) { blocks.push({ type: ‘bulleted_list_item’, bulleted_list_item: { rich_text: [{ text: { content: line.slice(2) } }] } }); } else if (/^\d+. /.test(line)) { blocks.push({ type: ‘numbered_list_item’, numbered_list_item: { rich_text: [{ text: { content: line.replace(/^\d+. /, ‘’) } }] } }); } else if (line.trim()) { blocks.push({ type: ‘paragraph’, paragraph: { rich_text: [{ text: { content: line } }] } }); }
i++;
}
return blocks;
} }
### Notion 导出脚本
```javascript
async function exportNotionToMarkdown(databaseId, outputDir) {
const manager = new NotionDocsManager(process.env.NOTION_TOKEN);
const pages = await manager.queryDocs(databaseId);
for (const page of pages) {
const title = page.properties.Title.title[0].plain_text;
const slug = page.properties.Slug.rich_text[0]?.plain_text || slugify(title);
const category = page.properties.Category.select?.name || 'uncategorized';
const content = await manager.getPageContent(page.id);
const frontMatter = `---
title: ${title}
notion_id: ${page.id}
last_updated: ${page.last_edited_time}
---
`;
const filePath = path.join(outputDir, category, `${slug}.md`);
await fs.mkdir(path.dirname(filePath), { recursive: true });
await fs.writeFile(filePath, frontMatter + content);
}
}
分析
GitBook 分析
async function getGitBookAnalytics(spaceId) {
const analytics = await gitbook.getAnalytics(spaceId, '30d');
return {
pageViews: analytics.pageViews,
uniqueVisitors: analytics.uniqueVisitors,
topPages: analytics.topPages.map(p => ({
path: p.path,
views: p.views
})),
searchQueries: analytics.searches.map(s => ({
query: s.query,
count: s.count,
noResults: s.noResults
}))
};
}
工作流程
- 配置 - 设置 API 凭证
- 连接 - 链接 Git 仓库或 Notion 数据库
- 同步 - 推送/拉取内容更改
- 发布 - 部署到托管平台
- 监控 - 跟踪分析和使用情况
依赖关系
{
"devDependencies": {
"@notionhq/client": "^2.2.0",
"gitbook-api": "^0.8.0",
"gray-matter": "^4.0.0"
}
}
CLI 命令
# 从 Notion 导出
node scripts/notion-export.js --database abc123 --output ./docs
# 同步到 GitBook
gitbook sync ./docs --space abc123
# 导入到 Notion
node scripts/notion-import.js --input ./docs --database abc123
应用的最佳实践
- 在 Git 中保持真相来源
- 在合并到主线时同步
- 使用一致的 Slug 模式
- 跟踪页面分析
- 设置 Webhook 以实现自动化
- 优雅地处理速率限制
参考
- GitBook API: https://developer.gitbook.com/
- Notion API: https://developers.notion.com/
- GitBook 同步: https://docs.gitbook.com/integrations/git-sync
目标流程
- knowledge-base-setup.js
- docs-versioning.js
- content-strategy.js