WikiDocs 2.0 API 使用详解

目标:系统讲清 WikiDocs(Wiki|Docs)2.0.2 内置 REST API 的认证体系、19 组端点的请求参数与实测响应,覆盖认证令牌、文档增删改查与版本、搜索、置顶、附件、回收站、账号与个人资料全域,并沉淀参数缺失 500、附件 token 机制、移动语义等实测排障结论。已在 Ubuntu 26.04.1 LTS + WikiDocs 2.0.2 环境逐条 curl 实测通过。

WikiDocsWikiDocs知识库自托管2026/10/30

WikiDocs 2.0 API 使用详解

目标:系统讲清 WikiDocs(Wiki|Docs)2.0.2 内置 REST API 的认证体系、19 组端点的请求参数与实测响应,覆盖认证令牌、文档增删改查与版本、搜索、置顶、附件、回收站、账号与个人资料全域,并沉淀参数缺失 500、附件 token 机制、移动语义等实测排障结论。已在 Ubuntu 26.04.1 LTS + WikiDocs 2.0.2 环境逐条 curl 实测通过。

一、环境信息

本文所有请求与响应均在以下真实环境逐条实测,读者在相近环境可按原文复现。

  • 操作系统:Ubuntu 26.04.1 LTS(内核 7.0.0-38-generic)
  • 应用版本:WikiDocs 2.0.2(官方镜像 zavy86/wikidocs:2,后端 NestJS + Node.js v22.23.4)
  • 部署形态:按 《WikiDocs 2.0 Docker Compose 部署指南》完成的单实例部署,MODE=public
  • 访问地址:http://127.0.0.1:8132(宿主 8132 → 服务内 3210),API 前缀 /api
  • 演示账号:管理员 admin@lab.local / q1w2e3r4
  • 接口清单取证口径:运行实例逐条 curl 实测 + 2.0.2 官方源码 backend/src/app.controller.ts(34 条路由)逐行核对

二、前置条件

  1. 已部署并完成初始化的 WikiDocs 2.0.2 实例(部署步骤见 《WikiDocs 2.0 Docker Compose 部署指南》第四章),健康探针通过:
curl -s -o /dev/null -w 'GET /api/health -> %{http_code}\n' http://127.0.0.1:8132/api/health
GET /api/health -> 204
  1. 客户端工具:本文统一用 curl(无需任何 SDK);响应为 JSON 的接口可配合 python3 -m json.tool 美化。浏览器端可访问实例自带的 Swagger 页 http://<服务器IP>:8132/api/,但注意 2.0.2 的 Swagger 配置未指向本服务接口(默认指向 petstore 演示),实际接口探索以本文端点总表与源码为准。

  2. 一个可用的管理员账号(本文全程用 admin@lab.local / q1w2e3r4),用于获取读写令牌。

三、API 总览

WikiDocs 2.0 的 API 是伴随 2.x TypeScript 重写(NestJS 后端)新增的能力,1.x PHP 线没有 REST API。设计要点:

  • 协议与风格:HTTP/1.1 + JSON,路径参数走 query string(?path=…),鉴权走 Authorization: Bearer <JWT> 头;
  • 认证方式:JWT 令牌,管理员/作者令牌有效期 900 秒(15 分钟),游客令牌 86400 秒(24 小时);无刷新机制,过期重新调用认证接口即可;
  • 授权模型:令牌 payload 携带 authorizations 数组(read/write/delete/sync/manage 五种),每个端点声明所需授权,不足返回 401/403;
  • 无分页:全部列表类接口(树、置顶、账号、回收站、搜索)一次全量返回,无 page/size 参数;过滤主要靠 path/query 参数;
  • 写入即落盘:所有写操作直接作用于数据集文件(无数据库、无事务),204 表示已写盘成功。

3.1 端点总表(19 组 34 条路由,与源码逐一核对)

域方法与端点所需授权用途
系统GET /api/health无健康检查(204)
系统GET /api/information无实例信息(版本/模式/运行时)
系统GET /api/releaseread当前与最新版本号
认证POST /api/initialize无(仅未初始化时)首次初始化建管理员
认证POST /api/authenticate无账号口令换令牌
认证HEAD /api/tokenBearer校验令牌有效性(204/401)
认证GET /api/guest无(仅 public 模式)游客只读令牌
认证GET /api/local无(仅 local 模式)桌面端本地令牌
资料PATCH /api/profileBearer修改本人姓名/口令
设置GET /api/settings / PUT /api/settingsread / manage站点设置读写
账号GET /api/accounts、POST /api/accounts、DELETE /api/accounts?account=manage账号列表/增改/删除
文档GET /api/tree?path=read目录树
文档GET /api/document?path=read读单页(含附件 token 与版本清单)
文档POST /api/document?path=write新建/更新正文
文档PATCH /api/document?path=&destination=write移动文档
文档DELETE /api/document?path=delete移入回收站
版本GET /api/version?path=&timestamp=、DELETE /api/version?path=&timestamp=read / delete读/删历史版本
搜索GET /api/search?query=read全文搜索
置顶GET/POST/PATCH/DELETE /api/pinnedread / write置顶清单与排序
附件POST /api/attachment?path=&file=、GET /api/attachment?path=&file=&token=、DELETE /api/attachment?path=&file=write / token / delete上传/下载/删除附件
回收站GET /api/trash、PATCH /api/trash?path=&destination=、DELETE /api/trash?path=delete列表/恢复/彻底删除
同步GET/POST/PUT /api/syncsync桌面端与远端实例同步

💡 说明:settings 域的 9 字段与校验规则属于配置专题,完整字段级讲解见 《WikiDocs 2.0 配置参数详解》第五、七章,本文 8.1 只给接口调用形态。

四、认证与首次调用

本节从零拿到一枚可用令牌并看清它的授权结构——这是调用一切受保护端点的前提。

4.1 实例信息与版本(公开端点)

curl -s http://127.0.0.1:8132/api/information
{"mode":"public","initialized":true,"service":"wikidocs-backend","version":"2.0.2","host":"10.89.58.2","platform":"linux","engine":"v22.23.4","pid":1,"uptime":383,"memory":38514688}
curl -s http://127.0.0.1:8132/api/release -H "Authorization: Bearer $JWT"
{"current":"2.0.2","latest":"2.0.2"}
HTTP 200

information 可用于部署后探活与版本核对;release 返回当前运行版本与上游最新版(实例能出外网时 latest 才是实时值,离线时该字段来自启动时的失败缓存,见部署指南 7.5)。

4.2 账号认证换取令牌

curl -s -X POST http://127.0.0.1:8132/api/authenticate \
  -H 'Content-Type: application/json' \
  -d '{"account":"admin@lab.local","password":"q1w2e3r4"}'

输出节选(实测首尾):

{"jwt":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkdXJhdGlvbiI6OTAwLCJnZW5lcmF0aW9uIjoiMjAyNi0xMC0zMFQwNzoyNToxMC40NzNaIiwiZXhwaXJhdGlvbiI6IjIwMjYtMTAtMzBUMDc6NDA6MTAuNDczWiIsImFjY291bnQiOiJhZG1pbkBsYWIubG9jYWwiLCJyb2xlIjoiYWRtaW5pc3RyYXRvciIsImF1dGhvcml6YXRpb25zIjpbInJlYWQiLCJ3cml0ZSIsImRlbGV0ZSIsInN5bmMiLCJtYW5hZ2UiXSwiaWF0IjoxNzkzMzQ1MTEwfQ.k3y…"}

把 payload 段 base64url 解码,看清授权结构(后续所有端点的授权判断依据):

JWT=$(curl -s -X POST http://127.0.0.1:8132/api/authenticate \
  -H 'Content-Type: application/json' \
  -d '{"account":"admin@lab.local","password":"q1w2e3r4"}' \
  | sed 's/.*"jwt":"\([^"]*\)".*/\1/')
echo "$JWT" | python3 -c "
import sys,json,base64
t=sys.stdin.read().strip()
p=t.split('.')[1]; p+='='*(-len(p)%4)
print(json.dumps(json.loads(base64.urlsafe_b64decode(p)),ensure_ascii=False,indent=1))"
{
 "duration": 900,
 "generation": "2026-10-30T07:25:10.473Z",
 "expiration": "2026-10-30T07:40:10.473Z",
 "account": "admin@lab.local",
 "firstname": "Admin",
 "lastname": "User",
 "role": "administrator",
 "authorizations": [
  "read",
  "write",
  "delete",
  "sync",
  "manage"
 ],
 "iat": 1793345110
}

管理员拿全量 5 项授权;普通用户角色的令牌实测只拿 read(见 8.3 账号域实测)。口令错误时返回 401:

{"message":"Authentication failed","error":"Unauthorized","statusCode":401}

4.3 令牌有效性校验(HEAD /api/token)

curl -s -o /dev/null -w '有效令牌 -> HTTP %{http_code}\n' -I http://127.0.0.1:8132/api/token -H "Authorization: Bearer $JWT"
curl -s -o /dev/null -w '无效令牌 -> HTTP %{http_code}\n' -I http://127.0.0.1:8132/api/token -H 'Authorization: Bearer invalid.token.here'
有效令牌 -> HTTP 204
无效令牌 -> HTTP 401

该端点是脚本里「先验令牌再干活」的轻量探针。

4.4 游客令牌与本地令牌(模式限定端点)

public 模式下游客可匿名领只读令牌(24 小时):

curl -s http://127.0.0.1:8132/api/guest | head -c 90; echo
{"jwt":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkdXJhdGlvbiI6ODY0MDAsImdlbmVyYXRpb24iOiIyMDI2LTEwLTMwVDA2OjU1OjMyLjMxMloiLCJhY2NvdW50IjoiZ3Vlc3Qi…

private 模式下同一端点返回 401(实测回显):{"message":"Guest access is enabled only in public mode","error":"Unauthorized","statusCode":401}。GET /api/local 同理只对 MODE=local 的桌面形态开放,public 实例实测:

curl -s -w '\nHTTP %{http_code}\n' http://127.0.0.1:8132/api/local
{"message":"Local access is enabled only in local mode","error":"Unauthorized","statusCode":401}
HTTP 401

五、文档域 API

文档域是 API 的主体:目录树、单页读写、移动、删除与版本、全文搜索。文档路径必须满足清洗规则(小写、仅 a-z0-9_.-/、禁 ..),详见 《WikiDocs 2.0 配置参数详解》5.9。

5.1 GET /api/tree:目录树

curl -s -w '\nHTTP %{http_code}\n' 'http://127.0.0.1:8132/api/tree?path=' -H "Authorization: Bearer $JWT"
{"metadata":{"path":"/","title":"Wiki|Docs","author":"Admin User <admin@lab.local>","timestamp":"2026-10-30T06:50:44.253Z","tags":["wiki","markdown","databaseless"]},"leaves":[{"path":"/guides","title":"guides","author":"","timestamp":"","tags":[]}]}
HTTP 200
  • path= 传根路径返回根节点与其直接子级;传子路径(如 path=guides)下钻一层;
  • ⚠️ 注意:path 参数必传(根也要传空串 path=),缺省直接 500(见 10.1)。

5.2 GET /api/document:读单页

curl -s 'http://127.0.0.1:8132/api/document?path=guides/quick-start' -H "Authorization: Bearer $JWT" | python3 -m json.tool

输出节选(实测,content.raw 截断展示):

{
  "exists": true,
  "pinned": true,
  "metadata": {
    "path": "/guides/quick-start",
    "title": "Quick Start",
    "author": "Admin User <admin@lab.local>",
    "timestamp": "2026-10-30T06:53:57.435Z",
    "tags": ["guide"]
  },
  "children": [{"path": "/guides/quick-start/quick-start", "title": "Quick Start", "author": "Admin User <admin@lab.local>", "timestamp": "2026-10-30T06:52:58.715Z", "tags": ["guide"]}],
  "attachments": [{"path": "/guides/quick-start", "file": "note.txt", "token": "Z3VpZGVzL3F1aWNrLXN0YXJ0fG5vdGUudHh0fHRucmdtaw.6tmJeTSGA_Ryqvk40CVhuA"}],
  "versions": [],
  "content": {"raw": "---\ntitle: Quick Start\ntimestamp: '2026-10-30T06:53:57.435Z'\nauthor: Admin User …"}
}

逐字段说明:

  • metadata:页面元数据(frontmatter 解析结果),timestamp 为最后修改时间;
  • children:子页面列表(wiki 树形结构);
  • attachments[].token:附件下载专用一次性凭证,与登录 JWT 无关(详见 6.2);
  • versions:历史版本时间戳字符串数组(毫秒值),配合 5.5 的版本读取端点使用;
  • content.raw:正文整包(含 YAML frontmatter),即 POST 更新时回传的载荷。

5.3 POST /api/document:新建与更新

请求体为 {"raw": "<含 frontmatter 的整包正文>", "versioning": true|false};path 走 query string。新建一页:

curl -s -w 'HTTP %{http_code}\n' -X POST 'http://127.0.0.1:8132/api/document?path=api-demo' \
  -H "Authorization: Bearer $JWT" -H 'Content-Type: application/json' \
  -d '{"raw":"---\ntitle: API 实测示例页\nauthor: Admin\n---\nHello from **REST ==API==** test page.","versioning":true}'
HTTP 204

对同一 path 再次 POST 即更新;versioning: true 时旧内容存入 _versions/<毫秒时间戳>.md(实测更新后 versions 字段变为 ["1793345132092"])。两个高频 400:

  • 请求体误加 path/title/content 等不属于 schema 的字段 → 400 property path should not exist(body 白名单校验);
  • raw 缺失 → 400 校验报错。

5.4 PATCH /api/document:移动文档

⚠️ destination 是「目标父目录」语义:文档移动到 destination/<原路径末段>,不是重命名为 destination 本身。实测把根目录 movetest 移入 guides:

curl -s -w 'HTTP %{http_code}\n' -X PATCH 'http://127.0.0.1:8132/api/document?path=movetest&destination=guides' -H "Authorization: Bearer $JWT"
HTTP 204

落盘核对(文档出现在 guides/movetest/content.md,原位置消失):

/opt/wikidocs/datasets/documents/guides/movetest/content.md

一个隐蔽的 400:目标路径以源路径为前缀时被拒绝(前缀包含判定):

curl -s -w '\nHTTP %{http_code}\n' -X PATCH 'http://127.0.0.1:8132/api/document?path=api-demo&destination=api-demo-v2' -H "Authorization: Bearer $JWT"
{"message":"Destination inside the source","error":"Bad Request","statusCode":400}
HTTP 400

api-demo-v2 只是名字相似并不在 api-demo 之内,但仍被判定 inside source——重命名类操作需换一个完全不同前缀的目标名,或移动到别的父目录再改名。

5.5 GET/DELETE /api/version:历史版本

用 versions 数组里的时间戳读指定版本:

curl -s -w '\nHTTP %{http_code}\n' 'http://127.0.0.1:8132/api/version?path=api-demo&timestamp=1793345132092' -H "Authorization: Bearer $JWT"
{"raw":"---\ntitle: API 实测示例页\nauthor: Admin\ntags: []\ntimestamp: '2026-10-30T07:25:32.080Z'\n---\nHello from **REST ==API==** test page.\n"}
HTTP 200

timestamp 缺省或不存在时的报错(实测):

{"message":"Version <> not found","error":"Not Found","statusCode":404}

DELETE /api/version?path=…&timestamp=…(需 delete 授权)删除指定快照,204。

5.6 GET /api/search:全文搜索

curl -s 'http://127.0.0.1:8132/api/search?query=REST' -H "Authorization: Bearer $JWT"
{"results":[{"metadata":{"path":"/guides/quick-start","title":"Quick Start","author":"Admin User <admin@lab.local>","timestamp":"2026-10-30T06:53:57.435Z","tags":["guide"]},"highlights":["Hello from **==REST== API**."]}]}

命中词用 ==词== 包裹(客户端渲染为高亮);results 全量返回、无分页。query 参数必传,缺省 500(同 10.1 的缺参问题)。

5.7 DELETE /api/document:移入回收站

curl -s -w 'HTTP %{http_code}\n' -X DELETE 'http://127.0.0.1:8132/api/document?path=api-demo' -H "Authorization: Bearer $JWT"
HTTP 204

删除是软删除:页面目录整体挪进 trash/ 并加毫秒时间戳前缀(1793345164787_api-demo),可恢复(第七章)。

六、置顶与附件 API

6.1 置顶:GET/POST/PATCH/DELETE /api/pinned

置顶一个页面(需 write 授权,path 走 query):

curl -s -w 'HTTP %{http_code}\n' -X POST 'http://127.0.0.1:8132/api/pinned?path=api-demo' -H "Authorization: Bearer $JWT"
curl -s 'http://127.0.0.1:8132/api/pinned' -H "Authorization: Bearer $JWT"
HTTP 204
{"documents":[{"path":"/guides/quick-start","title":"Quick Start","author":"Admin User <admin@lab.local>","timestamp":"2026-10-30T06:53:57.435Z","tags":["guide"]},{"path":"/api-demo","title":"API 实测示例页","author":"Admin User <admin@lab.local>","timestamp":"2026-10-30T07:27:39.121Z","tags":[]}]}
  • PATCH /api/pinned?path=…&sorting=0:调整排序(sorting 为目标下标),204;
  • DELETE /api/pinned?path=…:取消置顶,204;
  • 对未置顶/不存在的页面操作会报错(实测):不存在 → 400 {"message":"Document <guides/api-demo> not found",…};未置顶就调序/取消 → 404 {"message":"Document <…> is not pinned",…}。

6.2 附件:POST/GET/DELETE /api/attachment 与专用 token 机制

上传(multipart 表单,字段名必须是 file;path/file 走 query):

printf 'attachment body from REST test' > /tmp/rest-note.txt
curl -s -w 'HTTP %{http_code}\n' -X POST 'http://127.0.0.1:8132/api/attachment?path=api-demo&file=rest-note.txt' \
  -H "Authorization: Bearer $JWT" -F 'file=@/tmp/rest-note.txt'
HTTP 204

下载不用登录 JWT,而用 GET /api/document 响应里 attachments[].token 的一次性凭证:

TOKEN=$(curl -s 'http://127.0.0.1:8132/api/document?path=api-demo' -H "Authorization: Bearer $JWT" \
  | python3 -c "import sys,json; print(json.load(sys.stdin)['attachments'][0]['token'])")
echo "$TOKEN"
curl -s -w '\nHTTP %{http_code}\n' "http://127.0.0.1:8132/api/attachment?path=api-demo&file=rest-note.txt&token=$TOKEN"
YXBpLWRlbW98cmVzdC1ub3RlLnR4dHx0bnJncTM.fCmXVec4GZ6gng9GdEH7iQ
attachment body from REST test
HTTP 200

拿 JWT 冒充 token 与缺 token 的实测报错:

{"message":"Invalid attachment token","error":"Unauthorized","statusCode":401}
{"message":"Missing attachment token","error":"Unauthorized","statusCode":401}

DELETE /api/attachment?path=…&file=…(delete 授权)删附件,实测 204。上传时 query 缺 file 参数会 500(缺参防护不足,见 10.1)。

七、回收站 API

回收站三件套对应「列表 → 恢复 → 彻底删除」。查看回收站(条目带毫秒前缀):

curl -s 'http://127.0.0.1:8132/api/trash' -H "Authorization: Bearer $JWT"
{"documents":[{"path":"/1793345164787_api-demo","title":"1793345164787_api-demo","author":"","timestamp":"","tags":[]}]}

恢复到指定父目录(destination 为父目录,页面以原名放回其下):

curl -s -w 'HTTP %{http_code}\n' -X PATCH 'http://127.0.0.1:8132/api/trash?path=1793345164787_api-demo&destination=guides' -H "Authorization: Bearer $JWT"
HTTP 204

恢复后页面位于 guides/api-demo。⚠️ destination 必传,缺省直接 500(trim of undefined,见 10.1)。彻底删除(不可恢复):

curl -s -w 'HTTP %{http_code}\n' -X DELETE 'http://127.0.0.1:8132/api/trash?path=1793345164787_api-demo' -H "Authorization: Bearer $JWT"
HTTP 204

八、设置、账号与个人资料 API

8.1 设置:GET/PUT /api/settings

读取匿名可访问;写入需 manage 授权且为全量提交(9 字段整体覆盖,字段约束与校验回显详见 《WikiDocs 2.0 配置参数详解》第三、七章):

curl -s -w 'HTTP %{http_code}\n' -X PUT http://127.0.0.1:8132/api/settings \
  -H "Authorization: Bearer $JWT" -H 'Content-Type: application/json' \
  -d '{"title":"Lab Wiki","subtitle":"实验知识库","owner":"Admin User","notice":"Copyright © 2026 Admin","privacy":null,"localization":"zh","timezone":"Asia/Shanghai","template":"light","color":"#1e88e5"}'
HTTP 204

未带令牌写入的实测回显:{"message":"You must provide an authentication token..","error":"Unauthorized","statusCode":401}。

8.2 账号:GET/POST/DELETE /api/accounts

新增账号(body 为 accounts 数组,可一次批量;role 仅 administrator/author/user 三枚举;已有同名账号则转为更新):

curl -s -w 'HTTP %{http_code}\n' -X POST http://127.0.0.1:8132/api/accounts \
  -H "Authorization: Bearer $JWT" -H 'Content-Type: application/json' \
  -d '{"accounts":[{"account":"user@lab.local","firstname":"De","lastname":"Mo","role":"user","password":"q1w2e3r4"}]}'
HTTP 204

列表接口自动剥掉口令摘要字段;删除按 account 定位:

curl -s http://127.0.0.1:8132/api/accounts -H "Authorization: Bearer $JWT"
curl -s -w 'HTTP %{http_code}\n' -X DELETE 'http://127.0.0.1:8132/api/accounts?account=user@lab.local' -H "Authorization: Bearer $JWT"
{"accounts":[{"account":"admin@lab.local","firstname":"Admin","lastname":"User","role":"administrator"},{"account":"author@lab.local","firstname":"Au","lastname":"Thor","role":"author"}]}
HTTP 204

8.3 角色与授权的实测映射

用新建的普通用户登录并解码令牌,得到角色→授权的实证:

curl -s -X POST http://127.0.0.1:8132/api/authenticate \
  -H 'Content-Type: application/json' \
  -d '{"account":"user@lab.local","password":"q1w2e3r4"}' \
  | python3 -c "import sys,json,base64; t=json.load(sys.stdin)['jwt']; p=t.split('.')[1]; p+='='*(-len(p)%4); d=json.loads(base64.urlsafe_b64decode(p)); print('role:',d['role'],'authorizations:',d['authorizations'])"
role: user authorizations: ['read']

即 user 角色只有 read,写入/删除/管理端点对它一律 401/403;administrator 为全量 5 项(4.2 实测)。账号域端点本身需 manage 授权,普通用户调用返回 403/401。

8.4 个人资料:PATCH /api/profile

每个登录者改自己的姓名与口令(password 可选,缺省表示不改):

curl -s -w 'HTTP %{http_code}\n' -X PATCH http://127.0.0.1:8132/api/profile \
  -H "Authorization: Bearer $JWT" -H 'Content-Type: application/json' \
  -d '{"firstname":"Admin","lastname":"Ops"}'
HTTP 204

实测把 lastname 改为 Ops 后再改回 User,两次均 204;firstname/lastname 非空且 ≤32 字符,password ≤256 字符。

8.5 同步:GET/POST/PUT /api/sync(桌面端专用)

sync 域面向官方桌面客户端(Electron)与远端实例的数据同步,需 sync 授权(管理员令牌具备)。浏览器/脚本集成场景不需要它;本文实例为服务器形态,未对 sync 做写入实测,字段细节以源码 app.controller.ts 的 sync 处理器为准。

九、通用约定与注意事项

  • 错误码约定:校验失败 400(message 为数组,逐条原因);未带令牌/令牌失效 401;授权不足 401 或 403;资源不存在 404;参数缺失导致的空指针 500(2.0.2 已知防护缺口,见 10.1);成功写操作一律 204 无响应体,读操作 200 带 JSON。
  • 路径参数清洗:所有 path/destination/file 参数先过 sanitize 规则(小写、非法字符转 -、禁 .. 与 _versions),中文路径会被转成连字符串——建页建议直接用英文小写路径。
  • 排序:仅置顶支持显式排序(PATCH /api/pinned?sorting=N);树/搜索/回收站按路径与时间自然排序,无排序参数。
  • 过滤:列表接口靠 path 前缀下钻过滤,全文检索靠 search?query=;无标签过滤端点(标签数据在 metadata.tags 中随响应返回,可客户端自行过滤)。
  • 时间戳口径:版本快照与回收站前缀为毫秒时间戳;metadata.timestamp 为 ISO 8601 字符串,时区展示受 settings 的 timezone 影响。

十、常见问题(FAQ)

10.1 缺 query 参数直接 500

现象:GET /api/tree(不带 path)、GET /api/search(不带 query)、PATCH /api/trash(不带 destination)、POST /api/attachment(不带 file)等返回 500,响应体是通用错误(实测):

{"statusCode":500,"message":"Internal server error"}
HTTP 500

定位:真实原因在服务日志里(实测日志行):

ERROR [ExceptionsHandler] TypeError: Cannot read properties of undefined (reading 'trim')

2.0.2 对部分 query 参数未做 required 强校验,undefined 进入 sanitizeDocumentPath() 的 .trim() 抛 TypeError。

规避:调用前核对本文端点总表的必传参数——根目录也要显式传 path=(空值);destination、file、timestamp 等一个都不能少。这是版本已知行为,升级时留意 Release Notes 是否修复。

10.2 附件下载一直 401

现象:带着有效登录 JWT 下载附件仍 401 Invalid attachment token。

定位:附件下载走独立的 token 体系(GET /api/document 响应中 attachments[].token,格式为 base64url(path|file|随机数).签名),与登录 JWT 不互通——这是设计而非 bug。

修复:先 GET /api/document?path=<页面> 取 attachments[].token,再用该 token 拼附件下载 URL(6.2 实测流程)。token 过期或文件变更后需重新获取。

10.3 移动文档报 Destination inside the source

现象:PATCH /api/document?path=api-demo&destination=api-demo-v2 返回 400 Destination inside the source。

定位与修复:目标是源路径的字符串前缀即被拒(前缀包含判定);且 destination 语义是目标父目录(文档落到 destination/<原名>)。正确姿势:想把 api-demo 改名为 api-demo-v2,先移动到其他父目录(如 destination=tmp)再用 POST 更新原文重建,或干脆规划好路径一步到位。

10.4 写接口返回 401/403 的区分

现象:同样是无响应体报错,如何区分「没带令牌」「令牌过期」「授权不足」?

结论(实测归纳):请求头无令牌 → 401 You must provide an authentication token..;令牌签名校验失败/过期 → 401(authenticate 场景为 Authentication failed);令牌有效但缺端点所需授权(如 user 角色调写端点)→ 401/403。先用 HEAD /api/token 验令牌有效性,再解码 payload 核对 authorizations 是否覆盖端点要求(3.1 总表)。

10.5 初始化接口重复调用报 400

现象:对已初始化实例 POST /api/initialize 返回 400。

结论:初始化端点仅在 information.initialized=false 时可用,成功一次后永久关闭(重复调用 400);邮箱格式不合法同样 400(account must be an email)。运维场景用不到它——账号调整走 POST /api/accounts,站点信息走 PUT /api/settings。

十一、参考链接