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 实测通过。
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 条路由)逐行核对
二、前置条件
- 已部署并完成初始化的 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
-
客户端工具:本文统一用
curl(无需任何 SDK);响应为 JSON 的接口可配合python3 -m json.tool美化。浏览器端可访问实例自带的 Swagger 页http://<服务器IP>:8132/api/,但注意 2.0.2 的 Swagger 配置未指向本服务接口(默认指向 petstore 演示),实际接口探索以本文端点总表与源码为准。 -
一个可用的管理员账号(本文全程用
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/release | read | 当前与最新版本号 |
| 认证 | POST /api/initialize | 无(仅未初始化时) | 首次初始化建管理员 |
| 认证 | POST /api/authenticate | 无 | 账号口令换令牌 |
| 认证 | HEAD /api/token | Bearer | 校验令牌有效性(204/401) |
| 认证 | GET /api/guest | 无(仅 public 模式) | 游客只读令牌 |
| 认证 | GET /api/local | 无(仅 local 模式) | 桌面端本地令牌 |
| 资料 | PATCH /api/profile | Bearer | 修改本人姓名/口令 |
| 设置 | GET /api/settings / PUT /api/settings | read / 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=×tamp=、DELETE /api/version?path=×tamp= | read / delete | 读/删历史版本 |
| 搜索 | GET /api/search?query= | read | 全文搜索 |
| 置顶 | GET/POST/PATCH/DELETE /api/pinned | read / 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/sync | sync | 桌面端与远端实例同步 |
💡 说明: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×tamp=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=…×tamp=…(需 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。
十一、参考链接
- 官方仓库与 API 源码(backend/src/app.controller.ts):https://github.com/Zavy86/WikiDocs
- 架构与契约说明(ARCHITECTURE.md):https://github.com/Zavy86/WikiDocs/blob/main/ARCHITECTURE.md
- 实例自带 Swagger 页(2.0.2 配置未完成,仅作入口):
http://<服务器IP>:8132/api/ - 本系列文档:《WikiDocs 介绍与选型指南》、《WikiDocs 2.0 Docker Compose 部署指南》、《WikiDocs 2.0 配置参数详解》
- 同类组件使用实测:《Otter Wiki 2.26.1 常用命令与页面语法详解》、《WiKiss 0.3 核心使用与鉴权配置详解》