Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

API 参考

概述

基础 URL:http://<address>:<port>/(默认 0.0.0.0:10100)。

认证方式:令牌通过 ?token= 查询参数或 Authorization: Bearer <token> 请求头传递(查询参数优先)。令牌按作用域分组:

  • Read — 可访问只读渲染端点。
  • Admin — 可访问全部端点(Admin 令牌同时满足 Read 要求)。

所有受保护的端点都位于 /app/ 命名空间下。未认证或作用域不足的请求返回 401 Unauthorized

中间件

每个请求都会经过以下中间件栈(自外向内):

中间件行为
超时请求超过 9 秒返回 504 Gateway Timeout
追踪HTTP 请求追踪(TraceLayer
压缩透明负载压缩(CompressionLayer
请求体限制超过 10 MB 返回 413 Payload Too Large
请求追踪器为每个请求添加 x-request-id 响应头并记录日志
指标为每个请求记录 Prometheus 指标
CORS可配置的来源(setting.cors_origins,默认 ["*"]);所有响应附带 Access-Control-Allow-* 与 HSTS 头。来源列表每请求从 live config 读取,修改 cors_origins 后热重载立即生效
速率限制按 IP 限流(默认每 60 秒 600 次)+ 并发限制(默认 600),超限返回 429 Too Many Requests(带 Retry-After 头)
内容类型校验POST/PUT/PATCH 必须携带 Content-Type: application/jsonapplication/x-www-form-urlencoded,缺失返回 400,类型不符返回 415
认证按路由作用域校验令牌(Read/Admin)

公开端点

以下端点无需认证。

GET /

健康检查。返回纯文本问候信息。

响应200 OK

Hello world!

GET /v

版本信息。

响应200 OK

thurgio 0.18.0 (git-describe 快照)

GET /health

以 JSON 格式返回详细的系统健康状态。

响应200 OK

{
  "status": "healthy",
  "version": "thurgio 0.18.0 (...)",
  "database": "connected",
  "memory_usage_mb": 42,
  "cpu_cores": 8,
  "timestamp": "2025-01-01T00:00:00Z"
}

字段说明

字段类型描述
statusstring始终为 "healthy"
versionstring名称 + 版本号 + git 快照
databasestring始终为 "connected"
memory_usage_mbintegerRSS 内存(MB,来自 /proc/self/statusVmRSS
cpu_coresinteger可用并行度
timestampstringRFC 3339 时间戳(UTC)

GET /ready

就绪探针——检查 SQLite 数据库是否可读。

响应 200 OK(数据库可读):

{
  "status": "ready",
  "database": "connected"
}

响应 503 Service Unavailable(数据库不可读):

{
  "status": "not_ready",
  "database": "disconnected"
}

GET /metrics

Prometheus 格式的指标数据,包含请求指标、熔断器快照、渲染线程池指标(renderer_active_threadsrenderer_backlog_smoothedrenderer_threads_spawned_totalrenderer_threads_exited_totalrenderer_slots_total)与内存占用。

响应200 OK

纯文本 Prometheus 暴露格式。

GET /favicon.svg / GET /favicon-dark.svg

编译期嵌入的站点图标(crates/thurgio-server/assets/favicon.svgfavicon-dark.svg)。登录页与 Dashboard 通过 <link rel="icon" media="(prefers-color-scheme: ...)"> 按系统配色自动切换亮/暗图标。

响应200 OK,内容类型 image/svg+xml

GET /icon-app.svg

编译期嵌入的渐变 App 图标(crates/thurgio-server/assets/icon-app.svg),Dashboard 头部 brand 标记使用。

响应200 OK,内容类型 image/svg+xml

GET /app/login / POST /app/login

令牌登录页与校验接口(无需认证)。

  • GET 返回登录页 HTML。
  • POST 接收 JSON {"token": "..."},校验令牌作用域:

响应 200 OK(有效令牌):

{ "ok": true, "scope": "admin" }

scope 为 "admin""read"

响应 400 Bad Request(缺少 token 字段):

{ "ok": false, "error": "missing token" }

响应 401 Unauthorized(令牌无效):

{ "ok": false, "error": "invalid token" }

只读端点(Read/Admin 令牌)

以下端点接受 Read 或 Admin 作用域令牌。

渲染 Provider

GET /app/read/l?name={name}

按名称渲染 provider(name 为必填查询参数)。渲染前会按 TTL 检查并更新数据。

查询参数

参数类型描述
namestring必填——要渲染的 provider 名称(缺失或为空 → 400)
dstring可选——设置 Content-Disposition: attachment; filename="..."(为空时使用 name
tokenstring认证令牌(也可通过 Authorization: Bearer 头传递)
(任意)string其他查询参数将作为渲染参数(hp)传入

请求级规则覆盖(ADR-061,仅 provider 端点,不写入配置):

参数语义
pre_rename / filter / rename覆盖配置中对应阶段(逗号分隔的规则名);空值 = 清空该阶段
ap_pre_rename / ap_filter / ap_rename追加到配置对应阶段规则之后
emoji覆盖 emoji 策略(enable/disable/hide;非法值 → 400)
udp覆盖 udp 策略(none/false/try/true;非法值 → 400)
tpl覆盖本次渲染模板(空值回退 provider template

同一阶段同时传普通与 ap_ 参数(如 rename= + ap_rename=)→ 400;未知规则名静默忽略;带规则参数时从原始响应内容重新解析(仅 tpl 时复用缓存)。snippet(/app/read/r)与 adhoc 遮蔽命中时不支持规则覆盖。

# 覆盖 filter:本次请求只保留 us + vmess 规则命中的节点
curl ".../app/read/l?name=my-provider&token=...&filter=us,vmess"
# 追加规则 / 清空过滤
curl ".../app/read/l?name=my-provider&token=...&ap_filter=vmess"
curl ".../app/read/l?name=my-provider&token=...&filter="
# 切换模板
curl ".../app/read/l?name=my-provider&token=...&tpl=clash.tpl"

响应:渲染后的 provider 内容,并转发提供者响应的 subscription-userinfo 头。

渲染 Snippet

GET /app/read/r?name={name}

按名称渲染 snippet(name 为必填查询参数)。

查询参数:与 /app/read/l?name= 相同(但不支持请求级规则覆盖)。

响应:渲染后的 snippet 内容,并转发请求携带的 subscription-userinfo 头。

渲染 UI

GET /app/read/ui

后台触发一次更新,然后以 HTML 形式渲染 ui.tpl 模板。上下文注入当前令牌的 scope 变量("admin"/"read")。

响应200 OK,内容类型为 text/html

渲染上下文

GET /app/read/c

以 JSON 格式渲染完整的 Tera 上下文(configphpheadersversion 等),同样注入 scope 变量;config 内的 proxies/content 字段已剔除(dashboard 只消费资源键名)。

响应200 OK,内容类型为 application/json

构建通知(SSE)

GET /app/read/events

Server-Sent Events 流。每次构建完成推送 build_complete 事件,每 15 秒发送一次 keep-alive 心跳。

响应200 OKtext/event-stream

订阅健康度

GET /app/read/health

订阅健康度聚合:为每个 provider/snippet 报告熔断器状态(closed/open/half-open)、失败计数、最后失败时间(Unix 秒)与最后成功更新时间(RFC 3339),并汇总为总体状态。数据源为熔断器池快照与 DB 中持久化的抓取时间戳,不触发更新。

响应200 OK

{
  "overall": "degraded",
  "summary": { "total": 3, "closed": 2, "open": 1, "half_open": 0 },
  "items": [
    {
      "key": "provider:example",
      "name": "example",
      "type": "provider",
      "breaker": "open",
      "failure_count": 5,
      "last_failure_at": 1755160000,
      "last_update_at": "2026-08-14T12:00:00Z"
    }
  ]
}

overall 取值:healthy(无 open/half-open)、degraded(存在但未全部 open)、unhealthy(全部 open);空配置视为 healthy。未抓取过的 item 两个时间字段均为 null

订阅商店

GET /app/read/subs

聚合列出全部配置订阅。provider 在前(声明顺序),带解析后节点数(proxies)与流量元数据(来自 subscription-userinfo 头,无该头或不可解析时各字段为 null);snippet 在后(无节点数与元数据字段)。adhoc 临时资源不列出。

响应200 OK

{
  "count": 2,
  "items": [
    {
      "name": "example",
      "kind": "provider",
      "proxies": 42,
      "upload": 1073741824,
      "download": 10737418240,
      "total": 107374182400,
      "used": 11811160064,
      "expire": 1893456000,
      "expire_iso": "2030-01-01T00:00:00Z"
    },
    { "name": "ruleset", "kind": "snippet", "proxies": null, "upload": null, "download": null, "total": null, "used": null, "expire": null, "expire_iso": null }
  ]
}

除数据就绪门卫外不触发抓取,纯只读聚合。

订阅元数据

GET /app/read/meta?name={name}

报告单个 provider 的流量/配额元数据,解析其缓存响应中的 subscription-userinfo 头。

查询参数

参数类型描述
namestring必填——provider 名

响应200 OK

{
  "name": "example",
  "upload": 1073741824,
  "download": 10737418240,
  "total": 107374182400,
  "used": 11811160064,
  "expire": 1893456000,
  "expire_iso": "2030-01-01T00:00:00Z"
}
字段类型描述
usedinteger | null已用流量(upload + download),两者均未知时为 null
expire_isostring | null到期时间的 UTC RFC 3339 形式
其余integer | null上传/下载/总量/到期 Unix 秒,未知时为 null

仅支持 provider 名:snippet 或 adhoc 同名 → 400;未知 → 404;响应无该头或无可解析字段 → 404

Provider 历史序列

GET /app/read/history?name={name}&limit={n}

报告单个 provider 持久化的快照时间序列——每次成功同步上游记录一个点(时间戳、节点数、当时的流量元数据),按时间升序。只读 KV 持久层:无就绪门卫、不触发网络;从未同步过的 provider 返回空列表。

查询参数

参数类型描述
namestring必填——provider 名
limitinteger可选——只保留最近 N 个快照(结果仍升序)

响应200 OK

{
  "name": "example",
  "snapshots": [
    {
      "ts": 1755153600,
      "proxies": 40,
      "upload": null, "download": null, "total": null, "expire": null
    },
    {
      "ts": 1755160000,
      "proxies": 42,
      "upload": 1073741824, "download": 10737418240, "total": 107374182400, "expire": 1893456000
    }
  ]
}

provider-only 策略与 /app/read/meta 一致:snippet/adhoc 同名 → 400;未知 → 404

历史快照对比

GET /app/read/history/diff?name={name}&from={a}&to={b}

对比同一 provider 时间序列中的两个快照。from/to 先按快照时间戳精确匹配,匹配不到再作为 0 基序列索引解析;非数字、无法定位、或 from 新于 to 均 → 400

响应200 OK

{
  "name": "example",
  "from": { "ts": 1755153600, "proxies": 40, "...": "..." },
  "to":   { "ts": 1755160000, "proxies": 42, "...": "..." },
  "proxies_delta": 2
}

快照只持久化聚合约(时间戳/节点数/流量元数据),不含逐节点明细,因此没有节点级 added/removed 分解——需要时用 CLI 的 thurgio diff nodes 对比原始订阅源。

延迟测试

GET /app/read/latency?name={name}&timeout_ms={ms}&retry={n}

对单个 provider 的全部已解析代理做 TCP 连接延迟测试。结果按次计算、永不缓存(延迟是时效性数据)。代理列表来自已解析的代理库(与渲染缓存同源),不重新解析原始内容。

查询参数

参数类型缺省描述
namestring必填——provider 名
timeout_msinteger3000单次探测超时(钳制到 [100, 10000]
retryinteger1每个代理重试次数(钳制到 [1, 3]

并发度取 setting.concurrency_limit

响应200 OK(结果按延迟升序,不可达代理排在最后)

{
  "name": "example",
  "count": 2,
  "results": [
    { "proxy_name": "HK-01", "alive": true, "latency_ms": 45 },
    { "proxy_name": "US-01", "alive": false, "latency_ms": null }
  ]
}

UDP 能力

GET /app/read/udp?name={name}

逐代理报告 UDP 转发生效标志。这是订阅元数据的静态能力报告(非实时探测):标志在解析期应用全局 setting.udp 策略后得出——源条目省略 udp 字段但策略为 Try 时可能报 true

查询参数name(必填,provider 名)。

响应200 OK(按 provider 内顺序)

{
  "name": "example",
  "count": 2,
  "results": [
    { "proxy_name": "HK-01", "udp": true },
    { "proxy_name": "US-01", "udp": false }
  ]
}

入口落地检测

GET /app/read/geo?name={name}&timeout_ms={ms}

把每个已解析代理的 server 主机做 DNS 解析,并经 ip-api 批量 GeoIP 查询国家/地区。

查询参数

参数类型缺省描述
namestring必填——provider 名
timeout_msinteger5000单主机解析超时(钳制到 [100, 10000]

并发解析上限 8。单主机失败不传播错误——对应条目字段置 null

响应200 OK(按 provider 内顺序,重复 server 保留)

{
  "name": "example",
  "count": 2,
  "results": [
    { "server": "hk.example.com", "ip": "103.1.2.3", "country": "Hong Kong", "country_code": "HK" },
    { "server": "us.example.com", "ip": null, "country": null, "country_code": null }
  ]
}

流媒体解锁探测

GET /app/read/unlock?services={csv}&timeout_ms={ms}&proxy={url}

探测流媒体服务在当前出口网络的可用性与地区。

查询参数

参数类型缺省描述
servicescsv全部三项要探测的服务:netflixyoutubedisney(别名 disneyplus);未知值 → 400
timeout_msinteger5000单服务探测超时(钳制到 [100, 10000]
proxyURL本次请求使用的一次性上游代理(http(s)://socks5://),非法格式 → 400

出口语义:探测的是 thurgio 进程自身的出口网络(或 proxy= 指定的上游)——不是逐节点的出口。逐节点测试需要内嵌代理核心,超出 v1 范围。并发探测上限 3。

响应200 OK(按请求顺序)

{
  "results": [
    { "service": "netflix", "available": true, "region": "JP", "latency_ms": 230 },
    { "service": "youtube", "available": false, "region": null, "latency_ms": 180 },
    { "service": "disney", "available": false, "region": null, "latency_ms": null, "error": "timeout" }
  ]
}

被拦截的 HTTP 状态归类为“不可用“而非错误;只有网络错误/超时才填 error 字段。


管理端点(仅 Admin 令牌)

以下端点仅接受 Admin 作用域令牌。

设置参数

GET /app/admin/p

通过查询字符串设置渲染参数,与内存中的配置参数深度合并(点分键如 dns.enabled 会写入嵌套结构)。

查询参数:任意键值对——每个都成为一个渲染参数。

响应:空内容,200 OK

设置提供者源 URL

GET /app/admin/s

动态更新提供者的源 URL。不使用查询参数——依赖于请求头。

必需请求头

请求头
prov要更新的提供者名称
url提供者的新源 URL

更新后会自动重置该提供者的熔断器并触发一次强制更新。

响应200 OK

{ "status": "ok", "provider": "<name>", "source": "<url>" }

响应 400 Bad Request(缺少 provurl 头);404 Not Found(提供者不存在)。

构建并写入输出

GET /app/admin/write

完整构建流程:更新所有提供者,渲染所有 provider/snippet,并将输出文件写入磁盘。

响应:空内容,200 OK(失败时返回错误 JSON)。

强制刷新

GET /app/admin/refresh

强制更新所有提供者和 snippet。与 /app/admin/write 不同,此端点仅执行更新步骤,不写入输出。

响应:空内容,200 OK(失败时返回错误 JSON)。

重新加载模板引擎

GET /app/admin/reload

重新加载 Tera 模板引擎,拾取磁盘上模板文件的所有变更。

响应:空内容,200 OK

持久化到数据库

GET /app/admin/save

将所有当前 provider/snippet 响应持久化到数据库(未 fetch 过的 item 跳过,避免覆盖 last-known-good 缓存行)。

响应:空内容,200 OK

设置开发模式

GET /app/admin/dev?value={value}

启用或禁用开发模式(每次渲染前重新加载模板)。

查询参数

参数类型描述
valueboolean必填——truefalse(缺失或非法值 → 400)

响应:设置后的当前开发模式值的字符串——"true""false"

创建临时 Provider/Snippet

GET /app/admin/adhoc

创建一次性 provider/snippet(不写入配置文件),立即渲染返回;同名临时资源可经 /app/read/l/app/read/r 后续引用,且对配置中的同名正式项呈遮蔽效果。缓存持久化到 DB,重启后命中缓存即恢复;按 adhoc.max_items/adhoc.ttl_seconds 自动清理。

查询参数

参数类型必填描述
typestringprovider / snippet
urlstring数据源 URL
namestring名称,缺省自动生成 adhoc-<8 位 hex>
formatstringclash/meta/v2rayn/sip002/surge/auto,默认 auto(仅 provider 生效)
tplstring模板名;缺省回退 setting.adhoc.tpl.provider/tpl.snippet
pre_rename / filter / renamestring逗号分隔的规则名,按顺序应用(未知规则名静默忽略)
forcebooleantrue 忽略内存 + DB 缓存强制重新获取

响应:渲染后的内容,内容类型 text/plain,并携带 Content-Dispositiond 参数)与抓取响应的 subscription-userinfo 头。

备份配置到 GitHub Gist

GET /app/admin/gist?token={gh_pat}&description={text}

把进程工作目录(即 -r/--root 切换后的目录)下的声明配置 YAML 备份为 GitHub 私密 Gist。

查询参数

参数类型描述
tokenstring可选——GitHub PAT;缺省时依次回退 config params 的 gist.token、环境变量 GITHUB_TOKEN
descriptionstring可选——Gist 描述,默认 thurgio config backup

备份文件清单:thurgio.yaml / provider.yaml / params.yaml / snippet.yaml / filter.yaml / rename.yaml / ruleset.yaml 中实际存在的文件;一个都不存在 → 400。token 三个来源全空 → 400。v1 无状态,不持久化任何备份记录。

响应200 OK

{
  "id": "abc123...",
  "html_url": "https://gist.github.com/xxx/abc123",
  "files": ["thurgio.yaml", "provider.yaml"]
}

上游失败 / 非 2xx / 错误信封 → 502 并携带上游消息。

短链接生成

GET /app/admin/shorten?url={long}&service={name}

经配置的短链接服务缩短 URL。服务模板取自 params:?service= 命中 shortlink.<service> 键时用之,否则用 shortlink.default;模板必须包含 {url} 占位符(点路径深合并产生的嵌套 mapping 形态同样接受)。内置 TinyURL 预设。

# params.yaml 示例
shortlink:
  default: "https://tinyurl.com/api-create.php?url={url}"

查询参数

参数类型描述
urlstring必填——长链接,必须以 http:// / https:// 开头(否则 → 400
servicestring可选——params 中的服务模板名

响应200 OK

{ "short": "https://tinyurl.com/2p2xxxxx", "long": "https://example.com/very-long-url" }

未配置服务模板 → 400;上游请求失败或响应中提取不到短链 → 502


查询参数语义

渲染端点(l/r/p/ui/c)的查询参数:

  • token 用于认证,不会进入渲染参数。
  • 其余参数的值按 YAML 解析(true → 布尔、9090 → 数字、其他 → 字符串),作为 hp 注入模板(请求参数面,与配置参数面 p 分离)。
  • 键中包含 . 的参数(如 dns.enabled=true)会深度合并进嵌套结构(hp.dns.enabled)。
  • 单次请求参数超过 100 个会被拒绝(参数整体忽略并告警)。
  • d 参数额外触发 Content-Disposition: attachment; filename="<d>" 响应头(用于订阅下载场景)。

响应状态码

状态码含义
200 OK成功
400 Bad Request缺少必需请求头 / POST 缺少 Content-Type / 登录缺少 token
401 Unauthorized令牌无效或缺失(或作用域不足)
404 Not Found资源不存在(provider、snippet 等)
413 Payload Too Large请求体超过 10 MB 限制
415 Unsupported Media TypePOST/PUT/PATCH 的 Content-Type 不受支持
429 Too Many Requests超过速率限制或并发限制(带 Retry-After 头)
500 Internal Server Error服务器内部错误
503 Service Unavailable数据库未就绪(仅就绪探针)
504 Gateway Timeout请求处理超过 9 秒