Droplink 服务 API 规范 v2.3
🤔 这是什么?
Droplink 服务 API 规范 - 调度器与执行器之间的通信协议
这个规范定义了**调度器(Scheduler)如何调用具体的执行器(Worker)**来完成实际任务。
核心作用
┌──────────────┐ ┌─────────────┐
│ 调度器 │────── HTTP 通信 ───▶│ 执行器 │
│ Scheduler │ (本文档定义) │ Worker │
│ │◀───── JSend+ ──────│ │
└──────────────┘ └─────────────┘
分发任务 执行任务调度器负责:
- 监听 Gotify 消息队列
- 解析用户的 actions
- 调用对应的执行器(使用本文档定义的 API)
- 处理执行结果
**执行器(Worker)**负责:
- 接收调度器的调用请求
- 实现本文档定义的 API 接口(
/api/process、/api/health等) - 返回 JSend+ 格式的响应
- 执行具体的业务逻辑(如保存到 Notion、生成 AI 摘要)
为什么要定义这个规范?
没有统一的规范:
调度器需要为每个 Worker 写不同的调用代码:
- Notion Worker → 调用 /notion/archive,返回 { result: ..., error: ... }
- Obsidian Worker → 调用 /obsidian/save,返回 { success: ..., data: ... }
- AI Worker → 调用 /ai/generate,返回 { status: ..., output: ... }
→ 代码重复,难以维护,扩展困难有了统一的规范:
调度器用统一的方式调用任何 Worker:
- 任何 Worker → 调用 /api/process,返回 JSend+ 格式
- 所有 Worker 返回相同格式的响应:{ status, code, data, requestId }
- 新增 Worker 时,只需实现标准接口即可
→ 代码简洁,易于维护,轻松扩展适用范围
本文档仅适用于:调度器 → 执行器(Worker)的 HTTP 通信
示例 Worker:
- Notion Worker - 保存链接到 Notion
- Obsidian Worker - 同步到 Obsidian
- AI Worker - 生成文章摘要
- Archive Worker - 归档链接
- 任何第三方 Worker - 只要遵循本规范即可集成
不适用于:
- ❌ 前端 → Gotify(使用 Gotify 官方 API)
- ❌ 调度器 → Gotify(使用 Gotify 消息拉取 API)
- ❌ 用户 → 前端(使用 UI 交互)
🏗️ 整体架构
架构图
┌─────────┐ ┌─────────┐ ┌──────────┐ ┌─────────────┐
│ Android │────────▶│ Gotify │◀─────── │ Scheduler │────────▶│Archive-Worker│
│ Chrome │ (POST) │ Server │ (监听) │ │ (HTTP) │ AI Worker │
│ iOS │ │ Queue │ │ │ │ NotionWorker│
└─────────┘ └─────────┘ └──────────┘ └─────────────┘
│ │ │ │
│ 推送消息 │ 拉取新消息 │ 调用 Worker │ 执行任务
│ (Gotify API) │ (轮询/WS) │ (JSend+ 规范) │ (Notion/AI)
│ │ │ │
✅ 200 OK │ 解析消息 │ 返回结果 │ 返回数据
(前端任务完成) │ 执行 actions │ (JSend+ 格式) │ (JSend+ 格式)各层职责
1. 前端层(Android/Chrome/iOS)
职责:收集用户输入,推送到 Gotify
通信协议:Gotify Push API
关键点:
- ✅ 收到 Gotify 的
200 OK即表示任务完成 - ✅ 不关心后续的处理细节
- ✅ 不等待 Worker 的执行结果
- ✅ 立即返回 UI 反馈(如"已推送")
示例流程:
typescript
// 1. 用户在 App 上分享链接
// 2. App 收集用户的 actions(如"归档到 Notion")
// 3. App POST 到 Gotify
// 4. 收到 200 OK,显示"已推送"
// 5. 任务结束,用户可以继续使用 App2. 消息队列层(Gotify)
职责:接收并存储前端推送的消息
通信协议:Gotify REST API
关键点:
- ✅ 消息持久化存储
- ✅ 支持多客户端推送
- ✅ 提供消息拉取接口
- ❌ 不会主动推送 Webhook
- ❌ 不关心消息内容
调度器如何获取消息:
- 方式 A:轮询 - 定期调用
GET /application/messages?since={lastId} - 方式 B:WebSocket - 实时监听消息流
3. 调度器层(Scheduler)
职责:监听 Gotify 消息,分发任务到 Worker
通信协议:
- Gotify → Scheduler:拉取消息(轮询/WS)
- Scheduler → Worker:JSend+ 规范(本文档定义)
关键流程:
typescript
// 1. 监听 Gotify 消息
while (true) {
const messages = await fetchGotifyMessages(sinceLastId)
for (const msg of messages) {
// 2. 解析消息
const message = JSON.parse(msg.message)
// 3. 按 handler 分组 actions
const actionsByHandler = groupBy(message.actions, 'handler')
// 4. 并发调用所有 Workers(使用 JSend+ 规范)
await Promise.all(
Object.entries(actionsByHandler).map(([handler, actions]) =>
callWorker(handler, message, actions)
)
)
}
await sleep(2000) // 2秒后再次轮询
}4. 工作层(Workers)
职责:执行具体的业务逻辑
通信协议:JSend+ 规范(本文档定义)
示例 Workers:
- Notion Worker - 保存链接到 Notion
- Obsidian Worker - 同步到 Obsidian
- AI Worker - 生成文章摘要
- Readability Worker - 提取网页正文
关键点:
- ✅ 必须实现
/api/process接口 - ✅ 必须返回 JSend+ 格式的响应
- ✅ 必须支持
X-Request-Id传递 - ✅ 错误必须区分
fail和error
为什么需要这个规范?
想象一下,Droplink 可能要调用很多外部服务:
- Notion Worker - 保存链接到 Notion
- Obsidian Worker - 同步到 Obsidian
- AI Worker - 生成文章摘要
- Readability Worker - 提取正文内容
- 未来可能还有更多...
如果没有统一的规范,每个 Worker 都要定义自己的 API 格式,调度器就要为每个服务写不同的调用代码,维护起来非常麻烦。
有了这个规范后:
- ✅ 所有 Worker 遵循相同的 API 接口
- ✅ 调度器可以用统一的方式调用任何 Worker
- ✅ 开发新 Worker 时只需要实现标准接口
- ✅ 容易测试和调试
- ✅ 错误处理和重试机制统一
📋 规范概述
设计原则
本 API 规范遵循以下业界标准:
1. RESTful 设计
- 使用标准的 HTTP 方法(GET、POST、PUT、DELETE)
- 资源导向的 URL 设计
- 语义化的 HTTP 状态码
- 无状态通信
2. JSend+ 响应规范
所有 API 响应遵循 JSend+ 规范(基于 JSend,增加了工程实践),统一响应格式:
基本约定:
- Content-Type:
application/json; charset=utf-8 - 时间格式:ISO 8601 UTC(如
2026-01-19T03:00:00Z) - RequestId(强制):客户端 SHOULD 发送
X-Request-Id,服务端 MUST 回传
响应结构:
typescript
// 成功响应
{
"status": "success",
"code": 0,
"data": {
// 响应数据
},
"requestId": "req_uuid" // 强制
}
// 业务失败(客户端错误)
{
"status": "fail",
"code": 1001001,
"message": "validation failed",
"details": {
// 结构化错误信息
},
"requestId": "req_uuid"
}
// 系统错误(服务器错误)
{
"status": "error",
"code": 2000001,
"message": "internal error",
"details": {
"retryable": true // 可选,指示是否可重试
},
"requestId": "req_uuid"
}关键字段说明:
status: MUST,"success" | "fail" | "error"code: MUST,机器可读错误码,成功 MUST 为 0requestId: MUST,用于排障追踪message: SHOULD,人可读的简短信息(success 可省略)data: success MUST 包含,fail/error 可选details: OPTIONAL,结构化信息(字段校验、重试建议等)
3. 设计目标
- 统一接口:所有 Worker 服务遵循相同的 API 格式
- 类型安全:完整的 TypeScript 类型定义
- 易于扩展:支持同步和异步任务
- 标准化错误:使用 JSend 规范统一错误处理
📚 文档导航
本文档已拆分为多个部分,方便查阅:
核心文档
- API 详细规范 - 完整的 API 接口定义和 TypeScript 类型
- 错误处理规范 - 错误码设计、重试机制、幂等性
- 实现示例 - 前端、调度器、Worker 的完整代码示例
- 完整流程示例 - 端到端的完整流程演示