文档操作
文档运行、内容读写、协同感知、协作状态管理和复制等操作。
源码: apps/backend/src/routes/documents.ts、apps/backend/src/routes/document-edit.ts、apps/backend/src/routes/document-awareness.ts
GET /documents/:documentId/content
获取文档的 JSON 内容和内容哈希(content_hash)。哈希用于后续编辑时的乐观并发控制。
认证方式
JWT Token / API Key / Agent Token(combinedAuth)
请求参数
Path 参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
documentId | string | 是 | 文档 UUID |
响应格式
状态码: 200
{
"content": {
"type": "doc",
"content": [
{
"type": "codeBlock",
"attrs": { "id": "block-uuid", "name": "代码块" },
"content": [{ "type": "text", "text": "console.log('hello')" }]
}
]
},
"content_hash": "a1b2c3d4",
"title": "文档标题",
"updated_at": "2026-03-12T10:00:00.000Z"
}| 字段 | 类型 | 说明 |
|---|---|---|
content | ProsekitDocument | ProseMirror 格式的文档内容 |
content_hash | string | FNV-1a 哈希(8 字符 hex),用作 base_version |
title | string | null | 文档标题 |
updated_at | string | 最后更新时间 |
错误码
| 状态码 | 说明 |
|---|---|
| 404 | 文档不存在或无权访问 |
POST /documents/:documentId/edit
命令式或全量编辑文档内容,支持乐观并发控制。
认证方式
JWT Token / API Key / Agent Token(combinedAuth),需要文档写入权限。
并发控制机制
- 调用方先通过
GET /documents/:documentId/content获取content_hash - 编辑时将此值作为
base_version传入 - 若文档在此期间被修改,返回
409 Conflict+ 最新内容 - 若文档正在被协同编辑(Hocuspocus 活跃连接),返回
423 Locked
请求参数
Path 参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
documentId | string | 是 | 文档 UUID |
Body 参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
base_version | string | 是 | 当前持有的 content_hash |
mode | "commands" | "content" | 是 | 编辑模式 |
commands | Command[] | mode=commands 时必填 | 命令列表 |
content | ProsekitDocument | mode=content 时必填 | 全量替换的文档内容 |
编辑模式
全量替换模式 (mode: "content")
直接替换整个文档内容:
{
"base_version": "a1b2c3d4",
"mode": "content",
"content": {
"type": "doc",
"content": [...]
}
}命令式编辑模式 (mode: "commands")
通过命令序列精细操作文档中的 Block:
{
"base_version": "a1b2c3d4",
"mode": "commands",
"commands": [
{ "op": "list" },
{ "op": "read", "blockId": "block-uuid" },
{ "op": "write", "blockId": "block-uuid", "content": { "type": "paragraph", "content": [...] } },
{ "op": "insert", "after": "block-uuid", "block": { "type": "codeBlock", "attrs": { "id": "new-uuid" }, "content": [...] } },
{ "op": "delete", "blockId": "block-uuid" },
{ "op": "replace", "blockId": "block-uuid", "block": { ... } },
{ "op": "move", "blockId": "block-uuid", "after": "target-uuid" },
{ "op": "str_replace", "blockId": "block-uuid", "old_str": "旧文本", "new_str": "新文本" }
]
}支持的命令:
| 命令 | 参数 | 说明 |
|---|---|---|
list | - | 列出所有顶层 Block 的 id、type、name、index |
read | blockId | 读取指定 Block 的完整内容 |
read_range | from, to | 读取两个 Block 之间(含)的所有 Block |
write | blockId, content | 替换指定 Block 的 content(保留 type 和 attrs) |
insert | block, after? | 在指定 Block 之后插入新 Block(after=null 则插入开头) |
delete | blockId | 删除指定 Block |
replace | blockId, block | 整体替换指定 Block(包括 type 和 attrs) |
move | blockId, after? | 将 Block 移动到指定位置(after=null 则移到开头) |
str_replace | blockId, old_str, new_str | 在 Block 内做唯一文本替换(匹配数不为 1 则报错) |
响应格式
成功 200:
{
"success": true,
"version": "e5f6g7h8",
"results": [
{ "op": "list", "success": true, "data": [...] },
{ "op": "write", "success": true }
]
}版本冲突 409:
{
"error": "version_conflict",
"message": "文档已被修改,请重新获取最新内容",
"current_version": "new-hash",
"current_content": { "type": "doc", "content": [...] }
}文档锁定 423:
{
"error": "document_locked",
"message": "文档正在被协同编辑或等待落库,请稍后重试",
"active_connections": 2,
"scope": "local"
}错误码
| 状态码 | 错误 | 说明 |
|---|---|---|
| 400 | 无效请求 | base_version 缺失、mode 无效、commands 为空等 |
| 403 | 无写入权限 | 用户缺少 write 权限 |
| 404 | 文档不存在或无权访问 | 文档不存在或用户无权限 |
| 409 | version_conflict | 文档在读取后已被修改 |
| 423 | document_locked | 文档正在被协同编辑 |
GET /documents/:documentId/awareness
获取文档的在线协作者信息,包括用户身份、光标位置和所在 Block。
认证方式
JWT Token / API Key / Agent Token(combinedAuth)
请求参数
Path 参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
documentId | string | 是 | 文档 UUID |
响应格式
状态码: 200
{
"collaborators": [
{
"clientId": 1234,
"userId": "user-uuid",
"name": "张三",
"color": "#ff6b6b",
"cursor": { "anchor": 42, "head": 42 },
"selection": {
"blockId": "block-uuid",
"blockType": "codeBlock",
"text": null
}
}
],
"active_connections": 1,
"scope": "local"
}| 字段 | 类型 | 说明 |
|---|---|---|
collaborators | array | 在线协作者列表 |
collaborators[].clientId | number | Yjs 客户端 ID |
collaborators[].userId | string | null | 用户 ID |
collaborators[].name | string | null | 用户名 |
collaborators[].color | string | null | 光标颜色 |
collaborators[].cursor | object | null | ProseMirror 光标位置 |
collaborators[].selection | object | null | 光标所在的 Block 信息 |
collaborators[].selection.blockId | string | Block ID |
collaborators[].selection.blockType | string | Block 类型 |
collaborators[].selection.text | string | undefined | 选区文本(有选区时) |
active_connections | number | 活跃连接数 |
scope | string | 固定为 "local"(仅当前进程) |
scope 说明:当前仅覆盖本进程的协同连接。多实例部署时,协作连接可能分布在不同实例上。
错误码
| 状态码 | 说明 |
|---|---|
| 404 | 文档不存在或无权访问 |
POST /documents/:documentId/run
运行文档(调试模式)。支持三种响应模式:SSE 流式、NDJSON 流式、JSON 一次性。
认证方式
JWT Token(仅支持 JWT,combinedAuth)
请求参数
Path 参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
documentId | string | 是 | 文档 UUID |
Body 参数
{
"inputs": { "inputName": "值" },
"stream": true,
"format": "sse",
"content": { "type": "doc", "content": [...] },
"timeout": 60
}| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
inputs | object | {} | Input Block 的输入值(key 为 Input Block 名称) |
stream | boolean | true | 是否使用流式响应 |
format | "sse" | "ndjson" | "sse" | 流式响应格式(仅 stream=true 时生效) |
content | object | - | ProseMirror 文档内容(可选,调试模式可直接传入,跳过数据库查询) |
timeout | number | - | 超时时间(秒),不超过管理后台配置的最大值 |
允许空 body(使用默认值)。
响应格式
SSE 流式响应(默认)
Content-Type: text/event-stream
事件类型:
| 事件 | 数据字段 | 说明 |
|---|---|---|
run_start | runId, documentId | 运行开始 |
block_start | blockId, blockType, blockName | Block 开始执行 |
block_chunk | blockId, delta | Block 输出片段(流式输出) |
block_complete | blockId, output | Block 执行完成 |
block_end | blockId | Block 生命周期结束 |
block_error | blockId, error | Block 执行出错 |
tool_start | blockId, toolCallId, toolName | 工具调用开始 |
tool_result | blockId, toolCallId, result | 工具调用结果 |
run_error | runId, error, message | 运行异常(如超时) |
run_complete | runId, globalCtx, sessions, completedBlocks, duration, timedOut | 运行完成 |
NDJSON 流式响应
Content-Type: application/x-ndjson
每行一个 JSON 对象,包含 type 字段标识事件类型,字段与 SSE 事件一致。
JSON 一次性响应(stream: false)
状态码: 200
{
"runId": "uuid",
"documentId": "uuid",
"globalCtx": [...],
"sessions": [
{
"blockId": "uuid",
"blockType": "code",
"blockName": "代码块",
"status": "completed",
"output": "输出内容"
}
],
"duration": 1234,
"timedOut": false
}错误码
| 状态码 | 错误 | 说明 |
|---|---|---|
| 400 | 输入参数验证失败 | inputs 不符合 Input Block 的 schema 定义 |
| 400 | no_team_associated | 文档没有关联团队 |
| 402 | insufficient_credits | 积分余额不足 |
| 403 | 调试模式仅支持 JWT 认证 | 使用了非 JWT 认证 |
| 403 | 无运行权限,请联系项目管理员授权 | 用户缺少 execute 权限 |
| 404 | 文档不存在或无权访问 | 文档不存在或用户无权限 |
| 500 | balance_check_failed | 余额查询异常 |
GET /documents/:documentId/yjs-state
获取文档的 Yjs 协作状态缓存。
认证方式
JWT Token / API Key(combinedAuth)
请求参数
Path 参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
documentId | string | 是 | 文档 UUID |
响应格式
状态码: 200
{
"state": [1, 2, 3, ...]
}| 字段 | 类型 | 说明 |
|---|---|---|
state | number[] | null | Yjs 状态数组,null 表示无缓存 |
错误码
| 状态码 | 错误 | 说明 |
|---|---|---|
| 404 | 文档不存在或无权访问 | 无权限或文档不存在 |
PUT /documents/:documentId/yjs-state
保存文档的 Yjs 协作状态缓存。
认证方式
JWT Token / API Key(combinedAuth)
请求参数
Path 参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
documentId | string | 是 | 文档 UUID |
Body 参数
{
"state": [1, 2, 3, ...]
}| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
state | number[] | 是 | Yjs 状态数组 |
响应格式
状态码: 200
{
"success": true
}错误码
| 状态码 | 错误 | 说明 |
|---|---|---|
| 400 | 无效的 state 参数 | state 缺失或非数组 |
| 403 | 无写入权限 | 用户缺少 write 权限 |
| 404 | 文档不存在或无权访问 | 无权限或文档不存在 |
POST /documents/:projectId/duplicate
复制文件或文件夹节点(包含其下所有文档)。
认证方式
JWT Token(仅支持 JWT,combinedAuth)
请求参数
Path 参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
projectId | string | 是 | 项目 UUID |
Body 参数
{
"nodeKey": "document-uuid-or-folder-key",
"nodeType": "file"
}| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
nodeKey | string | 是 | 目标节点的 key(文档 ID 或文件夹 key) |
nodeType | "file" | "folder" | 是 | 节点类型 |
响应格式
状态码: 200
{
"success": true,
"node": {
"key": "new-uuid",
"label": "[Copy]原文档名",
"type": "file",
"docType": "workflow",
"isLeaf": true
}
}| 字段 | 类型 | 说明 |
|---|---|---|
success | boolean | 是否成功 |
node | FolderNode | 新创建的节点信息 |
复制命名规则:
"文档A"->"[Copy]文档A""[Copy]文档A"->"[Copy-2]文档A"
错误码
| 状态码 | 错误 | 说明 |
|---|---|---|
| 400 | nodeKey and nodeType are required | 缺少必填参数 |
| 403 | 仅支持 JWT 认证 | 使用了非 JWT 认证 |
| 403 | Permission denied | 非项目所有者且非团队成员 |
| 404 | Project not found | 项目不存在 |
| 404 | Node not found in filesFolder | 节点在文件树中不存在 |
| 500 | Failed to copy documents | 文档复制失败 |
| 500 | Failed to update project config | 项目配置更新失败 |
| 500 | Internal server error | 内部错误 |