flatnotes v5.5.5 常用工具与 API 详解

flatnotes v5.5.5 常用工具与 REST API 详解:数据目录直读检索与备份、JWT 认证、笔记 CRUD、全文搜索、标签、附件共 10 个端点的实测请求与响应回显。

flatnotesflatnotes笔记知识库2026/10/30

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 的接口约定比较克制,使用前记住四条:

  1. 没有独立的笔记列表接口:列全部笔记用 GET /api/search?term=*(首页快捷区块正是这么做的);
  2. 排序/过滤只在搜索接口:sort(score/title/lastModified)+ order(asc/desc)+ term 三者组合覆盖按相关度、按标题、按修改时间三种视图;
  3. 只有 limit 没有 offset:不支持偏移分页,超大笔记库的翻页需客户端按 lastModified 或标题游标自行处理;
  4. 响应统一驼峰命名,唯一例外是登录接口的 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 读取,可能读到旧索引,再做一次搜索即可触发同步。

九、参考链接

本系列其他篇章: