mikochi 1.12.2 API 使用详解

mikochi 1.12.2 全部 11 个 REST 端点实测手册:双令牌认证体系(Bearer 头 JWT 与 auth 参数流式令牌)、browse/upload/move/delete/stream 逐域实测回显、错误码速查表、登录指数退避限流与 1.12.0 端点改名破坏性变更。

mikochimikochiGo自托管文件服务器2026/10/30

mikochi 1.12.2 API 使用详解

目标:本篇完整覆盖 mikochi 1.12.2 的 REST API——全部 11 个端点的认证方式、请求参数与实测响应回显(含错误码、限流与流式令牌机制),命令均经真实环境逐条执行验证,可直接作为脚本化操作 mikochi 的参考手册。

一、环境信息

  • 操作系统:Linux x86_64(内核 5.x 系列,Ubuntu 系发行版实测)
  • mikochi:1.12.2(官方镜像 zer0tonin/mikochi:1.12.2,Docker Compose 部署,栈 mikochi-1.12.2-stack)
  • API 基地址:http://127.0.0.1:8148(宿主机端口 8148 映射服务内 8080)
  • 客户端工具:curl(系统自带即可,全部示例可直接复制执行)
  • 登录凭据:admin / q1w2e3r4(演示账号)
  • API 契约基准:1.12.2 运行实例逐条实测(查询日期 2026-10-30);注意 1.12.0 起 API 有一处破坏性变更——流式令牌端点由 /api/single-use 更名为 /api/stream-token,旧脚本升级时需同步改名

二、前置条件

  • 一个可用的 mikochi 1.12.2 实例(部署见 《mikochi 1.12.2 Docker Compose 部署指南》);
  • curl 命令行工具(或 Postman 等等效客户端);
  • 理解两类令牌的分工(下文 4.1 节):普通操作用 Authorization: Bearer 头,流式播放用 auth 查询参数;
  • 健康自检:curl -s http://127.0.0.1:8148/ready,预期 {"status":"ok"}(实测回显)。

三、API 总览

3.1 协议与端点清单

REST 风格,JSON 报文为主(上传为 multipart 表单,流式下载为原始文件流)。全部端点如下:

分域方法端点认证载体作用
认证POST/api/login无登录换取 JWT(730 小时)
认证GET/api/refreshBearer 头续签新 JWT
认证POST/api/logoutBearer 头吊销当前令牌(jti 黑名单)
认证GET/api/stream-tokenBearer 头为单个文件签发 24 小时流式令牌
浏览GET/api/browse/*pathBearer 头列目录 / 模糊搜索
流式GET/api/stream/*pathauth 查询参数拉流播放或下载(目录自动 tar.gz)
写操作PUT/api/upload/*pathBearer 头上传文件(multipart,字段名 file)
写操作PUT/api/mkdir/*pathBearer 头创建目录
写操作PUT/api/move/*pathBearer 头移动/重命名(JSON body)
写操作DELETE/api/delete/*pathBearer 头删除文件/目录(递归)
健康检查GET/ready无k8s 就绪探针端点

3.2 认证与限流约定

  • 普通令牌:POST /api/login 签发,JWT HS256,payload 含 scope: "mikochi-user" 与 jti(令牌唯一 ID),有效期 730 小时;请求时以 Authorization: Bearer <token> 头携带;
  • 登出黑名单:logout 把当前令牌的 jti 拉入黑名单并保留 730 小时(与令牌寿命一致),期间同 jti 的令牌即使签名正确也返回 403;
  • 流式令牌:与普通令牌完全独立——scope 即目标文件路径、有效期 24 小时、经 auth 查询参数传递(专为 VLC/mpv 这类无法自定义请求头的播放器设计),且不进登出黑名单;
  • 登录限流:按用户名指数退避,第 n 次失败冷却 2^n 秒(1s → 2s → 4s → …),成功登录即重置;被限流时返回 HTTP 429;
  • 路径约定:所有 *path 均相对 DATA_DIR,以 / 开头(/docs/report.md 即 DATA_DIR/docs/report.md)。

四、认证与首次调用

4.1 登录拿令牌

curl -s -X POST http://127.0.0.1:8148/api/login \
  -H 'Content-Type: application/json' \
  -d '{"username":"admin","password":"q1w2e3r4"}'

实测回显(token 节选):

{"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzY29wZSI6Im1pa29jaGktdXNlciIsImlzcyI6Ik1pa29jaGkiLCJleHAiOjE3OTU5NzUwNjksImlhdCI6MTc5MzM0NzA2OSwianRpIjoiNDk5OTM5MTEtODY3Yy00NDVlLWJhOGEtMGZiZDk1YjVhMTljIn0.d0tN0jDdvqgDYdWeaq8ePUhwc8Dm-rfQvXsjHLMD52Y"}

解码 payload 可见 scope、iss: "Mikochi"、exp(签发时间 + 730 小时)、jti。下文示例统一以 $TOKEN 表示该令牌:

TOKEN=$(curl -s -X POST http://127.0.0.1:8148/api/login \
  -H 'Content-Type: application/json' \
  -d '{"username":"admin","password":"q1w2e3r4"}' | sed -E 's/.*"token":"([^"]+)".*/\1/')

4.2 未认证与错误凭据的对照

未携带令牌访问受保护接口:

$ curl -s http://127.0.0.1:8148/api/browse/
{"err":"Invalid Authorization header format"}          # HTTP 400:连 Bearer 头都没有

密码错误:

$ curl -s -X POST http://127.0.0.1:8148/api/login \
    -H 'Content-Type: application/json' -d '{"username":"admin","password":"wrong"}' -w ' [HTTP %{http_code}]'
{"err":"Invalid credentials"} [HTTP 401]

连续失败触发限流(实测第 2 次失败起):

{"err":"You are being rate limited"} [HTTP 429]

4.3 首次成功调用

拿到令牌后第一件事——列根目录,验证认证与数据边界同时成立:

curl -s http://127.0.0.1:8148/api/browse/ -H "Authorization: Bearer $TOKEN"

实测回显(数据目录预置两个测试文件):

{"fileInfos":[{"isDir":false,"size":14,"path":"/hello.txt"},{"isDir":false,"size":0,"path":"/sample.bin"}],"isRoot":true}

五、各 API 域详解

5.1 浏览域:/api/browse

GET /api/browse/*path(Bearer 头认证)

  • 路径参数 *path:目标目录(/ 为根);
  • 查询参数 search(可选):提供时在当前目录子树内做模糊搜索并按匹配度排序,不提供时列出当前目录直接子项。

列子目录:

$ curl -s http://127.0.0.1:8148/api/browse/docs -H "Authorization: Bearer $TOKEN"
{"fileInfos":[{"isDir":false,"size":14,"path":"/docs/report.md"}],"isRoot":false}

模糊搜索(搜索词 sam 命中 /sample.bin):

$ curl -s "http://127.0.0.1:8148/api/browse/?search=sam" -H "Authorization: Bearer $TOKEN"
{"fileInfos":[{"isDir":false,"size":2048,"path":"/sample.bin"}],"isRoot":true}

响应字段说明:fileInfos[].path 为相对根的绝对路径;isDir 区分目录与文件;size 字节数(目录固定 4096);顶层 isRoot 标识是否正在浏览根目录。

5.2 写操作域:upload / mkdir / move / delete

PUT /api/mkdir/*path(Bearer 头)——创建目录:

$ curl -s -X PUT http://127.0.0.1:8148/api/mkdir/docs -H "Authorization: Bearer $TOKEN"
{"status":"ok"}

PUT /api/upload/*path(Bearer 头)——上传文件。路径即上传后的目标路径,请求体必须是 multipart 表单且字段名固定为 file:

$ curl -s -X PUT http://127.0.0.1:8148/api/upload/docs/report.md \
    -F "file=@./data/hello.txt" -H "Authorization: Bearer $TOKEN"
{"status":"ok"}

错误姿势实测(直接放二进制体,非 multipart):

$ curl -s -X PUT http://127.0.0.1:8148/api/upload/hello.txt \
    --data-binary @data/hello.txt -H "Authorization: Bearer $TOKEN"
{"err":"Couldn't read file from request"}              # HTTP 400

PUT /api/move/*path(Bearer 头)——移动或重命名,JSON body 指定新路径:

$ curl -s -X PUT http://127.0.0.1:8148/api/move/hello.txt \
    -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
    -d '{"newPath":"/hello-renamed.txt"}'
{"status":"ok"}

body 字段名是驼峰 newPath(不是 new_path);目标路径同样相对 DATA_DIR。

DELETE /api/delete/*path(Bearer 头)——删除(文件与目录同一端点,目录递归真删、无回收站):

$ curl -s -X DELETE http://127.0.0.1:8148/api/delete/hello-renamed.txt -H "Authorization: Bearer $TOKEN"
{"status":"ok"}

⚠️ 注意:delete 对目录执行的是递归删除且不可撤销,脚本化批量删除前先以 browse 核对路径;写操作成功后服务端会同步刷新内存索引,紧随其后的 browse 不会读到旧数据(实测验证)。

5.3 流式域:stream-token + stream

这是 mikochi API 设计上最有特色的一域,两步协作:

第一步,签发流式令牌——GET /api/stream-token?target=<路径>(Bearer 头):

$ curl -s "http://127.0.0.1:8148/api/stream-token?target=/sample.bin" -H "Authorization: Bearer $TOKEN"
{"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzY29wZSI6Ii9zYW1wbGUuYmluIiwiaXNzIjoiTWlrb2NoaSIsImV4cCI6MTc5MzQzMzczMiwiaWF0IjoxNzkzMzQ3MzMyLCJqdGkiOiI3ZDU3NmM1OS03MzRkLTRmZDQtYmQ1OS03NDZkMmZhMzBmZGYifQ.w8jq_..."}(节选)

解码 payload:scope 字段即绑定的目标路径 /sample.bin,exp - iat = 86400 秒(24 小时)。

第二步,凭 auth 查询参数拉流——GET /api/stream/*path?auth=<流式令牌>:

$ curl -s "http://127.0.0.1:8148/api/stream/sample.bin?auth=$ST" | head -c 48 | xxd | head -2
00000000: b628 505d b6e2 5dac 2f45 cce4 ac40 c7c6  .(P]..]./E...@..
00000010: fbca b806 c9ca cac9 adc9 0584 5edc 8e8d  ............^...

($ST 为第一步签发的流式令牌;文件流原样返回,VLC/mpv 直接打开形如 http://<host>:8148/api/stream/sample.bin?auth=<token> 的 URL 即可播放。)

令牌与路径严格绑定——拿 /sample.bin 的令牌去请求别的文件:

$ curl -s "http://127.0.0.1:8148/api/stream/hello.txt?auth=$ST" -w ' [HTTP %{http_code}]'
{"err":"Expired token"} [HTTP 401]

("Expired token" 这个错误文案实际涵盖"scope 不匹配",令牌本身没过期——实测确认。)

目录流式下载:对目录路径发起 stream,服务端自动打包为 tar.gz 并附下载头:

$ curl -s "http://127.0.0.1:8148/api/stream/docs?auth=$SD" -o docs.tar.gz -w '[HTTP %{http_code}] bytes=%{size_download}\n'
[HTTP 200] bytes=124
$ tar -tzf docs.tar.gz
/report.md

令牌传错位置(用 token 参数而非 auth)的实测:

$ curl -s "http://127.0.0.1:8148/api/stream/sample.bin?token=$ST"
{"err":"Failed to parse token"} [HTTP 401]

5.4 会话域:refresh 与 logout

GET /api/refresh(Bearer 头)——用旧令牌换新令牌(续期):

$ curl -s http://127.0.0.1:8148/api/refresh -H "Authorization: Bearer $TOKEN"
{"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzY29wZSI6Im1pa29jaGktdXNlciIsImlzcyI6Ik1pa29jaGkiLCJleHAiOjE3OTU5NzUxN..."}(节选)

POST /api/logout(Bearer 头)——吊销当前令牌:

$ curl -s -X POST http://127.0.0.1:8148/api/logout -H "Authorization: Bearer $TOKEN"
{"message":"Logged out successfully"}

登出后同一令牌立即失效(jti 进黑名单),实测:

$ curl -s http://127.0.0.1:8148/api/browse/ -H "Authorization: Bearer $TOKEN"
{"err":"Invalid token"}          # HTTP 403:签名正确但 jti 已被吊销

黑名单语义可再验证一步:拿已登出的令牌去签流式令牌,同样被拒:

$ curl -s "http://127.0.0.1:8148/api/stream-token?target=/sample.bin" -H "Authorization: Bearer $TOKEN"
{"err":"Invalid token"} [HTTP 403]

5.5 健康检查域:/ready

$ curl -s http://127.0.0.1:8148/ready
{"status":"ok"}

无需认证,k8s 就绪/存活探针直接指向它即可。

六、错误码与响应速查

HTTP 状态实测报文触发场景
200{"status":"ok"} / JSON 数据正常
400{"err":"Invalid Authorization header format"}缺少或格式错误的 Bearer 头
400{"err":"Couldn't read file from request"}上传非 multipart 或字段名不是 file
400{"err":"Couldn't deserialize command"}move 的 JSON body 解析失败
400{"err":"Invalid path"}路径逃逸 DATA_DIR 或非法
401{"err":"Invalid credentials"}登录凭据错误
401{"err":"Failed to parse token"}令牌无法解析(签名密钥不符/传参位置错/重启后旧令牌)
401{"err":"Expired token"}流式令牌过期或 scope 与请求路径不匹配
403{"err":"Invalid token"}jti 已被 logout 吊销
429{"err":"You are being rate limited"}登录失败触发指数退避限流
500{"err":"Couldn't move file"} 等服务端文件系统操作失败(如目标父目录不存在)

💡 说明:mikochi 的错误报文统一为 {"err": "<一句话>"}(logout 成功报文为 {"message": ...} 属例外),脚本解析时按 err 字段判错即可;Expired token 文案具有二义性(过期或不匹配),排障时先核对 scope 与路径是否完全一致(含前导斜杠)。

七、分页、过滤与排序的通用约定

mikochi API 没有分页、排序与字段过滤参数——浏览接口一次性返回目录全部直接子项,搜索接口返回全部匹配项。相关约定替代如下:

  • 大目录性能由服务端内存索引兜底(浏览不落盘遍历),实测根目录请求毫秒级返回(Gin 日志显示 browse 类请求耗时约 50–100µs 量级);
  • 排序由前端按名称组织,API 层保持原始顺序;
  • 需要"分页"效果时用 search 参数缩小结果集(模糊匹配当前目录子树)。

脚本化调用时的实用约定:写操作(upload/mkdir/move/delete)成功返回一律是 {"status":"ok"},读操作返回 {"fileInfos": [...], "isRoot": bool},以此区分成败即可,无需依赖 HTTP 状态码细分。

八、常见问题

Q1:脚本里 token 该放哪个位置? 两套体系:普通接口放 Authorization: Bearer 请求头;stream 接口放 auth 查询参数。实测把流式令牌放进 token 查询参数会得到 Failed to parse token。

Q2:升级 1.12.0+ 后旧脚本报 404? 流式令牌端点改名:/api/single-use → /api/stream-token(1.12.0 破坏性变更,官方 Release Note 明示)。语义同时明确为"绑定单文件 + 24 小时有效",并非字面上的"一次性"。

Q3:move 报 500 Couldn't move file? 实测常见原因:目标路径的父目录不存在,或目标与源在同一不可写挂载上。先确认目标父目录存在(可先用 mkdir 创建),JSON body 字段名确认是驼峰 newPath。

Q4:登录一直 429? 指数退避按用户名计数:失败冷却时间按 2 的幂增长,连续失败后冷却可达数分钟。等待冷却自然解除(不要用错误密码反复试探),或重启服务清空内存计数器。

Q5:重启后所有脚本令牌全失效? JWT_SECRET 未固定时每次重启随机重生(实测重启后旧令牌 401),两类令牌(普通/流式)同时失效。生产实例固定该变量(见 《mikochi 1.12.2 配置参数详解》 5.4 节)。

九、参考链接