DailyTxT 2.6.4 API 使用详解

DailyTxT 2.6.4 REST API 全表面逐端点实测:JWT Cookie 会话、日记/标签/模板/附件/地图/统计各域、免登录备份端点 backupUser、HTML 导出与导入闭环、管理 API 与错误码行为

DailyTxT自托管Go2026/11/01

DailyTxT 2.6.4 API 使用详解

目标:逐端点实测 DailyTxT 2.6.4 的 REST API 全表面——认证链(注册/登录/Cookie 会话)、日记读写、标签、模板、附件、地图标记、备份/导入/HTML 导出与管理端点,覆盖参数格式、成功回显与错误码行为。已在 Ubuntu 26.04.1 LTS(x86_64,Docker 29.9.2,phitux/dailytxt:2.6.4,端口 8137)实测通过。

一、环境信息

  • 实例:容器 dailytxt-264-web(compose 项目 dailytxt-264-stack),宿主 http://127.0.0.1:8137 → nginx 80 → Go 后端 8000
  • 部署与数据卷配置见 《DailyTxT 2.6.4 Docker Compose 部署指南》;环境变量语义见 《DailyTxT 2.6.4 配置参数详解》
  • 演示账号:admin / q1w2e3r4;管理面板密码 q1w2e3r4(ADMIN_PASSWORD)
  • 客户端工具:curl(cookie jar 维持会话)+ python3(生成 uuid / URL 编码)
  • 路由挂载:所有端点在 /api/ 前缀下(Go http.StripPrefix("/api")),无版本号;前端 SPA 与 API 同源

二、前置条件

  • DailyTxT 容器运行中,curl http://127.0.0.1:8137/api/version 有响应;
  • 已注册账号(或 ALLOW_REGISTRATION=true 可现场注册,见 4.1);
  • 下文所有请求以 shell 变量约定:B=http://localhost:8137,登录态经 curl -b cookies.txt 携带。

⚠️ 实操坑:cookie jar 与域名绑定。用 127.0.0.1 登录写入的 cookie,请求 localhost 时 curl 不会带出,表现为莫名 401。全程统一主机名。

三、API 总览

约定内容
协议REST,请求/响应 JSON(文件与导出为 multipart / zip 流)
认证登录签发 token Cookie(JWT HS256,HttpOnly; SameSite=Lax),后续请求自动携带;无 Bearer/API Key 机制
超时普通端点 15 秒 TimeoutHandler;长任务端点(login、statistics、uploadFile、downloadFile、allGPXFiles、exportData、importData、backup、backupUser)不设超时(源码 main.go 实测清单)
反向代理nginx client_max_body_size 0(附件上传不限体积,应用层单文件限 500MB)、proxy_read_timeout 6000s
错误格式纯文本 http.Error(如 Unauthorized、Missing translations parameter),非 JSON

端点域一览(路由表源码实查 + 本篇逐个实测):

域前缀鉴权
版本/api/version公开
用户/api/users/*login/register/logout/migrationProgress/isRegistrationAllowed 公开,其余需会话
日记/标签/模板/附件/地图/备份/api/logs/*会话(backupUser 例外,见 5.7)
管理/api/admin/*会话 + admin_password 双重校验

四、认证与首次调用

4.1 注册与登录

curl -s -X POST $B/api/users/register -H 'Content-Type: application/json' \
  -d '{"username":"admin","password":"q1w2e3r4"}'
{"success":true}

注册被禁时(ALLOW_REGISTRATION=false 默认值):

Registration is not allowed

http=403

重复注册实测返回 Internal Server Error: username already exists(HTTP 500,非 409,见 FAQ 7.7)。

登录:

curl -s -c cookies.txt -X POST $B/api/users/login -H 'Content-Type: application/json' \
  -d '{"username":"admin","password":"q1w2e3r4"}'
{"available_backup_codes":-1,"migration_started":false,"username":"admin"}

响应同时下发会话 Cookie(实测头):

Set-Cookie: token=<JWT>; Path=/; Expires=Fri, 11 Dec 2026 11:42:18 GMT; HttpOnly; SameSite=Lax

Expires 即 LOGOUT_AFTER_DAYS(本实例设 40)。available_backup_codes:-1 表示尚未生成备份码。

4.2 会话校验与登出

curl -s -b cookies.txt $B/api/users/check
{"user_id":1,"username":"admin"}

无 cookie 访问受保护端点:Unauthorized(401)。登出:

curl -s -b cookies.txt $B/api/users/logout
{"success":true}

⚠️ 实测:logout 仅清服务端下发 cookie 的意图,旧 JWT 在有效期内仍可继续调 API(登出后带旧 cookie check 仍返回 200)。服务端全局吊销的唯一手段是轮换 SECRET_TOKEN(配置篇 5.1 实测 401)。

4.3 版本检查

curl -s $B/api/version
{"current_version":"2.6.4","latest_overall_version":"2.6.4","latest_stable_version":"2.6.4"}

该端点后端实时访问 Docker Hub,容器出网受限时返回 Request timeout(15s),修复方法(HTTPS_PROXY)见部署篇 FAQ 11.1。

五、各 API 域详解

5.1 日记域(/api/logs/)

写日记 saveLog(POST JSON,day/month/year 定位,text 支持 Markdown):

curl -s -b cookies.txt -X POST $B/api/logs/saveLog -H 'Content-Type: application/json' \
  -d '{"day":1,"month":11,"year":2026,"text":"# DailyTxT 实测\n端到端加密日记第一篇。","date_written":"2026-11-01"}'
{"history_available":true,"success":true}

读日记 getLog(query 三参数):

curl -s -b cookies.txt "$B/api/logs/getLog?year=2026&month=11&day=1"
{"date_written":"2026-11-01","files":[],"history_available":true,"pins":[],"tags":[],"text":"# DailyTxT 实测\n端到端加密日记第一篇。"}

修改留痕 getHistory(同一篇多次 saveLog 后实测):

[{"date_written":"2026-11-01","text":"# DailyTxT 实测\n端到端加密日记第一篇。"}]

阅读模式 loadMonthForReading(整月,含标签/标记解析):

[{"date_written":"2026-11-01","day":1,"pins":[{"id":1,"lat":31.2304,"lon":121.4737,"text":"上海"}],"tags":[1],"text":"# DailyTxT 实测\n端到端加密日记第一篇。"}]

删除一天 deleteDay(先写 2 日再删,实测序列):

{"history_available":false,"success":true}

{"success":true}

{"date_written":"","files":[],"pins":[],"tags":[],"text":""}

书签 bookmarkDay 与标记日查询:

{"bookmarked":true,"success":true}

getMarkedDays 必须带 year 且带 month(缺参实测分别返回 Invalid year parameter / Invalid month parameter)。getALookBack 需 month+year+day 三参数(缺一报对应 Invalid ... parameter)。

5.2 标签域

创建→列表→挂到日记→搜索的完整实测链:

curl -s -b cookies.txt -X POST $B/api/logs/saveNewTag -H 'Content-Type: application/json' \
  -d '{"name":"写作","color":"#e91e63","icon":"✍️"}'
{"success":true}

[{"color":"#e91e63","icon":"✍️","id":1,"name":"写作"}]

addTagToLog({"tag":1,"day":1,"month":11,"year":2026})返回 {"success":true};removeTagFromLog、editTag、deleteTag 同构。

全文搜索(服务端解密后检索,这正是它不做客户端 E2E 的原因):

curl -s -b cookies.txt "$B/api/logs/searchString?searchString=加密"
[{"day":1,"month":"11","text":"DailyTxT 实测 端到端<b>加密</b>日记第一篇。","year":"2026"}]

命中词用 <b> 包裹。searchTag 按标签名检索返回整篇原文。

5.3 模板域

saveTemplates 提交数组全量覆盖,getTemplates 回读(实测):

{"success":true}

[{"is_default":true,"name":"晨间模板","text":"## 今日三件事\n- "}]

5.4 附件域

上传 uploadFile:multipart 表单,file 字段 + query 参数 uuid&day&month&year(uuid 客户端自生成;缺 uuid 实测报 Missing uuid parameter):

U=$(python3 -c "import uuid; print(uuid.uuid4())")
curl -s -b cookies.txt -X POST "$B/api/logs/uploadFile?uuid=$U&day=1&month=11&year=2026" \
  -F "file=@sample.txt"
{"success":true}

上传后 getLog 的 files 数组(实测:filename 明文、enc_filename 密文并存,落盘文件本体加密):

{"files":[{"enc_filename":"YgOZVhbIb1JHacfJqYFLxdrIYGOzc5jpuAdeEfig2H8wcTF63w4=","filename":"sample.txt","size":38,"uuid_filename":"902adb87-0ff4-43f0-900e-b8d02d5f6776"}]}

下载 downloadFile(?uuid=$U)实测 http=200 bytes=38,内容与上传原文一致。renameFile、reorderFiles、deleteFile 均返回 {"success":true};GPX 轨迹文件用 allGPXFiles 聚合查询。

5.5 地图标记域

addPin(日记页挂坐标点,实测上海外滩):

{"pin":{"id":1,"lat":31.2304,"lon":121.4737,"text":"上海"},"success":true}

allPins 按天聚合:

[{"day":1,"month":11,"pins":[{"id":1,"lat":31.2304,"lon":121.4737,"text":"上海"}],"year":2026}]

updatePinText / movePin / deletePin 实测均 {"success":true};修改后 getLog 回读可见新值("pins":[{"id":1,"lat":31.24,"lon":121.49,"text":"上海·外滩"}])。

5.6 用户设置与统计域

getUserSettings 返回全量偏好 JSON(节选,实测):

{"aLookBackYears":[1,5,10],"background":"gradient","checkForUpdates":true,"defaultMap":"osm","firstDayOfWeek":"monday","language":"en","timezone":"UTC","useMap":true,"writeDateFormat":"2-digit"}

saveUserSettings 全量提交修改(实测把 language 改为 zh 后回读生效)。statistics 返回逐日统计(字数/文件/标签/书签,供 GitHub 式分布图):

[{"year":2026,"month":11,"day":1,"wordCount":13,"fileCount":0,"fileSizeBytes":0,"pinCount":0,"tags":[1],"isBookmarked":false}]

validatePassword(改密前置校验):

{"available_backup_codes":-1,"valid":true}

createBackupCodes(带当前密码)实测生成 6 个一次性备份码(仅展示一次):

{"available_backup_codes":-1,"backup_codes":["$3jv{mJyM%","9C]#XMWRo#","V[06~4Ae1~","W9{aIb!hu4","TVLD3$(W39","8h=#c2R9GP"],"success":true}

不带密码直接调用返回 {"message":"Error checking password","success":false}。changePassword、changeUsername、deleteAccount、checkChangelog(实测 {"changelog":null})同域。

5.7 备份、导出与导入域

backup(POST,登录态,加密与否可选):

curl -s -b cookies.txt -X POST $B/api/logs/backup -H 'Content-Type: application/json' \
  -d '{"username":"admin","password":"q1w2e3r4","encrypted":false,"includeFiles":true,"includeTemplates":true,"includeTags":true,"includePins":true,"includeBookmarks":true}' \
  -o backup_plain.zip -w 'http=%{http_code} bytes=%{size_download}\n'
http=200 bytes=806
Archive:  backup_plain.zip
  Length      Date    Time    Name
---------  ---------- -----   ----
      156  1980-00-00 00:00   tags.json
      162  1980-00-00 00:00   templates.json
      484  1980-00-00 00:00   2026/11.json
---------                     -------
      802                     3 files

encrypted:true 时 zip 额外含 user.json(密钥材料,4 文件 3581B 解包,实测 http=200 bytes=2566)。

backupUser(POST /api/logs/backupUser,免登录——专为 cron 自动备份设计,凭据即鉴权):同上参数直接调用,实测 http=200 bytes=806 与登录态备份字节一致。密码错误实测:

Error getting encryption key: error creating cipher: chacha20poly1305: bad key length

http=500

exportData(GET,HTML 导出):参数 period=periodAll|periodVariable(+startDate/endDate)、split=month|year|aio、translations(必填,完整 JSON)、imagesInHTML/tagsInHTML/pinsInHTML/extendedFormatting 布尔开关。缺 translations 实测 400 Missing translations parameter;完整调用实测返回 zip:

http=200 bytes=3558 type=application/zip
Archive:  DailyTxT_export.html (14016 B)

periodVariable&split=month 按区间出月包(实测 200,DailyTxT_2026-11.html 13423B)。⚠️ translations 必须含 7 元素 weekdays 数组,否则后端越界 panic(见 FAQ 7.5)。

importData(POST multipart):file=@备份.zip + encrypted + password。实测把 5.7 的 backup_plain.zip 导入第二个用户后,getLog 完整还原日记、标签与地图标记:

{"success":true}

{"date_written":"2026-11-01","files":[],"history_available":false,"pins":[{"id":1,"lat":31.2304,"lon":121.4737,"text":"上海"}],"tags":[1],"text":"# DailyTxT 实测\n端到端加密日记第一篇。"}

5.8 管理域(/api/admin/)

全部需登录态 + 请求体带 admin_password:

curl -s -b cookies.txt -X POST $B/api/admin/validate-password \
  -H 'Content-Type: application/json' -d '{"password":"q1w2e3r4"}'
{"valid":true}

错误密码返回 {"valid":false};无登录态则直接 401。get-data 返回运行时配置与每用户磁盘占用(配置篇 3.2 实测回显)。open-registration 临时开放注册(实测 60 秒档):

{"duration":60,"success":true,"until":"2026-11-01T11:22:01Z"}

delete-user(实测删除第二用户 {"success":true})、delete-old-data(无 v1 遗留数据时返回 Old directory does not exist)。

六、分页/过滤/排序通用约定

DailyTxT 的 API 没有分页与排序参数:日记天然按 year/month/day 定位(getLog 单日、loadMonthForReading 整月、statistics 全量逐日数组),列表端点(allPins、getTags、getTemplates、searchString)一次全量返回。过滤靠路径语义:搜索词(searchString/searchTag)、时间区间(exportData 的 startDate/endDate)。多用户隔离实测:第二个用户登录后 getLog 同一天返回全空 {"date_written":"","files":[],"pins":[],"tags":[],"text":""},无任何跨用户读取通道。

七、常见问题

7.1 一切正常却 401? 先查 cookie jar 域名匹配(第二节警告);再查 SECRET_TOKEN 是否变过(容器重建/变量未导出,配置篇 5.1)。

7.2 uploadFile 报 Missing uuid parameter? uuid 是 query 参数且由客户端生成(python3 -c "import uuid; print(uuid.uuid4())"),不在 multipart 表单里。

7.3 exportData 400 Invalid period parameter? period 只接受 periodAll / periodVariable(不是 all),且 split 与 translations 必填。

7.4 backupUser 免登录安全吗? 它用用户名+口令现派生密钥再打包,口令错误即失败(5.7 实测 500)。适合 cron,但意味着备份脚本里存有明文口令,建议配合文件权限 600 与目标机隔离。

7.5 exportData 返回 502 Bad Gateway? 实测 translations JSON 缺 weekdays(或不足 7 元素)触发后端 translations.Weekdays[weekday] 越界 panic;Go net/http 恢复 panic 后断开连接,nginx 报 502,进程不受影响(随后请求正常)。传入完整 weekdays 数组即恢复 200。

7.6 logout 后旧 cookie 还能用? 是(4.2 实测)。JWT 无状态,有效期 = LOGOUT_AFTER_DAYS。全局吊销请轮换 SECRET_TOKEN。

7.7 重复注册返回 500 而不是 409? 实测回显 Internal Server Error: username already exists(HTTP 500)。脚本化注册先调 isRegistrationAllowed 并容忍 500 视为已存在。

7.8 GET /api/version 超时? 后端实时访问 Docker Hub(15s 超时策略),出网受限环境注入 HTTPS_PROXY 或关闭用户设置 checkForUpdates(部署篇 FAQ 11.1 实测修复)。

八、参考链接

DailyTxT 2.6.4 API 使用详解 - Quzuo