flatnotes v5.5.5 常用工具与 API 详解
flatnotes v5.5.5 常用工具与 REST API 详解:数据目录直读检索与备份、JWT 认证、笔记 CRUD、全文搜索、标签、附件共 10 个端点的实测请求与响应回显。
flatnotes v5.5.5 常用工具与 API 详解
目标:面向已完成部署的运维者与二次开发者,给出 flatnotes v5.5.5 的日常运维工具用法(数据目录直读检索、备份、健康自检)与全部 REST API 的实测调用方式(JWT 认证、笔记 CRUD、全文搜索、标签、附件、配置、健康检查),每个接口均附真实请求与响应回显。全部示例已在 Ubuntu 22.04 服务器实测通过。
一、环境信息
| 项目 | 值 |
|---|---|
| flatnotes 版本 | v5.5.5(2026-08-29 发布) |
| 服务地址 | http://localhost:8143(部署映射见《flatnotes v5.5.5 Docker Compose 部署指南》) |
| 数据目录 | /opt/flatnotes/data(服务内 /data) |
| 演示账号 | admin / q1w2e3r4 |
| 客户端工具 | curl 8.18.0 |
| 接口口径 | 服务内置 OpenAPI 描述 http://localhost:8143/openapi.json(2026-10-30 实查,10 个路径) |
二、前置条件
- 服务可探活:
curl http://localhost:8143/health
实测输出:
"OK"
- 已按《flatnotes v5.5.5 配置参数详解》确认认证模式(本文全部示例基于默认的
password模式); - 理解响应字段命名:笔记/搜索/附件等业务模型返回驼峰命名(如
lastModified),而登录接口返回的access_token/token_type为下划线命名,照抄实测回显即可,无需自行转换。
三、常用运维工具
3.1 数据目录直读与检索
flatnotes 的笔记就是磁盘上的 .md 文件,日常巡检、批量检索、脚本处理都可以直接在数据目录上做,无需经过应用:
cd /opt/flatnotes/data
find . -maxdepth 1 -not -name '.*' | sort
实测输出:
./attachments
./Nginx 配置片段.md
./服务器巡检手册(v2).md
全文检索直接用 grep(与笔记内容零转换):
grep -rn 'df -h' . --include='*.md'
实测输出:
./服务器巡检手册(v2).md:5:- 检查磁盘 `df -h`
单篇笔记就是纯文本,cat 即可查看原文:
# Nginx 配置片段
常用反代配置:
```nginx
location / {
proxy_pass http://127.0.0.1:3000;
}
#nginx #运维
> 💡 说明:正文里的 `#tag` 标签**保留在文件内容中**(标签只进搜索索引、不修改文件),因此磁盘直读与应用内看到的内容一致。中文片段如 `#运维` 不会被识别为标签(标签正则仅接受 `[a-zA-Z0-9_-]+`),但它仍是正文的一部分,全文搜索能命中。
### 3.2 备份与恢复
整个数据目录打包即完成全量备份(索引目录 `.flatnotes` 一并打包亦可,恢复后可整体使用):
```bash
tar -czf /opt/flatnotes/notes-backup.tar.gz -C /opt/flatnotes/data .
ls -la /opt/flatnotes/notes-backup.tar.gz
实测输出:
-rw-r--r-- 1 root root 6867 Oct 30 01:47 /opt/flatnotes/notes-backup.tar.gz
恢复:解包回 /opt/flatnotes/data(属主保持部署身份 PUID:PGID),重启服务即可;若担心索引状态异常,可只删除 .flatnotes/ 子目录后重启,应用会重建索引(启动日志 Creating new index)。
3.3 健康与认证自检
curl http://localhost:8143/health # 存活检查,输出 "OK"
curl http://localhost:8143/api/auth-check # 会话检查,输出 "OK" 或 401
/api/auth-check 是最轻量的鉴权链路自检:401 说明认证层工作正常但 Token 缺失/失效,"OK" 说明 Token 有效。监控探针建议用 /health(不依赖认证)。
3.4 接口自描述文档
flatnotes 基于 FastAPI,自带交互式接口文档与机器可读描述,升级版本后可直接自查接口变化:
curl -s http://localhost:8143/openapi.json | head -c 200
实测输出(节选):
{"openapi":"3.1.0","info":{"title":"FastAPI","version":"0.1.0"},…
浏览器访问 http://localhost:8143/docs 可打开 Swagger UI(实测 HTTP 200),逐接口在线试调。
四、API 总览
协议与约定:HTTP + JSON(附件上传为 multipart/form-data);认证方式为 OAuth2 风格 Bearer Token(JWT/HS256 签发,可由登录接口或 Cookie token 携带);服务端未内置限流机制,公开暴露时请在反代层做限速。openapi.json 实查的 10 个路径如下:
| 方法 | 路径 | 认证 | 说明 |
|---|---|---|---|
| POST | /api/token | 无 | 登录换取 Token(password/totp 模式存在) |
| GET | /api/auth-check | 需要 | 会话有效性自检 |
| GET | /api/notes/{title} | 需要* | 读取单篇笔记(*:none 模式无需认证,下同) |
| POST | /api/notes | 需要* | 新建笔记(read_only 模式禁用) |
| PATCH | /api/notes/{title} | 需要* | 修改内容/重命名 |
| DELETE | /api/notes/{title} | 需要* | 删除笔记 |
| GET | /api/search | 需要* | 全文搜索 |
| GET | /api/tags | 需要* | 全部标签清单 |
| GET | /api/config | 无 | 前端所需服务端配置 |
| GET/POST | /api/attachments、/api/attachments/{filename} | 需要* | 附件上传/下载 |
另有隐藏别名 GET /attachments/{filename}(生成站外相对链接用)与健康检查 GET /health,均不在 schema 中。
五、认证与首次调用
5.1 登录换取 Token
curl -s -X POST http://localhost:8143/api/token \
-H 'Content-Type: application/json' \
-d '{"username":"admin","password":"q1w2e3r4"}'
实测输出:
{"access_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZG1pbiIsImV4cCI6MTc5NTg4NzcwNX0.PW28_r_cP-buWm_FS4orAkoXL--0Fc2xl_gwx_UyyqQ","token_type":"bearer"}
密码错误时实测返回(HTTP 401):
{"detail":"Invalid login details."}
TOTP 模式(FLATNOTES_AUTH_TYPE=totp)下,登录密码为「密码 + 当前 6 位动态码」拼接,实测(密码 q1w2e3r4、动态码 015396)返回 HTTP 200 与同结构 Token;同一动态码 30 秒内不可复用,复用返回 401。
5.2 携带 Token 调用
TOKEN=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZG1pbiIsImV4cCI6MTc5NTg4NzcwNX0.PW28_r_cP-buWm_FS4orAkoXL--0Fc2xl_gwx_UyyqQ
curl -s http://localhost:8143/api/auth-check -H "Authorization: Bearer $TOKEN"
实测输出:
"OK"
不带 Token 调用受保护接口,实测返回(HTTP 401):
{"detail":"Invalid authentication credentials"}
六、各 API 域详解
6.1 笔记域(CRUD)
新建:POST /api/notes,请求体 {title, content},标题即文件名(服务内返回驼峰字段):
curl -s -X POST http://localhost:8143/api/notes \
-H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-d '{"title":"服务器巡检手册","content":"# 服务器巡检手册\n\n每周巡检清单:\n\n- 检查磁盘使用率 `df -h`\n- 检查内存 `free -m`\n\n#运维 #巡检"}'
实测输出:
{"title":"服务器巡检手册","content":"# 服务器巡检手册\n\n每周巡检清单:\n\n- 检查磁盘使用率 `df -h`\n- 检查内存 `free -m`\n\n#运维 #巡检","lastModified":1793295719.4703584}
标题重复时实测返回(HTTP 409):
{"detail":"Cannot create note. A note with the same title already exists."}
标题含 < > : " / \ | ? * 任一字符时实测返回(HTTP 422,参数校验层拦截,响应为校验错误数组):
{"detail":[{"type":"value_error","loc":["body","title"],"msg":"Value error, title cannot include any of the following characters: <>:\"/\\|?*","input":"bad/title","ctx":{"error":{}}}]}
读取:GET /api/notes/{title}(标题需 URL 编码):
curl -s -H "Authorization: Bearer $TOKEN" \
'http://localhost:8143/api/notes/%E6%9C%8D%E5%8A%A1%E5%99%A8%E5%B7%A1%E6%A3%80%E6%89%8B%E5%86%8C'
实测输出:
{"title":"服务器巡检手册","content":"# 服务器巡检手册\n\n每周巡检清单:\n\n- 检查磁盘使用率 `df -h`\n- 检查内存 `free -m`\n\n#运维 #巡检","lastModified":1793295719.4703584}
不存在时实测返回(HTTP 404):
{"detail":"The specified note cannot be found."}
修改/重命名:PATCH /api/notes/{title},请求体 {new_title, new_content}(两字段均可选,只传其一即单项修改):
curl -s -X PATCH -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d '{"new_title":"服务器巡检手册(v2)","new_content":"# 服务器巡检手册\n\n更新于 2026-10-30:增加 CPU 检查项。\n\n- 检查磁盘 `df -h`\n- 检查内存 `free -m`\n- 检查 CPU `top -bn1`\n\n#运维 #巡检"}' \
'http://localhost:8143/api/notes/%E6%9C%8D%E5%8A%A1%E5%99%A8%E5%B7%A1%E6%A3%80%E6%89%8B%E5%86%8C'
实测输出(标题与内容同时更新,磁盘文件随之重命名):
{"title":"服务器巡检手册(v2)","content":"# 服务器巡检手册\n\n更新于 2026-10-30:增加 CPU 检查项。\n\n- 检查磁盘 `df -h`\n- 检查内存 `free -m`\n- 检查 CPU `top -bn1`\n\n#运维 #巡检","lastModified":1793295730.745964}
删除:DELETE /api/notes/{title},成功返回 HTTP 200、响应体为 null:
null
删除后再读取该标题,实测返回 HTTP 404:
{"detail":"The specified note cannot be found."}
💡 说明:
lastModified为 Unix 时间戳(秒,浮点)。由于「文件即数据」,用 PATCH 重命名等价于磁盘上重命名.md文件,正文中[[...]]形式的 Wikilink 不会自动跟进新标题,重命名后需自行处理引用。
6.2 搜索域(/api/search)
GET /api/search,参数:term(必填,检索词)、sort(score/title/lastModified,默认 score)、order(asc/desc,默认 desc)、limit(整数,可选)。全文搜索实测:
curl -s -H "Authorization: Bearer $TOKEN" \
'http://localhost:8143/api/search?term=%E5%B7%A1%E6%A3%80'
实测输出(term=巡检,相关度得分与内容高亮):
[{"title":"服务器巡检手册(v2)","lastModified":1793295730.745964,"score":0.8953488372093024,"titleHighlights":null,"contentHighlights":"CPU `top -bn1`\n\n#运维 #<b class=\"match term0\">巡检</b>","tagMatches":null}]
标签检索:检索词写 #标签名,命中标签字段(tagMatches 有值,标签字段权重更高故 score=2.0):
curl -s -H "Authorization: Bearer $TOKEN" \
'http://localhost:8143/api/search?term=%23nginx'
实测输出:
[{"title":"Nginx 配置片段","lastModified":1793295719.479419,"score":2.0,"titleHighlights":null,"contentHighlights":null,"tagMatches":["nginx"]}]
组合排序与限量:term=* 匹配全部笔记,配合 sort/order/limit 可当「列表接口」使用:
curl -s -H "Authorization: Bearer $TOKEN" \
'http://localhost:8143/api/search?term=*&sort=title&order=asc&limit=1'
实测输出:
[{"title":"Nginx 配置片段","lastModified":1793295719.479419,"score":null,"titleHighlights":null,"contentHighlights":null,"tagMatches":null}]
6.3 标签域(/api/tags)
GET /api/tags 返回全部已被索引的标签(去重、小写):
curl -s -H "Authorization: Bearer $TOKEN" 'http://localhost:8143/api/tags'
实测输出:
["nginx"]
注意:上述测试笔记正文包含 #运维、#巡检、#nginx 三个片段,但返回值只有 nginx——标签识别正则为 [a-zA-Z0-9_-]+,中文片段不计入标签。这是 v5.5.5 的真实限制(源码实查),标签体系请用英文命名。
6.4 附件域(/api/attachments)
上传:POST /api/attachments,multipart 表单字段 file:
printf 'flatnotes attachment test 2026-10-30\n' > attach-test.txt
curl -s -H "Authorization: Bearer $TOKEN" -F "file=@attach-test.txt" \
http://localhost:8143/api/attachments
实测输出:
{"filename":"attach-test.txt","url":"attachments/attach-test.txt"}
下载:GET /api/attachments/{filename}:
curl -s -H "Authorization: Bearer $TOKEN" \
'http://localhost:8143/api/attachments/attach-test.txt'
实测输出(原文件内容原样返回):
flatnotes attachment test 2026-10-30
同名重复上传返回 HTTP 409 Cannot create attachment. An attachment with the same filename already exists.;文件不存在返回 HTTP 404。附件落盘于数据目录的 attachments/ 子目录,删除需在磁盘上操作(v5.5.5 未提供删除附件的接口)。
6.5 配置域与健康域
GET /api/config 无需认证,返回前端所需的服务端配置(与《flatnotes v5.5.5 配置参数详解》参数一一对应):
{"authType":"password","quickAccessHide":false,"quickAccessTitle":"RECENTLY MODIFIED","quickAccessTerm":"*","quickAccessSort":"lastModified","quickAccessLimit":4}
GET /health 无需认证,返回 "OK",适合做存活探针。
七、分页/过滤/排序通用约定
flatnotes v5.5.5 的接口约定比较克制,使用前记住四条:
- 没有独立的笔记列表接口:列全部笔记用
GET /api/search?term=*(首页快捷区块正是这么做的); - 排序/过滤只在搜索接口:
sort(score/title/lastModified)+order(asc/desc)+term三者组合覆盖按相关度、按标题、按修改时间三种视图; - 只有
limit没有offset:不支持偏移分页,超大笔记库的翻页需客户端按lastModified或标题游标自行处理; - 响应统一驼峰命名,唯一例外是登录接口的
access_token/token_type(OAuth2 规范要求下划线)。
八、常见问题(FAQ)
Q1:接口返回 401 Invalid authentication credentials,但密码明明是对的?
401 出现在受保护接口上,与登录密码无关:是 Token 缺失、过期(默认 30 天)或 FLATNOTES_SECRET_KEY 变更导致校验失败。重新 POST /api/token 换新 Token 即可。
Q2:搜索中文关键词没结果?
检查三件事:URL 是否对检索词做了编码(curl 里建议用 --data-urlencode 配 -G);应用外改过的 .md 文件要等下一次搜索触发索引增量同步(官方机制:每次搜索时同步);确认检索词不在代码块内(索引会剥离代码块内容)。
Q3:能通过 API 管理标签吗?
不能。标签是正文 #tag 的派生物,没有独立的增删接口;GET /api/tags 只读。要「删除标签」就编辑笔记内容移除 #tag 片段。
Q4:422 错误里 loc 是什么?
FastAPI 参数校验的标准结构:loc 标注出错字段位置(如 body.title),msg 为原因。标题非法字符是最高频的 422 场景(< > : " / \ | ? *)。
Q5:脚本里应优先用接口还是直接读文件?
写操作(建/改/删)走 API,能保证标题合法性与索引即时同步;只读统计、批量检索、备份走文件系统更直接。两者混用时注意:直接改文件后立刻调 API 读取,可能读到旧索引,再做一次搜索即可触发同步。
九、参考链接
- 官方仓库(README 列明 Restful API 特性):https://github.com/dullage/flatnotes
- 服务内置接口描述(部署后可访问):
http://<服务器IP>:8143/openapi.json、http://<服务器IP>:8143/docs - 官方 Wiki:https://github.com/dullage/flatnotes/wiki
本系列其他篇章: