Task Keeper 3.112 API 使用详解
Task Keeper 3.112 API 使用详解:External REST API 全端点、appSecret 鉴权、curl 实测回显与错误码表。
Task Keeper 3.112 API 使用详解
目标:讲清 Task Keeper(PiGa)v3.112 的 External REST API 体系——认证方式、全部对外端点、逐条 curl 实测回显与错误码。已在 Ubuntu 22.04 + Task Keeper v3.112 环境逐端点实测通过。
💡 说明:Task Keeper 没有独立的 CLI 工具集——
bin/server只是应用启动脚本(附带-DdropAndCreateNewDb/-DcreateAdminUser两个维护标志,已在《Ubuntu 22.04 安装 Task Keeper 3.112 指南》4.5 覆盖)。它的自动化能力由/externalapi/一组 REST 端点承担,因此本篇按 D5(API 使用详解)展开。
一、环境信息
- 应用版本:Task Keeper v3.112(Play Framework 2.8.20)
- 服务端点:
http://localhost:9000(裸机安装)或宿主机映射端口(Compose 部署) - 演示账号:admin / q1w2e3r4
- 应用密钥(
appSecret):application.conf中play.http.secret.key的值,本文实测环境为OQnYaBOXBc6lLC4IFudvdoG46XLm9f8I
二、前置条件
- Task Keeper 已安装并运行(安装见本专栏《Ubuntu 22.04 安装 Task Keeper 3.112 指南》或《Task Keeper 3.112 Docker Compose 部署指南》),健康检查通过:
curl -s http://localhost:9000/externalapi/healthcheck/
实测输出:
OK
- 拿到应用密钥。裸机形态:
grep -oP 'play.http.secret.key="\K[^"]+' /opt/task-keeper/conf/application.conf
实测输出:
OQnYaBOXBc6lLC4IFudvdoG46XLm9f8I
容器形态下即 compose 里 APPLICATION_SECRET 环境变量的值。
三、API 总览
Task Keeper 的 HTTP 接口分三组(依据:release 包内 conf/routes 路由表,查询日期 2026-10-25):
| 分组 | 前缀 | 鉴权方式 | 用途 |
|---|---|---|---|
| 页面与会话 | /login/、/logout/、/loggedin/、/app/* | 表单登录 → 会话 Cookie | 浏览器访问与脚本登录 |
| 内部 API | /scalajsapi/*、/websocket/hydropush/* | 会话 Cookie | 前端 SPA 与服务端通信(Scala.js 自动路由,二进制协议,不对外) |
| External API | /externalapi/* | URL 路径内携带 appSecret | 脚本/定时任务集成(本篇重点) |
External API 端点一览:
| 方法与路径 | 功能 | 是否需 appSecret |
|---|---|---|
GET /externalapi/healthcheck/ | 健康检查 | 否 |
GET /externalapi/dbschema/:appSecret/ | 查看数据库建表 DDL | 是 |
GET /externalapi/inserttask/:docId/:parentTag/:appSecret/*content | 向指定文档插入任务 | 是 |
GET /externalapi/gettasksasjson/:docId/:parentTag/:appSecret/ | 导出指定标签子树为 JSON | 是 |
GET /externalapi/share/:docId/:loginName/:appSecret/ | 把文档授权给指定用户 | 是 |
GET /externalapi/processdelayedtasks/:appSecret/ | 处理到期的延迟任务 | 是 |
GET /externalapi/regenordertokens/:dryOrWet/:appSecret/ | 重建任务排序令牌 | 是 |
GET /externalapi/clearoldentitymodifications/:dryOrWet/:appSecret/ | 清理历史实体修改日志 | 是 |
通用约定:
- 全部走 GET(含写操作),便于浏览器/书签/简单脚本直接触发;
- 鉴权方式:把
play.http.secret.key作为路径段:appSecret传入,密钥错误返回 500 错误页(无 401/403 细分); - 无分页/过滤/排序参数:任务导出固定返回「指定标签子树」全量;
- 响应体为纯文本(
OK开头)或 JSON。
💡 技术拓展:自托管应用的 API 鉴权常见几种方式——
方式 代表 特点 共享密钥(路径/参数携带) Task Keeper 最简单,适合内网脚本;密钥即根权限 Bearer Token / PAT Gitea、GitLab 可按用户签发与吊销 OAuth2 / OIDC Vikunja 等 标准三方集成,配置最重 Task Keeper 采用共享密钥模式,密钥等同管理员权限,务必只在可信网络中调用,且不要写进会外泄的脚本仓库。
四、认证与首次调用
4.1 表单登录(脚本模拟浏览器)
登录接口为表单 POST,成功后返回 303 跳转并种下会话 Cookie:
curl -s -c /tmp/tk.txt -X POST http://localhost:9000/login/ \
-d "loginName=admin&password=q1w2e3r4" -o /dev/null -w "%{http_code}\n"
实测输出:
303
携带 Cookie 校验登录态:
curl -s -b /tmp/tk.txt http://localhost:9000/loggedin/
实测输出:
true
密码错误时返回 400(实测):curl -s -X POST http://localhost:9000/login/ -d "loginName=admin&password=wrongpwd" -o /dev/null -w "%{http_code}\n" 输出 400。未登录访问 /app/ 返回 303 跳转到 /login/?returnTo=%2Fapp%2F。
4.2 健康检查(无需密钥)
curl -s http://localhost:9000/externalapi/healthcheck/
实测输出:
OK
该端点不校验密钥,适合作为负载均衡/监控探针。
五、各 API 域详解
5.1 数据库结构查询:dbschema
导出全部数据表的建表 DDL,用于了解数据模型或做集成开发:
SECRET=OQnYaBOXBc6lLC4IFudvdoG46XLm9f8I
curl -s "http://localhost:9000/externalapi/dbschema/$SECRET/"
实测输出:
OK
## USERS ##
create table `USERS` (`loginName` TEXT NOT NULL,`passwordHash` TEXT NOT NULL,`name` TEXT NOT NULL,`isAdmin` BOOLEAN NOT NULL,`id` BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,`lastUpdateTime` BLOB NOT NULL)
## DOCUMENT_ENTITIES ##
create table `DOCUMENT_ENTITIES` (`name` TEXT NOT NULL,`id` BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,`lastUpdateTime` BLOB NOT NULL)
## DOCUMENT_PERMISSION_AND_PLACEMENTS ##
create table `DOCUMENT_PERMISSION_AND_PLACEMENTS` (`documentId` BIGINT NOT NULL,`userId` BIGINT NOT NULL,`orderToken` BLOB NOT NULL,`id` BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,`lastUpdateTime` BLOB NOT NULL)
## TASK_ENTITIES ##
create table `TASK_ENTITIES` (`documentId` BIGINT NOT NULL,`contentHtml` TEXT NOT NULL,`orderToken` BLOB NOT NULL,`indentation` INTEGER NOT NULL,`collapsed` BOOLEAN NOT NULL,`checked` BOOLEAN NOT NULL,`delayedUntil` TIMESTAMP NULL,`tagsString` TEXT NOT NULL,`lastContentModifierUserId` BIGINT NOT NULL,`id` BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,`lastUpdateTime` BLOB NOT NULL)
## ENTITY_MODIFICATION_ENTITY ##
create table `ENTITY_MODIFICATION_ENTITY` (`userId` BIGINT NOT NULL,`entityId` BIGINT NOT NULL,`modification` BLOB NOT NULL,`date` TIMESTAMP NOT NULL,`instantNanos` BIGINT NOT NULL,`id` BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY)
密钥错误时实测返回 500 错误页(Play 默认 HTML 错误页,日志记录异常 id)。
5.2 插入任务:inserttask
GET /externalapi/inserttask/:docId/:parentTag/:appSecret/*content?isMarkdown=true&extraIndent=0
参数说明(依据:conf/routes 注释与 ExternalApi.scala 源码,查询日期 2026-10-25):
docId:文档 ID。文档在 Web 界面创建后,从浏览器地址栏 URL(/app/#/document/<id>)获得;parentTag:父任务标签名(URL 编码)。新任务会插入到「文档中第一个带该标签的任务」之下,作为其子任务;content:任务内容,推荐用base64:<base64编码>形式(直接放原文需自行 URL 编码);isMarkdown(可选):true时按 Markdown 解析内容;extraIndent(可选):在父任务基础上额外缩进的层级数,默认 0。
调用示例(向文档 12345 的 #inbox 标签下插入一条 Markdown 任务):
SECRET=OQnYaBOXBc6lLC4IFudvdoG46XLm9f8I
CONTENT=$(echo -n "Buy milk **today**" | base64 -w0)
curl -s "http://localhost:9000/externalapi/inserttask/12345/inbox/$SECRET/base64:$CONTENT?isMarkdown=true"
成功返回 OK。文档不存在或标签不存在时返回 500 错误页(实测:对不存在的 docId=999 调用返回 500,应用日志含 IllegalArgumentException: Could not find entry with id=999)。
5.3 导出任务:gettasksasjson
GET /externalapi/gettasksasjson/:docId/:parentTag/:appSecret/
导出指定文档中「第一个带 :parentTag 标签的任务及其全部子任务」为 JSON 数组,元素含 contentHtml、indentation、checked、tags 等字段(对应 TASK_ENTITIES 表结构,见 5.1)。docId 获取方式同 5.2。
调用示例:
curl -s "http://localhost:9000/externalapi/gettasksasjson/12345/inbox/$SECRET/"
成功时返回 OK + JSON 数组;文档不存在或该文档中没有带此标签的任务时返回 500(源码中 require(parentAndBelow.nonEmpty, s"Could not find $parentTag") 触发)。
5.4 文档授权:share
GET /externalapi/share/:docId/:loginName/:appSecret/
把文档 :docId 授权给用户 :loginName(该用户须已在「用户管理」中创建)。成功返回 OK。实测对不存在的文档调用返回 500。
5.5 延迟任务处理:processdelayedtasks
GET /externalapi/processdelayedtasks/:appSecret/
Task Keeper 的「延迟任务」机制:编辑器内 alt+shift+D 把任务排入延迟队列(需要文档中存在带 #delayed_tasks 与 #todo_unsorted 标签的任务作为队列位)。本端点把已到期的延迟任务回流到待办区,官方建议用 cron 每日调用一次:
SECRET=OQnYaBOXBc6lLC4IFudvdoG46XLm9f8I
curl -s "http://localhost:9000/externalapi/processdelayedtasks/$SECRET/"
实测输出(当前无到期任务):
OK
Moved 0 tasks (including children).
配合 cron 每日 04:00 调用(裸机形态):
echo '0 4 * * * root curl -s "http://localhost:9000/externalapi/processdelayedtasks/OQnYaBOXBc6lLC4IFudvdoG46XLm9f8I/" >/dev/null' > /etc/cron.d/task-keeper-delayed
5.6 排序令牌重建:regenordertokens
GET /externalapi/regenordertokens/:dryOrWet/:appSecret/
任务在文档中的顺序由 orderToken(BLOB)决定,长期大量插入/移动后排位精度可能耗尽,该端点重建全文档排序令牌。dryOrWet 取 dry(仅演练输出将做的事)或 wet(实际执行)。
实测(全新实例,dry):
curl -s "http://localhost:9000/externalapi/regenordertokens/dry/$SECRET/"
输出:
OK
全新实例无文档时无额外输出;有文档时会逐文档输出 Processing document <id>: <name> 及重排详情。日常维护建议先 dry 确认范围,再 wet 执行。
5.7 修改日志清理:clearoldentitymodifications
GET /externalapi/clearoldentitymodifications/:dryOrWet/:appSecret/
Task Keeper 把每次实体变更记录在 ENTITY_MODIFICATION_ENTITY 表(用于多端同步),长期运行会膨胀,该端点清理历史记录。同样支持 dry/wet。
实测 dry(查看将清理多少):
curl -s "http://localhost:9000/externalapi/clearoldentitymodifications/dry/$SECRET/"
输出:
OK
Total number of entity modifications: 0
Number of entity modifications to be removed: 0
实测 wet(实际清理):
OK
Total number of entity modifications: 0
Number of entity modifications to be removed: 0
Successfully removed 0 old modifications
六、错误码与排障约定
实测归纳:
| 场景 | HTTP 状态码 | 说明 |
|---|---|---|
| 正常调用 | 200 | 响应体以 OK 开头 |
| appSecret 错误 | 500 | Play HTML 错误页,服务端日志记录异常 id |
| docId 不存在 | 500 | 日志含 Could not find entry with id=<id> |
| parentTag 不存在 | 500 | 日志含 Could not find <tag> |
| 登录密码错误 | 400 | 无 Cookie 种下 |
| 未登录访问 /app/ | 303 | 跳转 /login/?returnTo=... |
排障路径:先 curl -s http://localhost:9000/externalapi/healthcheck/ 确认服务存活;500 类错误查服务端日志——裸机形态 journalctl -u task-keeper -n 50 --no-pager,容器形态 docker compose logs web | tail -50,错误页中的异常 id(如 8f5l4m0cn)可在日志中精确定位堆栈。
七、常见问题
7.1 调用 inserttask 报 404/路由不存在
检查 URL 段顺序:inserttask/:docId/:parentTag/:appSecret/*content——appSecret 在 content 之前,位置容易写反。content 建议统一用 base64: 前缀形式,避免特殊字符破坏路由解析。
7.2 密钥里有 / 导致路由解析错乱
appSecret 是 URL 路径段,生成密钥时避免 /、+、?、# 等字符。本文的生成命令(tr -dc 'a-zA-Z0-9')只保留字母数字,天然规避此问题。
7.3 能远程调用 External API 吗
可以,只要网络可达 9000 端口。但密钥随 URL 明文传输,公网暴露时务必在反向代理层加 TLS(如 Nginx + Let's Encrypt),否则密钥会被中间人截获。
7.4 内部 API(/scalajsapi/)能直接调吗
不建议。该组接口是 Scala.js 前端的自动路由,请求体为 boopickle 二进制序列化格式,无稳定对外契约,版本升级可能变更。脚本集成一律使用 /externalapi/。
八、参考链接
- 路由定义(全部端点权威清单):release 包内
conf/routes,或 https://github.com/nymanjens/piga/blob/master/conf/routes - External API 实现源码:https://github.com/nymanjens/piga/blob/master/app/jvm/src/main/scala/app/controllers/ExternalApi.scala
- 本专栏《Ubuntu 22.04 安装 Task Keeper 3.112 指南》
- 本专栏《Task Keeper 3.112 配置参数详解》