Focalboard Todoist 数据迁移实战:将 Todoist JSON 归档一键导入 Focalboard 看板
【免费下载链接】focalboardFocalboard is an open source, self-hosted alternative to Trello, Notion, and Asana.项目地址: https://gitcode.com/GitHub_Trending/fo/focalboard
导读
本文介绍 Focalboard 官方提供的 Todoist 导入器(focalboard-todoist-importer),讲解如何把 Todoist 的完整数据导出为 JSON 归档,再通过importTodoist.ts脚本转换为 Focalboard 可识别的boardarchive归档文件,最终在 Focalboard 中一键导入为可编辑的看板。读完本文,你将掌握从数据导出、环境安装、命令行运行到源码级映射原理的完整迁移链路,并了解项目(Project)→看板(Board)、分组(Section)→列表选项(Select Option)、任务(Item)→卡片(Card)、备注(Note)→卡片描述等核心映射规则。
一、工作原理概述
Todoist 导入器是一个运行在 Node.js 环境下的命令行应用,其核心思路是"数据中转":
Todoist JSON 归档 │ ▼ importTodoist.ts(数据转换,ts-node 运行) │ ▼ Focalboard 归档(.boardarchive,JSONL 格式) │ ▼ Focalboard 前端 "Import archive"(解析并落库)整个链路不依赖 Todoist API 的实时鉴权,而是使用 Todoist 官方数据导出服务生成的全量 JSON 文件作为输入,因此迁移过程可重复、可离线、可审计。核心脚本位于 import/todoist/importTodoist.ts,它复用了webapp中 Focalboard 自身的块(Block)与看板(Board)数据模型,保证生成的归档与 Focalboard 内部数据结构完全一致。
二、第一步:从 Todoist 导出全量 JSON 数据
导入器要求输入一个包含 Todoist 全部数据的 JSON 文件。按以下步骤获取:
- 访问 Todoist 的开源数据导出服务(darekkay.com 提供的 Todoist Export 工具)。
- 在Export As(导出格式)选项中选择
JSON (all data)(JSON 全量数据)。 - 若Archived(已归档)选项被勾选,请取消勾选,以确保导出范围符合预期。
- 点击Authorize and Backup(授权并备份)。此时会跳转到你的 Todoist 账号进行授权,请按屏幕提示完成操作。
- 记下下载得到的json文件名称与存放位置,后续命令行参数需要引用该路径。
需要说明的是,导出工具与 Todoist 的授权交互属于第三方服务行为,本仓库仅消费其产出的 JSON 文件;从源码结构看,导入器只依赖该 JSON 的结构,不绑定特定导出渠道。
三、第二步:安装依赖
导入器是一个独立的 TypeScript 项目,其声明位于 import/todoist/package.json,运行时依赖minimist(命令行参数解析),开发依赖包括ts-node、typescript、eslint等。由于脚本直接引用webapp下的源码模块(见下文源码解析),因此需要先在webapp目录安装一次依赖。
在仓库根目录下依次执行:
# 1. 安装 Focalboard 前端依赖(提供 Block / Board / Card 等数据模型) cd focalboard/webapp npm install # 2. 安装 Todoist 导入器自身依赖 cd ../import/todoist npm install说明:
focalboard/webapp即本仓库的 webapp 目录;若你已将仓库克隆到其他路径,请按实际路径调整。安装完成后,import/todoist下会出现node_modules与ts-node可执行环境。
四、第三步:运行导入命令
在focalboard/import/todoist目录内执行:
npx ts-node importTodoist.ts -i <path-to-todoist.json> -o archive.boardarchive其中:
| 参数 | 含义 | 默认值 | 说明 |
|---|---|---|---|
-i | 输入的 Todoist JSON 文件路径 | 无(必填) | 指向第一步下载的 json 文件 |
-o | 输出的 Focalboard 归档文件路径 | archive.boardarchive | 可自定义,如todoist-import.focalboard |
命令成功后会打印Exported to <outputFile>。若文件不存在,程序会输出File not found: <path>并以退出码2结束;若缺少-i参数则调用showHelp()后以退出码1结束(见 importTodoist.ts)。
也可以直接复用项目预置的测试脚本(见 package.json 的scripts段):
npm test # 等价于 ts-node importTodoist.ts -i test/todoist.json -o test/todoist-import.focalboard npm run debug:test # 以调试模式运行(--inspect=5858),可配合 Chrome DevTools 断点调试转换逻辑五、第四步:在 Focalboard 中导入归档
- 启动 Focalboard,进入目标工作区。
- 点击界面上的
Settings(设置)。 - 选择
Import archive(导入归档)。 - 选中上一步生成的
archive.boardarchive文件。
导入完成后,Todoist 中的各个项目会以看板形式出现在工作区中。归档的解析与落库由 Focalboard 前端 archiver.ts 与后端 API 配合完成,前端先使用ArchiveUtils.parseBlockArchive读取 JSONL 内容,再通过导入接口写入存储。
六、源码级解析:Todoist 数据如何映射到 Focalboard
转换的核心逻辑集中在convert(input, project)函数(importTodoist.ts),映射规则如下:
1. 项目 → 看板(Board)
- 遍历
input.projects,每个非 Inbox 的 Todoist 项目生成一个 Focalboard 看板。 - 看板标题与描述均取项目名
project.name。 - 命令行会打印
Board: <project.name>便于跟踪进度。
2. 分组(Section)→ 列表选择属性(Select)
- 项目内的分组(Section)被映射为一个名为
List、类型为select的卡片属性(IPropertyTemplate),每个分组对应一个带颜色的选项(option)。 - 选项颜色从 10 色调色板轮换分配:
propColorGray、propColorBrown、propColorOrange、propColorYellow、propColorGreen、propColorBlue、propColorPurple、propColorPink、propColorRed(见 importTodoist.ts)。 - 关键分支逻辑:
getProjectColumns会先收集项目自身的 section;若项目没有任何 section(列表长度不大于 1),则退化为使用五个默认分组:No Status、Next Up、In Progress、Completed、Archived(importTodoist.ts)。这是让无分组项目也能在 Focalboard 看板中呈现列结构的关键设计。
3. 任务(Item)→ 卡片(Card)
- 每个任务生成一张卡片,标题为任务内容
item.content。 - 卡片所属分组通过
section_id映射为List属性的选项值;无分组任务归入No Status选项。若映射失败会打印警告Invalid idList: ...但不会中断转换。 - 卡片在 Focalboard 数据模型中的类型定义可参考 webapp/src/blocks/card.ts。
4. 备注(Note)与附件 → 卡片描述
getCardDescription收集该任务下的所有备注(input.notes中item_id匹配的任务),合并为卡片描述文本(以空行分隔)。- 若备注带文件附件(
file_attachment),描述中会追加 Markdown 链接:Attachment: <title>(importTodoist.ts)。 - 描述以独立的文本块(Text Block)形式挂载到卡片下,并通过
contentOrder建立父子关系;文本块的创建逻辑见 webapp/src/blocks/textBlock.ts。
5. 看板视图(Board View)
- 每个看板附带一个名为
Board View、类型为board的视图块,使导入结果默认以看板(Kanban)形态展示(importTodoist.ts);视图字段结构可对照 webapp/src/blocks/boardView.ts 中的createBoardView。
6. 特殊处理:Inbox 项目
- 名为
Inbox的项目会被直接跳过,不生成看板(importTodoist.ts),避免把 Todoist 的收件箱杂项带入工作区。
七、归档文件格式:JSONL 与 ArchiveUtils
生成的.boardarchive是一个JSON Lines(JSONL)文件,由 import/util/archive.ts 中的ArchiveUtils.buildBlockArchive构建:
- 第一行为头(Header):
{"version":1,"date":<时间戳>}。 - 之后每行一个 JSON 对象,
type为board或block,data为对应的看板或块数据。
{"version":1,"date":1699999999999} {"type":"board","data":{...}} {"type":"block","data":{...}}读取侧parseBlockArchive会校验头版本号(version >= 1),逐行解析并按type分类,解析失败会抛出ERROR parsing line <n>。这种"一行一实体"的格式便于增量扩展与流式处理,源码注释中亦提到未来可扩充更多行类型。
八、Todoist JSON 数据结构速览
import/todoist/todoist.ts 使用 quicktype 根据实际导出样本生成了完整的 TypeScript 类型定义,转换脚本只消费其中四类核心数据:
projects:项目数组(含id、name、inbox_project、is_archived等字段)。sections:分组数组(含id、project_id、name、is_deleted等字段)。items:任务数组(含id、project_id、section_id、content、parent_id、priority等字段)。notes:备注数组(含id、item_id、content、file_attachment等字段)。
理解该结构有助于排查"某类数据未被迁移"的问题——例如标签(labels)、提醒(reminders)、过滤器(filters)在类型定义中存在,但当前导入器并未做映射,属于已知的覆盖范围边界。
九、其他实现细节与注意事项
- ID 生成:转换过程中所有 Focalboard 实体 ID 由 import/todoist/utils.ts 的
Utils.createGuid()生成(基于crypto.randomBytes的 UUID 实现),保证每次转换生成的归档 ID 互不冲突。 - 运行环境:脚本顶部有
(global.window as any) = {}的兼容处理(importTodoist.ts),用于在 Node 环境复用时让依赖浏览器全局对象的工具函数正常工作。 - 类型检查:
tsconfig.json启用了strict与strictNullChecks,若二次开发请保持类型完整,避免引入空值访问。 - 适用前提:本文命令基于仓库当前版本(
typescript ^4.1.5、ts-node ^9.1.1、minimist ^1.2.6)验证;导入仅覆盖上文所述的映射范围,Todoist 中未映射的数据(如标签、重复任务规则、提醒)不会出现在导入结果中,请在迁移前评估业务影响。
十、小结
Focalboard 的 Todoist 导入器以"JSON 导出 → 命令行转换 → 前端导入"三段式流水线,提供了一条低成本、可复现的数据迁移路径。其设计复用webapp的块模型与统一的 JSONL 归档协议,也让开发者可以参照 import/todoist 的结构,为其他数据源(仓库内另有 asana、jira、trello、notion、nextcloud-deck 等导入器)快速编写同类迁移工具。
【免费下载链接】focalboardFocalboard is an open source, self-hosted alternative to Trello, Notion, and Asana.项目地址: https://gitcode.com/GitHub_Trending/fo/focalboard
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考