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

模板示例

Thurgio 的核心扩展面是 Tera 模板:每个 provider / snippet 通过 template 字段指定一个模板,渲染上下文携带代理列表(l.proxies)、配置(config)、参数(p)等数据,配合内置过滤器即可产出任意格式的订阅(Clash / sing-box / V2RayN / Surge / 纯文本 / Base64…)。

本页索引 fixtures/examples/tpls/ 中的示例模板,并给出过滤器快速参考。按输出格式分类的精选索引(用途 / 依赖过滤器 / 适用客户端)见 fixtures/examples/README.md。模板语法详见配置指南

目录结构

fixtures/examples/
├── README.md         # 精选模板索引(按输出格式分类)
├── thurgio.yaml       # 主配置(setting)
├── provider.yaml      # provider 定义(模板引用在这里)
├── snippet.yaml       # snippet 定义
├── filter.yaml        # 过滤规则
├── rename.yaml        # 改名规则
├── params.yaml        # 渲染参数
├── tpls/              # 模板目录(THURGIO_TPL_DIR,默认 "tpls")
├── prov/              # 订阅源(本地文件)
├── rules/             # 规则集(snippet 源)
└── prod/              # build 输出

模板清单

以下模板全部默认接线provider.yamloutputssnippet.yaml 条目已引用它们,thurgio build 一次产出所有格式。

模板适用输出格式说明
base.tplproviderClash YAML最小模板(thurgio init 生成),json_str 逐节点输出
clash.tplproviderClash YAML紧凑 JSON 节点 + test 块 + rule-providers(遍历 config.snippetp.clash_url
clash_groups.tplproviderClash YAMLp.groups 规则名动态生成 proxy-groupsfilter(rules=...) 实时筛选成员)
clash_list.tplproviderClash YAMLclash.tpl 变体:头部注释打印 l.rename / l.filter
surge.tplproviderSurge 配置手写 [Proxy] 段(for 循环 + 字段展开,教学向)
surge_proxy.tplproviderSurge 配置surge_proxy 过滤器版 [Proxy] 段({{ l.proxies | surge_proxy() }}
surge_full.tplproviderSurge 完整配置[General] / [Proxy] / [Proxy Group] / [Rule] 四段齐全,可直接订阅导入(surge_proxy 节点行 + select/url-test 组 + p.rules 规则聚合,首行 #!MANAGED-CONFIG
v2rayn.tplprovider纯文本每行一个 vmess:// 分享链接(v2rayn 过滤器)
sip002.tplprovider纯文本每行一个 ss:// 分享链接(sip002 过滤器)
sip008.tplproviderJSONSS Android 官方订阅:version + servers 数组(sip008 过滤器,仅 ss 节点)
ssd.tplprovider纯文本ssd:// 订阅:UrlSafe Base64 JSON(ssd 过滤器,仅 ss 节点)
shadowrocket.tplprovider纯文本每行一个分享链接(shadowrocket 过滤器,复用 sip002/v2rayn 转换,wg 跳过)
base64_sub.tplproviderBase64机场式订阅:v2rayn + sip002 链接整体 base64(mode="encode") 编码
plain_list.tplprovider纯文本人类可读节点清单:名称 | 类型 | 服务器:端口(for 循环 + 字段访问)
singbox.tplprovidersing-box JSON完整 sing-box 配置:sibox 转 outbounds + direct/block
singbox_rules.tplsnippetsing-box JSONclash 规则文本 → sing-box rule 数组(clashbox 过滤器),与 singbox.tpl 组合成完整配置
qx.tplproviderQX 节点行每行一个 QX 节点(qx 过滤器,ss/ssr/vmess/vless/trojan/http/socks5)
qx_config.tplproviderQX 完整配置完整 Quantumult X 配置:[general]/[dns]/[server_local]/[policy]qx_config 过滤器)
loon.tplproviderLoon 节点行每行一个 Loon 节点(loon 过滤器,10 种类型)
loon_config.tplproviderLoon 完整配置完整 Loon 配置:[General]/[Proxy]/[Proxy Group]/[Rule]loon_config 过滤器)
mellow.tplproviderMellow JSONMellow/V2Ray-core JSON:socks inbound + per-proxy outbounds(mellow 过滤器,ss/vmess/vless/trojan/http/socks5 + TCP/WS/TLS)
mixed.tplprovider混合分享链接混合单链接列表:逐节点 V2RayN 优先、SIP002 回退,mixed 过滤器
rules_aggregate.tplsnippetQuantumult X 规则集多规则集聚合:拼接 config.snippet 中各规则集内容 → uniq 去重 → sort_domain_ip 排序 → quantumult 输出(s.p.rules 指定要聚合的 snippet 名,s.p.policy 指定策略);换 clash / surge 过滤器即得对应格式规则列表
snippet.tplsnippet原样原样输出 snippet 内容(s.response.content
rules_pipeline.tplprovider教学演示模板内 filter → rename → v2rayn/sip002/cla 过滤器链
date.tpl片段文本{% include %} 引入的生成时间注释
ui.tplHTMLDashboard 页面(服务器自带,勿删除)
auto/*.tplproviderUA 自适应tpl=auto 时按 User-Agent 选择的模板(auto/cla.tpl/surge.tpl/qx.tpl/loon.tpl/sibox.tpl/shadowrocket.tpl/v2rayn.tpl,见 examples/templates-auto/,复制到 tpls/auto/ 即用)

配置引用方式provider.yamltemplate: clash.tpl 即相对模板目录的模板名;outputs 下每个具名输出可指定不同模板,同一份代理列表渲染多种格式:

provider:
  example:
    type: clash
    source: "prov/example.yaml"
    template: clash_list.tpl
    filename:
      - prod/list/example.yaml
    outputs:
      base64_sub:
        template: base64_sub.tpl
        filename:
          - prod/example_base64.txt
      plain_list:
        template: plain_list.tpl
        filename:
          - prod/example_plain.txt

surge_full.tpl — Surge 完整配置

产出可直接被 Surge 客户端订阅的完整配置([General] / [Proxy] / [Proxy Group] / [Rule] 四段齐全)。节点行来自 surge_proxy 过滤器;[Proxy Group] 含一个 select 组(Proxy)与一个 url-test 自动测速组(Auto);[Rule] 段把 p.rules 指定的规则集 snippet 聚合后经 surge 过滤器输出,末尾补 FINAL,Proxy 兜底。模板不硬编码任何服务器信息,全部来自上下文变量。

参数放在 params.yaml(渲染上下文 p 绑定来自全局参数):

# params.yaml
rules:              # [Rule] 段聚合的 snippet 名(config.snippet 的键,缺省空)
  - China
rule_policy: Proxy  # [Rule] 默认策略名(缺省 Proxy)
test:               # url-test 测速参数(缺省 http://cp.cloudflare.com/generate_204 / 300)
  url: "http://cp.cloudflare.com/generate_204"
  interval: 300

provider.yamloutputs 中挂载(template / filename 字段):

provider:
  example:
    type: clash
    source: "prov/example.yaml"
    template: clash_list.tpl
    filename:
      - prod/list/example.yaml
    outputs:
      surge_full:
        name: example
        template: surge_full.tpl
        filename:
          - prod/example_surge_full.conf

首行 #!MANAGED-CONFIG 的拉取地址来自环境变量 SURGE_CONFIG_URL(未设置时输出占位 URL,需改成自己的订阅地址),Surge 客户端会按 interval=86400 定期更新。

[Proxy Group] 的节点名列表与 [Proxy] 段保持一致:仅列出 Surge 支持的 ss/trojan/vmess/snell/http/socks5/wireguard 七种类型(与 Surge::supports 能力谓词同步)——vless/ssr/hysteria/hysteria2/tuic/anytls 会被 surge_proxy 跳过,组内引用必须匹配,避免引用不存在的节点。

跑通示例

cd fixtures/examples
thurgio -r . build        # -r 是顶层选项,必须放在子命令前

或从仓库根目录:

cargo run -q -p thurgio-cli -- -r fixtures/examples build

构建成功后输出位于 fixtures/examples/prod/

输出模板格式
prod/list/example.yamlclash_list.tplClash
prod/example.yamlclash.tplClash
prod/example_groups.yamlclash_groups.tplClash(动态 proxy-groups)
prod/example_surge.txtsurge.tplSurge(手写 [Proxy] 段)
prod/example_surge_proxy.txtsurge_proxy.tplSurge(过滤器版 [Proxy] 段)
prod/list/example_v2rayn.txt / prod/example_v2rayn.txtv2rayn.tpl分享链接
prod/list/example_sip002.txt / prod/example_sip002.txtsip002.tpl分享链接
prod/example_sip008.jsonsip008.tplSS Android JSON 订阅
prod/example_ssd.txtssd.tplssd:// 订阅
prod/example_shadowrocket.txtshadowrocket.tplShadowrocket 分享链接
prod/example_base64.txtbase64_sub.tplBase64 订阅
prod/example_plain.txtplain_list.tpl纯文本节点清单
prod/example_singbox.jsonsingbox.tplsing-box 完整配置
prod/example_singbox_rules.jsonsingbox_rules.tplsing-box 规则数组
prod/example_china.listsnippet.tpl原样规则集
prod/example_quantumult.txtrules_aggregate.tplQuantumult X 规则集

挂载新模板:在 provider.yamloutputs(或 snippet.yaml 条目)中按 key 复制现有条目,改 template / filename 即可。注意 clash.tpl 会遍历所有 snippet 读取 p.clash_url,新 snippet 也要带该参数。

fixtures/examples 被集成测试锁定(ADR-060),修改声明文件后跑 cargo test -p thurgio-app --test render_examples 验证。

tpl=auto:按 User-Agent 自适应输出

服务端在 GET /app/read/l 收到 ?tpl=auto 时嗅探请求 User-Agent 选模板,按候选顺序取第一个存在于模板目录的 auto/<key>.tpl 渲染(对齐 subconverter 行为)。仅 provider 端点支持;snippet 与 /app/admin/adhoc 不支持。

工作原理:大小写不敏感子串匹配,先命中先赢;命中行的候选键按序探测文件存在性,首个存在的即为本次渲染模板。

UA 匹配表

行顺序即优先级(同时命中 clashv2rayclash 行先赢)。surge 行命中后会兜底追加默认顺序的剩余键;其余行仅尝试自身候选。

UA 片段(小写)候选键 → 模板文件说明
surgesurgeauto/surge.tpl,未命中则按默认顺序继续 cla / qx / loon / sibox / shadowrocket / v2raynSurge(含 Mac / iOS)
quantumultqxauto/qx.tplQuantumult X
loonloonauto/loon.tplLoon
clashclaauto/cla.tplclash / clash-verge / clashmeta / Clash for Windows 等
mihomoclaauto/cla.tplmihomo
stashclaauto/cla.tplStash
sing-boxsiboxauto/sibox.tplsing-box
singboxsiboxauto/sibox.tplsingbox(无连字符)
sfasiboxauto/sibox.tplsing-box Android 前端
sfisiboxauto/sibox.tplsing-box iOS 前端
sfmsiboxauto/sibox.tplsing-box macOS 前端
shadowrocketshadowrocketauto/shadowrocket.tplShadowrocket
v2rayv2raynauto/v2rayn.tplv2rayN / v2rayNG
(未知或缺失 UA)claauto/cla.tplsurgeqxloonsiboxshadowrocketv2rayn默认顺序,依次探测

默认顺序见 crates/thurgio-server/src/ua.rs:16DEFAULT_ORDER;匹配表见同文件 UA_TABLEcrates/thurgio-server/src/ua.rs:44)。

部署步骤

examples/templates-auto/ 提供可直接复制的最小可用模板集(cla.tpl / surge.tpl / qx.tpl / loon.tpl / sibox.tpl / shadowrocket.tpl / v2rayn.tpl):

mkdir -p tpls/auto
cp examples/templates-auto/*.tpl tpls/auto/
# 按需删改:不需要的客户端模板可直接删除,剩余的即为该 UA 的唯一候选

模板目录由 THURGIO_TPL_DIR 决定(默认 tpls),tpl=auto 的探测根固定为该目录下的 auto/ 子目录(crates/thurgio-server/src/ua.rs:29RENDERER_TPL_DIR)。

优先级与回退

  • 显式 ?tpl= 优先?tpl=auto 才走 UA 探测;?tpl=clash.tpl 等显式模板名直接透传,不做 UA 解析(crates/thurgio-server/src/ua.rs:115apply_auto_template 透传分支,见 crates/thurgio-server/tests/app/auto_tpl.rs:118)。?tpl= 为空时回退到 provider 配置的 template
  • 候选内回退:同一 UA 的候选列表内按序探测,首个存在的 auto/<key>.tpl 胜出(crates/thurgio-server/src/ua.rs:87resolve_auto_template);仅 surge 行会扩展为全默认顺序,其余行仅尝试自身键(crates/thurgio-server/src/ua.rs:66auto_template_candidates)。
  • 无匹配即 400:全部候选都不存在时返回 400 Bad Request,错误信息形如 no auto template exists (tried: auto/cla.tpl, auto/surge.tpl, ...) 并列出本次已尝试的全部模板名(crates/thurgio-server/src/ua.rs:130NoAutoTemplate,映射为 AppError::bad_requestcrates/thurgio-server/src/helpers/provider_request.rs:47)。

注意

provider.type 不能写 autoauto 仅用于渲染侧 ?tpl=auto 的 UA 自适应选择;输入格式仍需显式指定真实类型(clash / v2rayn / sip002 等),见配置指南配置参考

过滤器快速参考

完整说明见配置指南,此处给出最常用的调用形式:

输出过滤器(代理数组 → 目标格式)

过滤器参数示例
v2rayn{{ l.proxies | v2rayn() }}
sip002{{ l.proxies | sip002() }}
sip008{{ l.proxies | sip008() }} — SS Android JSON 订阅(仅 ss 节点)
shadowrocket{{ l.proxies | shadowrocket() }} — 每行一个分享链接(wg 跳过)
ssd{{ l.proxies | ssd() }}ssd:// 订阅(仅 ss 节点)
surge_proxy{{ l.proxies | surge_proxy() }} — Surge [Proxy] 段行(ss/trojan/vmess/snell/http/socks5/wireguard)
qx{{ l.proxies | qx() }} — Quantumult X 节点行(7 种类型)
qx_configcheck_url=, dns=, group={{ l.proxies | qx_config(group="Proxy") }} — 完整 QX 配置四段
loon{{ l.proxies | loon() }} — Loon 节点行(10 种类型)
loon_configdns=, group={{ l.proxies | loon_config(group="Proxy") }} — 完整 Loon 配置四段
mellowport=1080{{ l.proxies | mellow(port=1080) }} — Mellow/V2Ray-core JSON(socks inbound + outbounds)
mixed{{ l.proxies | mixed() }} — 混合单链接列表(V2RayN 优先、SIP002 回退)
stash{{ l.proxies | stash() }} — Stash(Clash 兼容)节点行
surfboard{{ l.proxies | surfboard() }} — Surfboard(Surge 兼容)节点行
clashr{{ l.proxies | clashr() }} — ClashR 节点行
sibox{{ l.proxies | sibox() | json_str(pretty=true) }} — sing-box outbound 对象数组
singbox{{ l.proxies | singbox() }} — sing-box 完整 JSON(别名 sibox)
clameta=true{{ l.proxies | cla(meta=true) | json_str() }} — 过滤出 clash 兼容节点
proxy_suffixsuffix="..."{{ l.proxies | proxy_suffix(suffix="HK") }} — 给节点名加后缀
jssrc="..."{{ l.proxies | js(src="return $input.map(p=>...)") }} — 内联 JS 变换(boa_engine)
sort_nodesby=name/region/latency, order=asc/desc, latencies={{ l.proxies | sort_nodes(by="latency", latencies=map) }}
clash_groupsgroups="a,b"{{ l.proxies | clash_groups() }} — Clash/Mihomo 策略组
surge_groups{{ l.proxies | surge_groups() }} — Surge 策略组

规则过滤器(模板内动态应用 filter.yaml / rename.yaml)

过滤器参数示例
filterrules="a,b"(或数组){{ l.proxies | filter(rules="hk") | v2rayn() }}
renamerules="a,b"(或数组){{ l.proxies | rename(rules="rm_brackets") | v2rayn() }}

未定义的规则名静默忽略;过滤器可链式组合,顺序即应用顺序。

规则文本过滤器(snippet 内容 / 规则集)

过滤器参数示例
clashpolicy=..., symbol=...{{ s.response.content | clash(policy="Proxy") }} — 规则文本 → clash 规则列表
surgepolicy=..., symbol=...{{ s.response.content | surge(policy="Proxy") }}
quantumultpolicy=..., symbol=...{{ s.response.content | quantumult(policy="Proxy") }} — 规则文本 → Quantumult X 规则集(host/host-suffix/host-keyword/ip-cidr/ip6-cidr/geoip/user-agent/final;无对应类型的规则丢弃)
clashboxpolicy=..., premium=..., meta=..., pretty=true{{ s.response.content | clashbox(policy="proxy") }} — clash 规则文本 → sing-box rule 对象
no_resolvevalue=true/false{{ s.response.content | no_resolve(value=true) }}
sort_domain_ip域名/IP 规则排序

通用过滤器

过滤器参数示例
base64mode="encode"/"decode"{{ lines | base64(mode="encode") }}
json_strpretty=false{{ i | json_str(pretty=false) }}
yaml_str{{ l.proxies | yaml_str() }}
indentwidth=2{{ body | indent(width=2) }}
uniq按行去重
clearnum=3, n=2压缩连续空行
slugify / urlencodeseparator="-"字符串处理

函数

函数参数示例
nowformat="[year]-[month]-[day]...", tz="+08:00", utc=true, timestamp=true{{ now() }}{{ now(timestamp=true) }}
get_envname="VAR", default="..."{{ get_env(name="HOME", default="") }}

js() 过滤器:内联 JavaScript 变换

js 是代理数组的内联变换过滤器,经 thurgio-scriptboa_engine 0.21)执行用户 JS 片段,适合在模板内完成 filter / rename 规则之外的临时加工与字段归一。

用法

{# 基础写法:src 内为完整 JS 函数体,$input 为本次输入 #}
{{ l.proxies | js(src="return $input.length;") }}

{# 带参数传递:模板变量可拼进 src,或经 tera 变量传入 #}
{% set script = "return $input.filter(p => p.type !== 'ss');" %}
{{ l.proxies | js(src=script) | v2rayn() }}

Tera 注册见 crates/thurgio-renderer/src/filters/js.rs:13js 函数(register_all 统一注册),调用为 src="..." 关键字参数;缺失 src 时渲染报错(同文件 js:14 的校验)。

$input 结构

$input 为本次过滤器输入的 JSON 镜像(JsValue::from_json 注入,crates/thurgio-script/src/engine.rs:55),在 provider 渲染中即 l.proxies 的代理对象数组。每个元素为 ProxyTy 的序列化形态(crates/thurgio-proxy/src/proxy/mod.rs:436Serialize for ProxyTy),常见顶层字段:

字段说明
name / type / server / port必有基础字段(与模板中 l.proxies[].name 等一致)
cipher / password / uuid / alterId协议相关字段(ss / vmess 等按类型出现)
tls / network / ws-opts / grpc-opts传输与 TLS 字段(vmess / vless 等按需出现)
udpUDP 覆盖标志(可选)

字段集合随代理类型不同而异,以实际序列化结果为准;模板中 {{ l.proxies | json_str(pretty=true) }} 可直观查看当前数据的可用字段。

输入与输出均经 serde_json::Value 往返(crates/thurgio-script/src/engine.rs:74to_json),未定义字段保留原样,返回结构可直接链给下游过滤器(如 | js(...) | v2rayn() / | cla() | json_str())。

约束

  • 必须显式 return:脚本体以 (function() { <src> })() 形式求值(crates/thurgio-script/src/engine.rs:66wrapped),undefined 结果视为错误 user script must return a value(同文件 engine.rs:71),缺失 return 会导致渲染失败(ErrorKind::Render,见 crates/thurgio-script/src/engine.rs:37crates/thurgio-script/src/engine.rs:178missing_return_is_err)。
  • boa_engine 沙箱:每次调用新建独立 Contextcrates/thurgio-script/src/engine.rs:53),$input 突变不外泄、不跨调用共享(同文件 engine.rs:187input_not_shared_between_calls)。默认上下文仅提供 ECMAScript 内置对象(Array / Object / JSON / Math 等),未挂载 fetch / 文件系统 / 进程等宿主能力,无网络与文件 I/O;但 v1 未提供执行超时与强沙箱隔离(crates/thurgio-script/src/lib.rs:11 的信任模型),脚本来自受信模板,勿直接执行不可信第三方脚本(design/crate-notes.md:44)。
  • 错误归类:语法错误、运行时 throw、输入/输出 JSON 转换失败均映射为 ErrorKind::Render 并以字符串透传给 Tera(crates/thurgio-script/src/engine.rs:67crates/thurgio-renderer/src/filters/js.rs:23),渲染管线以 422/500 形式返回。

实用示例

示例 1 — 过滤 + 改名(筛掉 ss、其余加前缀)

{# tpls/filtered.tpl — 仅保留非 ss 节点并统一加前缀 #}
{{ l.proxies | js(src="
  return $input
    .filter(p => p.type !== 'ss')
    .map(p => {
      p.name = '[FILTERED] ' + p.name;
      return p;
    });
") | v2rayn() }}

等价于组合 filter / rename 规则的内联写法,适合一次性、客户端特定的临时逻辑,无需在 filter.yaml / rename.yaml 新增规则。

示例 2 — 字段归一(兼容历史字段)

{# 将历史数据中的 legacy_port 归一到 port,归一后走 clash 输出 #}
{{ l.proxies | js(src="
  return $input.map(p => {
    if (p.legacy_port) { p.port = p.legacy_port; delete p.legacy_port; }
    return p;
  });
") | cla() | json_str(pretty=true) | indent(width=2) }}

更复杂的变换可抽为独立 .js 片段经 include_str! 或模板变量拼装;链式组合时 js 可位于任意位置,注意后续过滤器的输入类型需匹配(如 cla 期望代理数组)。

渲染上下文速查

变量可用范围说明
l.proxiesprovider代理对象数组(每个元素有 name / type / server / port 等字段)
l.name / l.rename / l.filterprovider名称 / 配置层规则名
s.response.contentsnippetsnippet 源内容(原样文本)
p.*全部自定义参数(params.yaml + /app/admin/p 热更新)
hp.*全部本次 HTTP 请求的查询参数(与 p 分离,ADR-081)
config全部完整配置对象(config.snippetconfig.params…)
headers / version全部请求头 / 版本字符串