dayGLANCE 5.9.0 API 使用详解

dayGLANCE 5.9.0 自托管实例 HTTP 接口全解:WebDAV 同步代理与日历订阅代理两个端点的路由、方法、鉴权透传、缓存策略、状态码与错误体,全部 curl 实测取证。

dayGLANCEdayGLANCEREST API2026/10/23

dayGLANCE 5.9.0 API 使用详解

目标:讲清 dayGLANCE 5.9.0 自托管实例对外暴露的全部 HTTP 接口——WebDAV 同步代理 /api/webdav-proxy/ 与日历订阅代理 /api/calendar-proxy/ 两个端点的路由规则、方法、请求头、鉴权透传、状态码与错误体,每个接口都给出可独立执行的 curl 请求与实测响应。dayGLANCE 无传统 REST 数据 API(数据全部存浏览器)、无 CLI 命令行工具,服务端接口就是这两个代理端点,因此本文按 API 使用篇撰写。全部命令与回显均在 Ubuntu 22.04.5 LTS + dayGLANCE 5.9.0 官方镜像实例实测取得。部署与配置背景见 《dayGLANCE 5.9.0 Docker Compose 部署指南》 与 《dayGLANCE 5.9.0 配置参数详解》。

一、环境信息

本文所有实测在这套环境取得:

  • 操作系统:Ubuntu 22.04.5 LTS(x86_64);客户端工具 curl 7.81.0
  • dayGLANCE:v5.9.0 官方镜像 ghcr.io/krelltunez/dayglance:latest(内置 Nginx 1.31.7 + Node.js 同步代理)
  • 实例 A:默认配置,接口地址 http://localhost:8131(放行私网目标)
  • 实例 B:WEBDAV_PROXY_BLOCK_PRIVATE=1 封锁对照实例,接口地址 http://localhost:8133
  • 测试目标 1(静态):http://10.89.59.2——返回固定文本 fixture-for-webdav-proxy 的静态目标
  • 测试目标 2(WebDAV 行为 + 鉴权):http://10.89.59.4:8085——支持 GET/PUT/DELETE/MKCOL/PROPFIND 的测试服务,Basic 认证账号 admin / q1w2e3r4(Base64:YWRtaW46cTF3MmUzcjQ=),受保护路径未带凭据返回 401,/echo-auth 端点回显收到的 Authorization 头
  • 接口源码定位:/app/docker/proxy-server.mjs(路由与转发)、/app/api/_ssrfGuard.mjs(目标网段守卫),2026-10-23 自运行实例取出

💡 说明:这套接口的用途非常聚焦——浏览器里的 dayGLANCE 受同源策略限制,无法直接读写你 NAS 上的 WebDAV 或日历服务器的 ICS;实例内置的这两个代理端点就是替浏览器"跑腿"的中间层。理解它们,你既能排查同步故障,也能用脚本验证同步链路,甚至让其他工具复用这条通道。

二、前置条件

  1. 一个已部署的 dayGLANCE 5.9.0 实例(部署方法见 《dayGLANCE 5.9.0 Docker Compose 部署指南》);
  2. 任意 HTTP 客户端(本文统一用 curl;浏览器 DevTools 的 Network 面板亦可观察应用自身对接口的调用);
  3. 若要复现鉴权透传与 WebDAV 方法族的示例,需要一个可访问的 WebDAV/日历测试目标(本文用一台带 Basic 认证的最小测试服务充当,地址 http://10.89.59.4:8085;读者可换成自己的 NAS WebDAV,把示例中的 URL 与凭据换成实际值即可);
  4. 目标地址在请求中必须做 URL 编码(如 http:// 编码为 http%3A%2F%2F),本文示例均已编码,可直接复制执行。

三、API 总览

项约定(源码与实测口径)
协议HTTP/1.1,明文;生产公网建议前置 HTTPS 反向代理
版本化无 /v1 类版本前缀;接口随镜像版本演进,v5.9.0 实测口径即本文
端点/api/webdav-proxy/(WebDAV 同步转发)、/api/calendar-proxy/(日历订阅转发)
认证接口本身无认证(无登录态、无 API Key);上游服务的凭据经请求头透传(见第四章)
限流无(源码明确无 rate limiting);公网暴露时靠反向代理层做访问控制
请求参数目标地址固定放查询参数 ?url=(URL 编码),无请求体格式约束(方法语义由目标端决定)
成功响应原样中继目标端的状态码、Content-Type、ETag 等响应头与响应体
错误响应代理自身错误统一 JSON:{"error":"<原因>"};目标端错误原样中继
浏览器同源接口由实例自身域名/端口提供,应用前端直调不存在跨域问题

关键差异解读:这两个端点是转发代理而非数据接口——它们不理解 dayGLANCE 的任务数据格式,只负责把浏览器发出的请求送到目标服务器并把响应带回。数据格式(任务 JSON、ICS 日历)完全由应用与目标端约定,对接口使用者透明。

四、认证与首次调用

先建立直觉:接口无平台级认证,任何能访问实例端口的人都能调用(这正是"不要裸暴露公网"的原因);要访问有认证的目标,把标准 Authorization 头的值放进 X-WebDAV-Auth(WebDAV 域)或 X-Calendar-Auth(日历域),代理会原样转成目标请求的 Authorization 头。

首次调用——读取一个无认证目标(实测):

curl -s -w "\n[%{http_code}]\n" "http://localhost:8131/api/webdav-proxy/?url=http%3A%2F%2F10.89.59.2%2Fhello.txt"
fixture-for-webdav-proxy

[200]

带认证目标的完整三连(实测)。第一步,不带凭据访问受保护路径——目标端的 401 原样中继:

curl -s -w "\n[%{http_code}]\n" "http://localhost:8131/api/webdav-proxy/?url=http%3A%2F%2F10.89.59.4%3A8085%2Fprotected%2Fhello.txt"
401 Unauthorized

[401]

第二步,把 Authorization: Basic YWRtaW46cTF3MmUzcjQ= 的值放进 X-WebDAV-Auth 头重试:

curl -s -w "\n[%{http_code}]\n" -H 'X-WebDAV-Auth: Basic YWRtaW46cTF3MmUzcjQ=' "http://localhost:8131/api/webdav-proxy/?url=http%3A%2F%2F10.89.59.4%3A8085%2Fprotected%2Fhello.txt"
secret behind auth
[200]

第三步,用测试目标的 /echo-auth 端点验证服务端实际收到的头——确认透传的就是标准 Authorization:

curl -s -H 'X-WebDAV-Auth: Basic YWRtaW46cTF3MmUzcjQ=' "http://localhost:8131/api/webdav-proxy/?url=http%3A%2F%2F10.89.59.4%3A8085%2Fecho-auth"
{"received":"Basic YWRtaW46cTF3MmUzcjQ="}

💡 技术拓展:为什么浏览器需要这条代理通道——跨域请求的放行机制对比:

机制原理取舍
目标端开 CORS目标服务器返回 Access-Control-Allow-Origin 放行要求 NAS/日历服务逐个配置,多数私有部署默认不开
同源代理转发(本方案)请求先发到同源的反代端点,由服务端转发目标端零配置;代理成为安全边界
JSONP借 <script> 标签绕过同源仅限 GET,已被淘汰

dayGLANCE 采用同源代理转发,且在代理响应里也声明了完整 CORS 头(Access-Control-Allow-Origin: * 等,实测见 5.1 节 OPTIONS),让应用在 PWA 与桌面端间迁移时行为一致。

五、各 API 域详解

5.1 WebDAV 同步代理域:/api/webdav-proxy/

应用云同步(Settings → Cloud Sync)的全部读写都走这个端点。请求参数与请求头:

参数/请求头必填说明
url(查询参数)是目标地址,URL 编码;缺失返回 400
请求方法是GET/PUT/DELETE/MKCOL/PROPFIND/OPTIONS 是 WebDAV 声明集;其他方法原样转发、由目标端裁决(实测 POST 被目标判 405)
X-WebDAV-Auth否值原样转成目标请求的 Authorization 头
DepthPROPFIND 建议原样透传(0 自身 / 1 一层 / infinity 全树)
If-Match / If-None-Match否条件请求头,原样透传,配合目标端 ETag 做乐观并发
Content-TypePUT 等带体方法原样透传(未带时默认 application/octet-stream)

GET 读取(目标不存在时 404 由目标端产生,原样中继):

curl -s -w "\n[%{http_code}]\n" "http://localhost:8131/api/webdav-proxy/?url=http%3A%2F%2F10.89.59.4%3A8085%2Fnot-exist.txt"
404 Not Found

[404]

PUT 写入——实测返回目标端的 201 与 ETag(同步协议用 ETag 做变更检测),并带代理的 Cache-Control: no-store(同步数据绝不缓存):

curl -s -D - -o /dev/null -X PUT --data-binary 'etag-check' "http://localhost:8131/api/webdav-proxy/?url=http%3A%2F%2F10.89.59.4%3A8085%2Fetag.txt" | grep -iE 'HTTP|etag|cache-control'
HTTP/1.1 201 Created
Cache-Control: no-store
ETag: "dav-test-etag-1"

写后读回验证(实测):

curl -s -w "\n[%{http_code}]\n" "http://localhost:8131/api/webdav-proxy/?url=http%3A%2F%2F10.89.59.4%3A8085%2Fd5.txt"
d5-write-test
[200]

MKCOL 建目录(实测 201):

curl -s -w "[%{http_code}]\n" -X MKCOL "http://localhost:8131/api/webdav-proxy/?url=http%3A%2F%2F10.89.59.4%3A8085%2Fd5dir%2F"
[201]

PROPFIND 列目录——带 Depth: 1 请求,实测返回目标端的 207 Multi-Status XML(输出节选,实测首部):

curl -s -X PROPFIND -H 'Depth: 1' "http://localhost:8131/api/webdav-proxy/?url=http%3A%2F%2F10.89.59.4%3A8085%2F" | head -4
<?xml version="1.0" encoding="utf-8"?>
<D:multistatus xmlns:D="DAV:" Depth="1">
  <D:response><D:href>/hello.txt</D:href><D:propstat><D:prop><D:getcontentlength>24</D:getcontentlength></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>
  <D:response><D:href>/protected/hello.txt</D:href><D:propstat><D:prop><D:getcontentlength>18</D:getcontentlength></D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>

OPTIONS 预检——实测返回 204 与全套 CORS 声明(同时叠加了实例 Nginx 的安全响应头):

curl -s -i -X OPTIONS "http://localhost:8131/api/webdav-proxy/?url=http%3A%2F%2F10.89.59.2%2Fhello.txt" | head -10
HTTP/1.1 204 No Content
Server: nginx/1.31.7
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, PUT, DELETE, MKCOL, PROPFIND, OPTIONS
Access-Control-Allow-Headers: Authorization, X-WebDAV-Auth, Content-Type, Depth, If-Match, If-None-Match
Access-Control-Max-Age: 86400
X-Frame-Options: SAMEORIGIN
X-Content-Type-Options: nosniff

DELETE 删除——存在返回 204,不存在返回 404(均实测):

curl -s -w "[%{http_code}]\n" -X DELETE "http://localhost:8131/api/webdav-proxy/?url=http%3A%2F%2F10.89.59.4%3A8085%2Fd5.txt"
curl -s -w "[%{http_code}]\n" -X DELETE "http://localhost:8131/api/webdav-proxy/?url=http%3A%2F%2F10.89.59.4%3A8085%2Fd5.txt"
[204]
[404]

该域错误码汇总(全部实测):

状态码触发条件实测响应体
400url 参数缺失{"error":"Missing url parameter"}
403目标命中网段策略(保留网段一律拒;私网在封锁模式下拒){"error":"Private/reserved addresses are not allowed"}
404代理路径不在两个端点白名单内(如 /api/foo)空 body
413请求体超过实例 Nginx 默认 1 MBNginx HTML 错误页
502目标不可达/连接失败{"error":"Failed to proxy WebDAV request"}
上游状态码目标端返回的 200/201/204/207/401/404/405 等原样中继

502 的实测(目标地址无服务):

curl -s -w "\n[%{http_code}]\n" "http://localhost:8131/api/webdav-proxy/?url=http%3A%2F%2F10.89.58.99%2Fhello.txt"
{"error":"Failed to proxy WebDAV request"}
[502]

5.2 日历订阅代理域:/api/calendar-proxy/

应用的日历订阅(ICS 订阅导入)走这个端点。与 WebDAV 域的差异:只处理 GET;不透传 Content-Type 等写请求头,仅声明 Accept: text/calendar, text/plain, */*;凭据走 X-Calendar-Auth。

基本读取(实测):

curl -s -w "\n[%{http_code}]\n" "http://localhost:8131/api/calendar-proxy/?url=http%3A%2F%2F10.89.59.2%2Fhello.txt"
fixture-for-webdav-proxy

[200]

带认证的日历读取——不带凭据 401,带 X-Calendar-Auth 拿到 ICS 内容(实测,ICS 输出完整照抄):

curl -s -w "\n[%{http_code}]\n" -H 'X-Calendar-Auth: Basic YWRtaW46cTF3MmUzcjQ=' "http://localhost:8131/api/calendar-proxy/?url=http%3A%2F%2F10.89.59.4%3A8085%2Fprivate.ics"
BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//AuthTest//EN
BEGIN:VEVENT
UID:auth-1@t
DTSTART:20261023T120000Z
SUMMARY:Private ICS via auth
END:VEVENT
END:VCALENDAR

[200]

缓存策略(本域特色,实测响应头):日历内容变化频率低,代理给响应加了 15 分钟新鲜窗口 + 60 秒过期兜底,减少对日历服务器的轮询:

curl -s -I "http://localhost:8131/api/calendar-proxy/?url=http%3A%2F%2F10.89.59.2%2Fhello.txt" | grep -iE 'HTTP|cache-control'
HTTP/1.1 200 OK
Cache-Control: public, max-age=900, stale-while-revalidate=60

网段策略与本域同样生效——封锁实例上订阅私网日历被 403(实测,对照实例 8133):

curl -s -w "\n[%{http_code}]\n" "http://localhost:8133/api/calendar-proxy/?url=http%3A%2F%2F10.89.59.2%2Fhello.txt"
{"error":"Private/reserved addresses are not allowed"}
[403]

5.3 应用侧集成视角

以上接口对应用是全自动使用的:在 Settings → Cloud Sync 填入 WebDAV/Nextcloud 地址与凭据后,应用把同步请求组装为对 /api/webdav-proxy/ 的调用(凭据自动放进 X-WebDAV-Auth);日历订阅同理走 /api/calendar-proxy/。无需手工调用接口即可完成同步;本文的 curl 示例价值在于验证链路与排障定位——同步失败时,按 5.1 节错误码表逐个复现即可定位是代理层(400/403/404/413/502)还是目标端(401/404/405 等)的问题。

六、通用约定

  • 无分页/过滤/排序:这两个端点是转发代理,没有列表型数据接口,因此不存在分页、过滤、排序参数;目录枚举需求由 WebDAV 的 PROPFIND + Depth 头承担(见 5.1 节实测)。
  • URL 编码强制:url 参数值必须整体 URL 编码(http:// → http%3A%2F%2F);未编码的 :// 会破坏查询串解析。
  • 错误体统一:代理自身的拒绝(400/403/502)一律是单键 JSON {"error":"..."};目标端响应原样中继,不再包装。
  • 请求体上限:受实例 Nginx 默认 1 MB 限制(413),大附件场景需在自建形态放开(见 《dayGLANCE 5.9.0 配置参数详解》 5.2 节实测)。

七、常见问题

Q1:直接从浏览器脚本 fetch 别的域名的 WebDAV 为什么不行,走代理就行? 浏览器同源策略默认拦截跨源响应,除非目标端返回放行的 CORS 头;多数 NAS/自建 WebDAV 默认不放行。代理端点与页面同源,服务端转发不受浏览器限制(机制对比见第四章技术拓展)。

Q2:接口需要 token 吗?为什么抓包看不到认证头? 接口本身无认证;对有认证的目标,应用把凭据放在 X-WebDAV-Auth/X-Calendar-Auth 请求头里,由代理转成目标端的标准 Authorization(三连实测见第四章)。对无认证目标(如局域网开放读的日历)自然看不到任何凭据头。

Q3:同步报 Failed to proxy WebDAV request(502)怎么排? 代理已收到请求但连不上目标:按 5.1 节错误码表核对目标地址与端口可达性;若目标在私网且实例开了封锁模式,会先得到 403 而非 502。

Q4:代理响应里的 Server: nginx/1.31.7 是目标端的吗? 不一定是——实例内置 Nginx 会把自己的安全响应头叠加在代理响应上(Server 头属实例 Nginx);目标端自身的响应头(如 ETag、Content-Type)原样透传。判断错误来自哪一层:JSON {"error":...} 是代理层,HTML 错误页多为目标端或 Nginx 拦截(413),其余状态码看语义对照表。

Q5:能让第三方工具(如 Obsidian 插件、脚本备份)复用这条代理吗? 技术上可以(端点就是标准 HTTP 转发),但务必注意它对实例访问者是无鉴权的——仅建议在受信任网络内使用,公网实例前置认证反代(部署口径见 《dayGLANCE 5.9.0 Docker Compose 部署指南》 常见问题 Q3)。

八、参考链接