Wiki-Go 1.9.2 API 使用详解
Wiki-Go 1.9.2 REST API 全端点逐条实测:认证/文档/版本回滚/评论/附件/搜索/设置/用户/访问规则/备份导出/导入/工具 12 个域的请求与真实回显,含 CLI 面核验与秒级时间戳坑
Wiki-Go 1.9.2 API 使用详解
目标:给出 Wiki-Go 1.9.2 管理/数据 REST API 的逐域实测手册——每个端点的请求形态、真实回显、权限层级与已踩过的坑。本文接口全部在 v1.9.2 实例逐条 curl 实测(2026-11-02),路由权限分层与源码
internal/routes/routes.go实查交叉验证;无编造响应。
一、环境信息
- 操作系统:Ubuntu 22.04 LTS(x86_64),Wiki-Go v1.9.2(官方 release 二进制部署,监听 8080;Compose 形态同 API 面)
- Base URL:
http://127.0.0.1:8080(明文 HTTP 实测;HTTPS/反代见部署指南) - 账号:
admin/q1w2e3r4(演示口令);测试中另建editor/viewer角色用户核验权限 - 客户端工具:
curl 8.x(cookie jar 持会话),全部示例可独立复制执行 - 明文 HTTP 前置:
allow_insecure_cookies: true(否则 Cookie 不回传,除 login 外全部 401,见配置详解 5.1)
二、前置条件
2.1 起一个可测实例
未完成部署先按 《Wiki-Go 1.9.2 Docker Compose 与二进制部署指南》;最小可用启动:
cd /opt/wiki-go && ./wiki-go
2.2 登录取会话(所有实测的起点)
curl -s -c jar -X POST http://127.0.0.1:8080/api/login \
-H 'Content-Type: application/json' \
-d '{"username":"admin","password":"q1w2e3r4","keepLoggedIn":false}'
{"message":"Login successful","success":true}
后续命令统一 -b jar 携带会话。错密码对照(实测 401):
{"message":"Invalid credentials","success":false}
三、API 总览与 CLI 面核验
3.1 CLI 面核验(为何本篇是 API 详解而非命令详解)
Wiki-Go 是 Go 单二进制,先核验其 CLI 管理面是否足以成篇。实测 ./wiki-go -h 全量输出:
Usage of ./wiki-go:
-configfile string
where to find config.yaml (default "data/config.yaml")
--version 传入直接报 flag provided but not defined: -version;任意子命令(如 backup)被当作无效参数忽略后照常起服务。结论:v1.9.2 的 CLI 面只有「启动 + 配置定位」一件事,无管理子命令族,全部管理/数据能力都在 REST API——故本专栏运维面按 D5(API 使用详解)成篇。
3.2 协议与版本
- 协议:HTTP/1.1 REST,JSON 请求/响应为主;
save接口请求体是text/markdown原文,import是multipart/form-data,附件下载/备份下载返回二进制; - 无版本号前缀(无 v1/v2 路径),无文档化限流(仅登录防爆破 429,见 3.4)。
3.3 认证方式
会话 Cookie 双件套 session_user + session_token;keepLoggedIn 控制有效期(实测 true ≈ 1 年、false ≈ 21 天)。无 API Token 机制(实测未见任何 token 端点/头),服务间集成需维持登录会话。
3.4 权限分层总表(源码路由实查 + 逐条实测复核)
| 层级 | 端点 | 实测证据 |
|---|---|---|
| 公开 | /api/login /api/render-markdown /api/data/emojis /api/utils/slugify /sitemap.xml /api/check-default-password /static/* | 未登录 200 |
| 登录即可(任意角色) | /api/check-auth /api/logout /api/password /api/document/<path> /api/documents/list /api/search /api/folders | viewer 读渲染页 200 |
| editor 或 admin | /api/source/* /api/save/* /api/versions/* /api/document/move /api/document/create /api/comments/* /api/files/* /api/links/fetch-metadata | viewer 调 source/save 实测 401 |
| 仅 admin | /api/settings/* /api/users /api/access-rules* /api/backup/* /api/import* | editor/viewer 实测 403 Admin access required |
注:
/api/source/虽在路由表中不挂 editor 中间件,但实测 viewer 会话读取返回{"message":"Unauthorized. Admin or editor access required.","success":false}(401)——源文读取按 editor+ 对待。
3.5 统一响应约定
绝大多数端点返回 {"success": true|false, "message": ...} 信封;失败时 HTTP 码同步语义化(400/401/403/404/405/429)。方法不对一律 405(实测 GET /api/search → Method not allowed)。
四、认证域
| 端点 | 方法 | 说明 |
|---|---|---|
/api/login | POST | 见 2.2 |
/api/check-auth | GET | 回显当前会话身份 |
/api/logout | POST | 注销 |
/api/password | POST | 改当前用户密码 |
/api/check-default-password | GET | 是否仍在用默认 admin/admin |
curl -s -b jar http://127.0.0.1:8080/api/check-auth
curl -s -b jar -X POST http://127.0.0.1:8080/api/password -H 'Content-Type: application/json' -d '{"current_password":"q1w2e3r4","new_password":"q1w2e3r4"}'
curl -s -b jar http://127.0.0.1:8080/api/check-default-password
curl -s -b jar -X POST http://127.0.0.1:8080/api/logout
curl -s -b jar http://127.0.0.1:8080/api/check-auth
实测回显:
{"groups":null,"role":"admin","success":true,"username":"admin"}
{"logged_out":false,"message":"User password updated successfully","redirect":"/login","success":true}
{"defaultPasswordInUse":false}
{"message":"Logout successful","success":true}
{"message":"Unauthorized","success":false}
⚠️ 注意 password 的行为分叉:改他人可见语义的同值密码返回 logged_out:false,而部署期把默认 admin 改成新密码时实测 logged_out:true 并附 redirect:/login(旧会话强制失效)。生产脚本请两者都判。
登录防爆破:180 秒内 5 次失败触发封禁,第 6 次起(含正确密码)一律:
{"message":"Too many failed logins; try again later","retryAfter":60,"success":false}
HTTP 429(实测),封禁指数增长至最长 24 小时(security 参数见 《Wiki-Go 1.9.2 配置参数详解》)。
五、文档域
5.1 创建
curl -s -b jar -X POST http://127.0.0.1:8080/api/document/create -H 'Content-Type: application/json' -d '{"title":"API Test Doc","path":"demo/api-test-doc"}'
{"message":"Document created successfully","success":true,"url":"/demo/api-test-doc"}
初始正文是本地化模板(zh-CN 环境实测为「在此处输入内容。」)。
5.2 保存(正文写入)
printf '# API Test Doc\n\n第一版正文 :smile: 与 $$E=mc^2$$。\n' | \
curl -s -b jar -X POST http://127.0.0.1:8080/api/save/demo/api-test-doc -H 'Content-Type: text/markdown' --data-binary @-
{"message":"Document saved successfully","success":true}
请求体是 Markdown 原文(Content-Type: text/markdown),不是 JSON 包装。
5.3 读取源文与渲染页
curl -s -b jar http://127.0.0.1:8080/api/source/demo/api-test-doc
# API Test Doc
第一版正文 :smile: 与 $$E=mc^2$$。
渲染 HTML 请走页面 GET /demo/api-test-doc(200 实测);专用 JSON 端点未实现:
curl -s -b jar http://127.0.0.1:8080/api/document/demo/api-test-doc
{"message":"Document retrieval not yet implemented","path":"/demo/api-test-doc","success":true}
5.4 列表
curl -s -b jar http://127.0.0.1:8080/api/documents/list
{"success":true,"documents":[{"title":"API Test Doc","path":"/demo/api-test-doc"}]}
无文档时实测 documents: null(消费方按 null/[] 双兼容写)。
5.5 重命名与移动
curl -s -b jar -X POST http://127.0.0.1:8080/api/document/move -H 'Content-Type: application/json' -d '{"sourcePath":"demo/api-test-doc","targetPath":"","newSlug":"api-test-doc-renamed"}'
{"success":true,"message":"Document moved successfully","newPath":"demo/api-test-doc-renamed","oldPath":"demo/api-test-doc"}
targetPath 为空 = 原位改名;非空 = 移动到目标目录(官方 REST 示例两种形态)。
5.6 删除
curl -s -b jar -X DELETE http://127.0.0.1:8080/api/document/demo/api-test-doc-renamed
{"message":"Document deleted successfully","success":true}
六、版本域(历史与回滚,含关键语义坑)
6.1 快照语义(源码 + 磁盘实测双重确认)
每次 save 前,Wiki-Go 把被替换掉的旧内容写成 versions/documents/<路径>/<本次保存时间戳>.md。磁盘实测(文档连存两版):
find data -path '*t10*' -name '*.md' | sort
data/documents/demo/t10-rollback/document.md
data/versions/documents/demo/t10-rollback/20261102090339.md # 内容=建页模板(第一次保存替换掉的)
data/versions/documents/demo/t10-rollback/20261102090340.md
即:版本文件的内容是「该次保存之前的状态」,列表里最新 ts 对应的是当前正文的前一态。
6.2 版本列表与单版本取回
curl -s -b jar http://127.0.0.1:8080/api/versions/demo/t17-doc
curl -s -b jar http://127.0.0.1:8080/api/versions/demo/t17-doc/20261102091310
{"success":true,"versions":[{"timestamp":"20261102091310","path":"demo/t17-doc/20261102091310"},{"timestamp":"20261102090636","path":"demo/t17-doc/20261102090636"}]}
{"success":true,"content":"# T17 Doc\n\n在此处输入内容。"}
6.3 回滚(restore)实测闭环
间隔 3 秒连存 VERSION-THREE / VERSION-FOUR 后,取含 VERSION-THREE 的快照 ts 回滚:
curl -s -b jar -X POST http://127.0.0.1:8080/api/versions/demo/t10-rollback/20261102090636/restore
curl -s -b jar http://127.0.0.1:8080/api/source/demo/t10-rollback
{"message":"Document restored to version from 20261102090636","success":true}
# T10
VERSION-THREE
恢复同时先把被替换的当前内容再存一份快照(源码行为,实测新增 20261102090638.md => VERSION-FOUR)——回滚本身也可回滚,无数据丢失。
6.4 秒级时间戳冲突(实测踩坑)
版本 ts 格式 yyyymmddhhmmss(秒级)。同一秒内「保存 + 恢复」会让恢复生成的快照文件覆盖同秒保存的快照(实测 20261102090339/090340 一次同秒操作后,原 VERSION-ONE 快照被 VERSION-TWO 内容覆盖)。规避:自动化脚本中 save/restore 之间 sleep ≥1 秒(本文 6.3 即按此写法),或将 wiki.max_versions 场景设计成不做亚秒级连续操作。
七、评论域
curl -s -b jar -X POST http://127.0.0.1:8080/api/comments/add/demo/api-test-doc -H 'Content-Type: application/json' -d '{"content":"API 评论测试"}'
curl -s -b jar http://127.0.0.1:8080/api/comments/demo/api-test-doc
curl -s -b jar -X DELETE http://127.0.0.1:8080/api/comments/delete/demo/api-test-doc/20261102073408_admin.md
{"success":true,"message":"Comment added successfully"}
{"comments":[{"ID":"20261102073408_admin.md","Author":"admin","Timestamp":"20261102073408","TimestampUnix":1793604848,"Content":"API 评论测试","RenderedHTML":"\u003cp\u003eAPI 评论测试\u003c/p\u003e\n","FormattedTime":"Nov 2, 2026 at 07:34"}],"success":true}
{"message":"Comment deleted successfully","success":true}
评论 ID 即磁盘文件名(<ts>_<user>.md,实测 comments/ 目录落盘同构);RenderedHTML 服务端已渲染。删除按 comment 域权限(editor+)。
八、附件域
8.1 上传
curl -s -b jar -X POST http://127.0.0.1:8080/api/files/upload -F "docPath=demo/d5-doc" -F "file=@upl.txt"
{"success":true,"message":"File uploaded successfully.","url":"/api/files/demo/d5-doc/upl.txt"}
8.2 列表 / 改名 / 下载 / 删除
curl -s -b jar http://127.0.0.1:8080/api/files/list/demo/d5-doc
curl -s -b jar -X POST http://127.0.0.1:8080/api/files/rename -H 'Content-Type: application/json' -d '{"currentPath":"demo/d5-doc/upl.txt","newName":"d5-renamed.txt"}'
curl -s -o dl.txt http://127.0.0.1:8080/api/files/demo/d5-doc/d5-renamed.txt
curl -s -b jar -X DELETE http://127.0.0.1:8080/api/files/delete/demo/d5-doc/d5-renamed.txt
{"success":true,"message":"Files retrieved successfully.","files":[{"name":"upl.txt","url":"/api/files/demo/d5-doc/upl.txt","size":13,"type":"text/plain"}]}
{"success":true,"message":"File renamed successfully.","url":"/api/files/demo/d5-doc/d5-renamed.txt"}
{"success":true,"message":"Files retrieved successfully.","files":[{"name":"d5-renamed.txt","url":"/api/files/demo/d5-doc/d5-renamed.txt","size":9,"type":"text/plain"}]}
(下载行实测返回文件本体;删除成功回显与常规一致。)上传受 max_upload_size(默认 10 MB)与扩展名/mimetype 白名单双重限制(配置详解 5.2)。
九、搜索域
curl -s -b jar -X POST http://127.0.0.1:8080/api/search -H 'Content-Type: application/json' -d '{"query":"UNIQUESEARCHTERM-8821"}'
[{"title":"T14 Doc","path":"/demo/t14-doc/","excerpt":"# t14 doc\n\nuniquesearchterm-8821\n"}]
实测要点:仅 POST(GET → 405);命中当前正文(大小写不敏感、excerpt 输出为小写化源文);无命中返回 null;正文被回滚后旧词即搜不到(快照不参与索引)。
十、设置域(admin)
curl -s -b jar http://127.0.0.1:8080/api/settings/wiki
curl -s -b jar http://127.0.0.1:8080/api/settings/security
回显见配置详解 3.3(全量 JSON 实测)。POST 修改为全量覆盖语义:
curl -s -b jar -X POST http://127.0.0.1:8080/api/settings/wiki -H 'Content-Type: application/json' -d '{"title":"📚 Wiki-Go","owner":"wiki.example.com","notice":"Copyright :::year::: © All rights reserved.","timezone":"Asia/Shanghai","private":true,"disable_comments":false,"disable_file_upload_checking":false,"max_versions":10,"max_upload_size":10,"language":"zh-CN","log_level":"info"}'
{"message":"Settings updated successfully","success":true}
实测把 private:true 落盘后,匿名取源文 401、站点根 303 跳登录;editor 调本域实测 403 {"message":"Admin access required","success":false}。
十一、用户域(admin)
11.1 建 / 查 / 改 / 删
curl -s -b jar -X POST http://127.0.0.1:8080/api/users -H 'Content-Type: application/json' -d '{"username":"d5user","password":"q1w2e3r4","role":"editor"}'
curl -s -b jar http://127.0.0.1:8080/api/users
curl -s -b jar -X PUT http://127.0.0.1:8080/api/users -H 'Content-Type: application/json' -d '{"username":"d5user","role":"viewer"}'
curl -s -b jar -X DELETE "http://127.0.0.1:8080/api/users?username=d5user"
{"message":"User created successfully","success":true}
{"success":true,"users":[{"username":"admin","role":"admin"},{"username":"d5user","role":"editor"}]}
{"logged_out":false,"message":"User updated successfully","success":true}
{"message":"User deleted successfully","success":true}
创建成功是 201(其余 200)。DELETE 不带 username 查询参数实测 400 {"message":"Username is required","success":false};重复删除返回 404(实测)。删除末位 admin 是否被兜底未做破坏性验证,生产上请保留至少一个 admin。
11.2 角色分层实测
| 会话 | /api/users GET | /api/settings/wiki POST | /api/backup/start | /api/save/* | /api/source/* |
|---|---|---|---|---|---|
| admin | 200 | 200 | 200 | 200 | 200 |
| editor(带组) | 403 | 403 | 403 | 200 | 200 |
| viewer | 403 | 403 | 403 | 401 | 401 |
(每格均为本文实测 HTTP 码;403 响应体 Admin access required、401 响应体 Unauthorized. Admin or editor access required.。)
十二、访问规则域(admin)
curl -s -b jar -X POST http://127.0.0.1:8080/api/access-rules -H 'Content-Type: application/json' -d '{"pattern":"/demo","access":"restricted","groups":["staff"],"description":"测试规则"}'
curl -s -b jar http://127.0.0.1:8080/api/access-rules
curl -s -b jar -X DELETE http://127.0.0.1:8080/api/access-rules/0
{"message":"Access rule created successfully","rule":{"pattern":"/demo","access":"restricted","groups":["staff"],"description":"测试规则"},"success":true}
{"rules":[{"pattern":"/demo","access":"restricted","groups":["staff"],"description":"测试规则"}]}
{"message":"Access rule deleted successfully","success":true}
实测坑:请求体必须扁平(包一层 rule:{} → 400 Pattern is required);删除按数组下标 DELETE /api/access-rules/<idx>;生效后 staff 组 editor 可写、无组 viewer 与匿名一律 401(十一/11.2 表对照)。
十三、目录域(登录即可)
curl -s -b jar http://127.0.0.1:8080/api/folders
{"folders":[{"path":"/","name":"/ (homepage)","level":0},{"path":"/demo","name":"Demo","level":0},{"path":"/demo/d5-doc","name":"D5 Doc","level":1}]}
返回按 level 展平的目录树,适合重建侧栏导航。
十四、备份导出域(admin)
导出 = 备份 zip(含 config.yaml、documents、versions、comments、pages、static),四步闭环实测:
curl -s -b jar -X POST http://127.0.0.1:8080/api/backup/start
curl -s -b jar http://127.0.0.1:8080/api/backup/status/1793610517809821671
curl -s -b jar http://127.0.0.1:8080/api/backup/list
curl -s -b jar -o backup.zip http://127.0.0.1:8080/api/backup/download/backup_20261102090837.zip
curl -s -b jar -X DELETE http://127.0.0.1:8080/api/backup/delete/backup_20261102090837.zip
{"jobId":"1793610517809821671","statusUrl":"/api/backup/status/1793610517809821671"}
{"id":"1793610517809821671","status":"completed","progress":100,"totalFiles":32,"processedFiles":32,"currentFile":"versions/documents/demo/t14-doc/20261102090837.md","filename":"backup_20261102090837.zip"}
{"backups":[{"name":"backup_20261102090837.zip","size":145001,"date":"2026-11-02 09:08:37","url":"/api/backup/download/backup_20261102090837.zip"}]}
下载实测 HTTP 200、145001 字节,zip 内核对命中 documents/demo/t14-doc/document.md 与对应版本快照。⚠️ 竞态坑:start 是异步作业,立刻 list 只会看到旧备份——先轮询 status 到 completed 再取(本文示例即此顺序);删除不存在的备份名返回 File not found(实测纯文本响应,非 JSON 信封)。
十五、导入域(admin)
导入 = multipart 上传 zip(字段名 zipFile),异步作业 + 状态端点,实测闭环:
python3 -c "import zipfile;z=zipfile.ZipFile('import_t14.zip','w');z.writestr('t14-doc/document.md','# T14 Doc\n\nUNIQUESEARCHTERM-8821 (imported copy)\n');z.close()"
curl -s -b jar -X POST http://127.0.0.1:8080/api/import -F "zipFile=@import_t14.zip"
curl -s -b jar http://127.0.0.1:8080/api/import/status/import-1793610519890164440
{"success":true,"message":"Import started successfully.","statusUrl":"/api/import/status/import-1793610519890164440","jobId":"import-1793610519890164440"}
{"status":"completed","progress":100,"successCount":1,"errorCount":0,"importedFiles":[{"originalPath":"t14-doc/document.md","newPath":"/t14-doc/document"}],"message":"Import completed successfully."}
实测坑与规则:
- zip 内每个
.md成为一个文档:sub/imported.md → /sub/imported(文件名作 slug);t14-doc/document.md → /t14-doc/document(document不会被特殊剥离)——整库备份 zip 直接回灌会得到<原路径>/document形态的路径,导入前先重组 zip 内目录名; status必须带真实 jobId(编造/过期 id → 404Import job not found.,实测);- 导入上限同
max_upload_size(源码以ParseMultipartForm(maxUploadSize)截断)。
十六、工具域(公开)
curl -s -X POST http://127.0.0.1:8080/api/utils/slugify -H 'Content-Type: application/json' -d '{"text":"Hello World 测试"}'
curl -s http://127.0.0.1:8080/api/data/emojis | head -3
curl -s http://127.0.0.1:8080/sitemap.xml
curl -s -b jar -X POST http://127.0.0.1:8080/api/links/fetch-metadata -H 'Content-Type: application/json' -d '{"url":"https://example.com"}'
{"slug":"hello-world-ce-shi"}
[
{ "emoji": "😀", "shortcodes": ["grinning"] },
{ "emoji": "😃", "shortcodes": ["smiley"] },
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url><loc>http://127.0.0.1:8080</loc><changefreq>weekly</changefreq><priority>1.0</priority></url>
{"title":"Example Domain","description":"","url":"https://example.com","success":true}
实测说明:slugify 中文转拼音(ce-shi);emojis 返回完整短码表(emoji 面板数据源);sitemap.xml 的 <loc> 取自请求 Host(换域名无需配置);fetch-metadata 需 editor+(路由挂 editorMiddleware,源码实查)。render-markdown 的输出包装异常见 17.3。
十七、通用约定与注意事项
17.1 无分页、无过滤参数
documents/list、backup/list、folders 均一次全量返回(实测无 page/limit 参数处理)。页面规模大时列表接口一次性拉全,前端自行分页。
17.2 Content-Type 三个世界
JSON 端点 application/json;save 收 text/markdown 原文;import/upload 用 multipart/form-data。写错类型不会报 415 而是解析为空/400,示例请严格照抄。
17.3 未实现与异常包装清单(实测)
GET /api/document/<path>→Document retrieval not yet implemented(取正文用/api/source/);POST /api/render-markdown会把请求 JSON 原样当 Markdown 渲染(输出含 HTML 转义的 JSON 串),勿用于生产渲染链路;- 备份删除不存在项 → 纯文本
File not found(非 JSON 信封)。
17.4 会话失效场景
改密码(针对默认口令态)、logout、登录封禁触发都会让既有 jar 立刻 401/429——脚本要具备重新登录重试逻辑(实测行为均有回显在案)。
十八、常见问题(FAQ)
18.1 非 HTTPS 环境所有接口 401?
Cookie Secure 属性未回传。开 allow_insecure_cookies: true 并重启(配置详解 5.1)。
18.2 回滚后搜不到旧内容?
设计如此:搜索只索引当前正文(第九节实测)。历史内容检索请导出备份 zip 在磁盘侧 grep。
18.3 脚本批量保存偶发版本丢失?
秒级 ts 同秒覆盖(6.4 实测),save/restore 间隔 ≥1 秒。
18.4 导入成功但页面路径多了 /document?
zip 内文件名参与 slug(第十五节规则),打包时用 <slug>.md 或重组目录名。
18.5 管理员误把自己删了?
users 段就是 config.yaml 明文可编辑部分:直接编辑 users 恢复一个 admin 条目(password 填 bcrypt 哈希,生成方法见配置详解 7.4),重启生效。
十九、参考链接
- 路由与权限源码:https://github.com/leomoon-studios/wiki-go/blob/master/internal/routes/routes.go
- 官方 REST 请求示例集:https://github.com/leomoon-studios/wiki-go/blob/master/rest-api-examples.http
- 版本处理源码:https://github.com/leomoon-studios/wiki-go/blob/master/internal/handlers/versions.go
- 部署:《Wiki-Go 1.9.2 Docker Compose 与二进制部署指南》
- 配置:《Wiki-Go 1.9.2 配置参数详解》
- 选型与限制:《Wiki-Go 1.9.2 介绍与选型指南》