dayGLANCE 5.9.0 API 使用详解
dayGLANCE 5.9.0 自托管实例 HTTP 接口全解:WebDAV 同步代理与日历订阅代理两个端点的路由、方法、鉴权透传、缓存策略、状态码与错误体,全部 curl 实测取证。
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;实例内置的这两个代理端点就是替浏览器"跑腿"的中间层。理解它们,你既能排查同步故障,也能用脚本验证同步链路,甚至让其他工具复用这条通道。
二、前置条件
- 一个已部署的 dayGLANCE 5.9.0 实例(部署方法见 《dayGLANCE 5.9.0 Docker Compose 部署指南》);
- 任意 HTTP 客户端(本文统一用 curl;浏览器 DevTools 的 Network 面板亦可观察应用自身对接口的调用);
- 若要复现鉴权透传与 WebDAV 方法族的示例,需要一个可访问的 WebDAV/日历测试目标(本文用一台带 Basic 认证的最小测试服务充当,地址
http://10.89.59.4:8085;读者可换成自己的 NAS WebDAV,把示例中的 URL 与凭据换成实际值即可); - 目标地址在请求中必须做 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 头 |
Depth | PROPFIND 建议 | 原样透传(0 自身 / 1 一层 / infinity 全树) |
If-Match / If-None-Match | 否 | 条件请求头,原样透传,配合目标端 ETag 做乐观并发 |
Content-Type | PUT 等带体方法 | 原样透传(未带时默认 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]
该域错误码汇总(全部实测):
| 状态码 | 触发条件 | 实测响应体 |
|---|---|---|
| 400 | url 参数缺失 | {"error":"Missing url parameter"} |
| 403 | 目标命中网段策略(保留网段一律拒;私网在封锁模式下拒) | {"error":"Private/reserved addresses are not allowed"} |
| 404 | 代理路径不在两个端点白名单内(如 /api/foo) | 空 body |
| 413 | 请求体超过实例 Nginx 默认 1 MB | Nginx 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)。
八、参考链接
- 官方云同步文档(应用如何使用这些接口):https://docs.dayglance.app/en/cloud-sync
- 官方自托管文档:https://docs.dayglance.app/self-hosting
- 代理源码(转发逻辑):https://github.com/krelltunez/dayGLANCE/blob/main/docker/proxy-server.mjs
- 网段守卫源码(SSRF 策略):https://github.com/krelltunez/dayGLANCE/blob/main/api/_ssrfGuard.mjs
- 本专栏配套文章:软件定位与选型见 《dayGLANCE 5.9.0 介绍与选型指南》;非容器化安装见 《Ubuntu 22.04 安装 dayGLANCE 5.9.0 指南》;容器化部署见 《dayGLANCE 5.9.0 Docker Compose 部署指南》;配置参数与网段策略详解见 《dayGLANCE 5.9.0 配置参数详解》