Files
pangolin/docs/configurable-proxy-prototype.html
T
wangjia 45158c8b99
ci-pangolin / Redline Scan — 脱敏 (UI 文案) (pull_request) Successful in 27s
ci-pangolin / Cleartext Scan — Android 禁明文 (pull_request) Successful in 18s
ci-pangolin / Portable SQL — 可移植性 (mysql/sqlite) (pull_request) Successful in 17s
ci-pangolin / Lint — shellcheck (pull_request) Successful in 32m31s
ci-pangolin / Flutter — analyze + test (pull_request) Successful in 5m41s
ci-pangolin / OpenAPI Sync Check (pull_request) Failing after 11m25s
ci-pangolin / Codegen Drift — token 生成物未漂移 (pull_request) Failing after 15m29s
ci-pangolin / DS-flow — 原型/跨端同源/代码色单源闸 (pull_request) Successful in 35m20s
ci-pangolin / Go — build + test (pull_request) Failing after 5m34s
ci-pangolin / Golden — 视觉回归 (全量:components/auth/desktop/tablet) (pull_request) Successful in 2m33s
ci-pangolin / Go — integration (mysql/redis testcontainers) (pull_request) Failing after 10m41s
ci-pangolin / E2E Smoke — L4 进程级端到端 (pull_request) Failing after 10m48s
docs: 客户端可配置分流(类 Shadowrocket)设计 + 交互原型(Task 3,待确认)
让用户自定义路由规则(域名/域名后缀/关键字/IP-CIDR/GeoIP/GeoSite × 直连/走隧道/
拒绝),有序首命中。架构铁律(client 不拼配置)下走服务端 per-user 路由档案:
App 编规则 → PUT /v1/routing-profile 存档案 → connect 时 BuildClientConfig
翻译进 sing-box route.rules,原样下发。

产物:
- docs/configurable-proxy-prototype.html — 移动端三屏 mockup(规则表/加规则/文本导入)
- docs/configurable-proxy-spec.html — 规则模型 + 优先级层级 + 直连真生效
  (route_exclude/reverse_mapping)+ API + 校验兜底 + 与现有机制关系 + 分期 + 4 个待确认取舍
- docs/index.html 登记

设计-first,待用户确认后出实现计划开发。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A79VtQA1BwTuQN1ThpvYpo
2026-07-27 00:19:34 +08:00

226 lines
15 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Pangolin 可配置分流 · 原型</title>
<style>
:root{
--bg:#0f1117; --panel:#171a22; --panel2:#1d2129; --fg:#e6e8ee; --fg2:#a8afbd;
--accent:#e0884f; --accent2:#5fb0c9; --ok:#5ec27a; --bad:#e06a6a; --warn:#e0b84f;
--border:#262b36; --sans:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"PingFang SC","Microsoft YaHei",sans-serif;
--mono:"SF Mono",ui-monospace,Menlo,Consolas,monospace;
}
@media (prefers-color-scheme: light){
:root{--bg:#f4f5f8;--panel:#fff;--panel2:#eef0f4;--fg:#1a1d24;--fg2:#5a6172;--border:#dde0e7}
}
:root[data-theme="dark"]{--bg:#0f1117;--panel:#171a22;--panel2:#1d2129;--fg:#e6e8ee;--fg2:#a8afbd;--border:#262b36}
:root[data-theme="light"]{--bg:#f4f5f8;--panel:#fff;--panel2:#eef0f4;--fg:#1a1d24;--fg2:#5a6172;--border:#dde0e7}
*{box-sizing:border-box}
body{margin:0;background:var(--bg);color:var(--fg);font-family:var(--sans);line-height:1.6;font-size:15px}
.wrap{max-width:1180px;margin:0 auto;padding:32px 20px 80px}
h1{font-size:24px;margin:0 0 4px}
.lead{color:var(--fg2);margin:0 0 28px;font-size:14.5px}
h2{font-size:17px;color:var(--accent);border-bottom:1px solid var(--border);padding-bottom:6px;margin:38px 0 16px}
.cols{display:flex;gap:34px;flex-wrap:wrap;align-items:flex-start}
.note{flex:1;min-width:280px}
.note h3{font-size:15px;color:var(--accent2);margin:18px 0 6px}
.note p{color:var(--fg2);font-size:14px;margin:6px 0}
.tag{display:inline-block;font-size:11px;padding:1px 8px;border-radius:20px;font-family:var(--mono);vertical-align:middle}
.t-direct{background:rgba(94,194,122,.16);color:var(--ok)}
.t-proxy{background:rgba(224,136,79,.18);color:var(--accent)}
.t-reject{background:rgba(224,106,106,.16);color:var(--bad)}
.t-sys{background:rgba(95,176,201,.14);color:var(--accent2)}
code{font-family:var(--mono);font-size:.87em;background:var(--panel2);padding:1px 6px;border-radius:5px;color:#f0d9c4}
/* phone frame */
.phones{display:flex;gap:26px;flex-wrap:wrap}
.phone{width:330px;background:#000;border-radius:38px;padding:11px;box-shadow:0 12px 40px rgba(0,0,0,.5);flex:0 0 auto}
.screen{background:var(--bg);border-radius:28px;overflow:hidden;height:686px;display:flex;flex-direction:column;border:1px solid var(--border)}
.statusbar{height:26px;display:flex;align-items:center;justify-content:space-between;padding:0 20px;font-size:12px;color:var(--fg2);font-weight:600}
.navbar{display:flex;align-items:center;gap:10px;padding:8px 16px 12px;border-bottom:1px solid var(--border)}
.navbar .back{color:var(--accent);font-size:20px}
.navbar .title{font-size:17px;font-weight:700}
.navbar .add{margin-left:auto;color:var(--accent);font-size:22px;font-weight:300}
.body{flex:1;overflow-y:auto;padding:14px 14px 20px}
.caption{font-size:11.5px;color:var(--fg2);text-transform:uppercase;letter-spacing:.5px;margin:16px 4px 7px}
.card{background:var(--panel);border:1px solid var(--border);border-radius:14px;overflow:hidden}
.segment{display:flex;background:var(--panel2);border-radius:11px;padding:3px;margin:2px}
.segment div{flex:1;text-align:center;font-size:13px;padding:7px 0;border-radius:8px;color:var(--fg2);font-weight:600}
.segment div.on{background:var(--accent);color:#1a1206}
.row{display:flex;align-items:center;gap:11px;padding:12px 14px;border-bottom:1px solid var(--border)}
.row:last-child{border-bottom:none}
.row .grip{color:var(--fg2);font-size:15px;opacity:.6}
.row .main{flex:1;min-width:0}
.row .rt{font-size:14px;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.row .rsub{font-size:11.5px;color:var(--fg2);font-family:var(--mono);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.toggle{width:40px;height:24px;border-radius:20px;background:var(--accent);position:relative;flex:0 0 auto}
.toggle.off{background:var(--panel2)}
.toggle::after{content:"";position:absolute;top:2px;left:18px;width:20px;height:20px;border-radius:50%;background:#fff;transition:.2s}
.toggle.off::after{left:2px}
.rowlabel{flex:1;font-size:14px}
.rowval{font-size:13px;color:var(--fg2);font-family:var(--mono)}
.lock{font-size:11px;color:var(--fg2)}
.addbtn{text-align:center;color:var(--accent);font-size:14px;font-weight:600;padding:13px}
.hint{font-size:11.5px;color:var(--fg2);padding:8px 6px;line-height:1.5}
/* modal mock (add rule) */
.sheet{background:var(--panel);border:1px solid var(--border);border-radius:18px;overflow:hidden}
.sheet .sh{padding:12px 16px;border-bottom:1px solid var(--border);display:flex;align-items:center}
.sheet .sh .c{color:var(--fg2);font-size:14px}
.sheet .sh .t{flex:1;text-align:center;font-weight:700;font-size:15px}
.sheet .sh .s{color:var(--accent);font-size:14px;font-weight:700}
.field{padding:12px 16px;border-bottom:1px solid var(--border)}
.field .fl{font-size:11.5px;color:var(--fg2);margin-bottom:6px}
.field .fv{font-size:15px}
.chips{display:flex;gap:8px;flex-wrap:wrap}
.chip{font-size:12.5px;padding:6px 12px;border-radius:9px;background:var(--panel2);color:var(--fg2);border:1px solid transparent}
.chip.on{background:rgba(224,136,79,.16);color:var(--accent);border-color:var(--accent)}
.inp{background:var(--panel2);border:1px solid var(--border);border-radius:9px;padding:9px 11px;font-family:var(--mono);font-size:14px;color:var(--fg)}
table{border-collapse:collapse;width:100%;font-size:13.5px;margin:6px 0}
th,td{border:1px solid var(--border);padding:7px 10px;text-align:left;vertical-align:top}
th{background:var(--panel2);color:var(--accent2);font-weight:600}
td code{white-space:nowrap}
</style>
</head>
<body>
<div class="wrap">
<h1>Pangolin 可配置分流 · 交互原型</h1>
<p class="lead">Task 3 设计稿 · 让用户像 Shadowrocket 那样自定义"哪些流量直连 / 走隧道 / 拒绝"。以下是移动端三块核心界面的视觉原型(mockup,非真实数据)。配套规则模型与实现见 <a href="configurable-proxy-spec.html" style="color:var(--accent2)">配置说明文档 →</a></p>
<div class="phones">
<!-- Screen 1: 分流规则主屏 -->
<div class="phone"><div class="screen">
<div class="statusbar"><span>9:41</span><span>5G ▮▮▮</span></div>
<div class="navbar"><span class="back"></span><span class="title">分流规则</span><span class="add"></span></div>
<div class="body">
<div class="caption">代理模式</div>
<div class="card"><div class="segment">
<div>全局代理</div><div class="on">智能分流</div><div>全部直连</div>
</div></div>
<div class="hint">智能分流:按下面规则表逐条匹配;命中「直连」不走隧道,「走隧道」经节点出网。</div>
<div class="caption">内置规则(系统)</div>
<div class="card">
<div class="row"><span class="rowlabel">国内直连(GeoIP/GeoSite CN)</span><span class="toggle"></span></div>
<div class="row"><span class="rowlabel">局域网直连(LAN/私网)</span><span class="lock">🔒 强制</span></div>
<div class="row"><span class="rowlabel">私有服务走隧道</span><span class="lock">🔒 服务端</span></div>
</div>
<div class="caption">我的规则 · 按顺序匹配(首命中生效)</div>
<div class="card">
<div class="row"><span class="grip"></span><div class="main"><div class="rt">git.51yanmei.com</div><div class="rsub">域名后缀 · DOMAIN-SUFFIX</div></div><span class="tag t-direct">直连</span></div>
<div class="row"><span class="grip"></span><div class="main"><div class="rt">*.aliyun.com</div><div class="rsub">域名后缀 · DOMAIN-SUFFIX</div></div><span class="tag t-direct">直连</span></div>
<div class="row"><span class="grip"></span><div class="main"><div class="rt">github.com</div><div class="rsub">域名后缀 · DOMAIN-SUFFIX</div></div><span class="tag t-proxy">走隧道</span></div>
<div class="row"><span class="grip"></span><div class="main"><div class="rt">广告 / 追踪(GeoSite: category-ads)</div><div class="rsub">规则集 · GEOSITE</div></div><span class="tag t-reject">拒绝</span></div>
<div class="row"><span class="grip"></span><div class="main"><div class="rt">35.190.0.0/16</div><div class="rsub">IP 段 · IP-CIDR</div></div><span class="tag t-proxy">走隧道</span></div>
</div>
<div class="card" style="margin-top:10px"><div class="addbtn"> 添加规则</div></div>
<div class="caption">最终(未命中任何规则)</div>
<div class="card"><div class="row"><span class="rowlabel">FINAL · 兜底动作</span><span class="tag t-proxy">走隧道</span></div></div>
<div class="hint">从文本导入(Shadowrocket / Clash 规则)· 重置为默认</div>
</div>
</div></div>
<!-- Screen 2: 添加/编辑规则 -->
<div class="phone"><div class="screen">
<div class="statusbar"><span>9:41</span><span>5G ▮▮▮</span></div>
<div class="navbar"><span class="back"></span><span class="title">添加规则</span></div>
<div class="body">
<div class="sheet">
<div class="sh"><span class="c">取消</span><span class="t">添加规则</span><span class="s">保存</span></div>
<div class="field"><div class="fl">类型</div>
<div class="chips">
<span class="chip">域名</span><span class="chip on">域名后缀</span><span class="chip">关键字</span>
<span class="chip">IP-CIDR</span><span class="chip">GeoIP 国家</span><span class="chip">GeoSite 类别</span>
</div>
</div>
<div class="field"><div class="fl"></div>
<input class="inp" style="width:100%" value="git.51yanmei.com" readonly>
</div>
<div class="field"><div class="fl">动作</div>
<div class="chips">
<span class="chip on" style="border-color:var(--ok);color:var(--ok);background:rgba(94,194,122,.14)">直连 DIRECT</span>
<span class="chip">走隧道 PROXY</span>
<span class="chip">拒绝 REJECT</span>
</div>
</div>
<div class="field" style="border-bottom:none"><div class="fl">说明(选填)</div>
<input class="inp" style="width:100%" value="CI 编译源,国内直连不占隧道" readonly>
</div>
</div>
<div class="hint">GeoIP/GeoSite 由服务端自托管规则集(.srs)支持,墙内可直连下载,无需梯子。IP-CIDR 支持 v4/v6。</div>
<div class="caption">预览:将翻译为 sing-box 路由规则</div>
<div class="card" style="padding:12px"><pre style="margin:0;font-size:11.5px;color:#cdd3df;font-family:var(--mono);white-space:pre-wrap">{ "domain_suffix": ["git.51yanmei.com"],
"outbound": "direct" }</pre></div>
</div>
</div></div>
<!-- Screen 3: 从文本导入 -->
<div class="phone"><div class="screen">
<div class="statusbar"><span>9:41</span><span>5G ▮▮▮</span></div>
<div class="navbar"><span class="back"></span><span class="title">从文本导入</span><span class="add" style="font-size:14px;font-weight:700">导入</span></div>
<div class="body">
<div class="caption">粘贴规则(Shadowrocket / Clash 风格)</div>
<div class="card" style="padding:12px"><pre style="margin:0;font-size:11.5px;color:#cdd3df;font-family:var(--mono);white-space:pre-wrap"># 一行一条:类型,值,动作
DOMAIN-SUFFIX,git.51yanmei.com,DIRECT
DOMAIN-SUFFIX,aliyun.com,DIRECT
DOMAIN-SUFFIX,tencent.com,DIRECT
DOMAIN-SUFFIX,flutter-io.cn,DIRECT
DOMAIN-SUFFIX,github.com,PROXY
GEOSITE,category-ads,REJECT
GEOIP,CN,DIRECT
FINAL,PROXY</pre></div>
<div class="hint">解析后逐条加入「我的规则」,可再拖动排序。非法行(错误 CIDR / 未知类型)高亮跳过并提示。</div>
<div class="caption">解析结果预览</div>
<div class="card">
<div class="row"><div class="main"><div class="rt">✓ 7 条有效</div><div class="rsub">4 直连 · 1 走隧道 · 1 拒绝 · FINAL</div></div><span class="tag t-sys">OK</span></div>
<div class="row"><div class="main"><div class="rt">⚠ 0 条跳过</div><div class="rsub">无非法行</div></div></div>
</div>
</div>
</div></div>
</div>
<h2>三块界面在讲什么</h2>
<div class="cols">
<div class="note">
<h3>① 分流规则主屏</h3>
<p><b>代理模式</b>三选一(全局代理 / 智能分流 / 全部直连),对应 Shadowrocket 的 Global/Rule/Direct。</p>
<p><b>内置规则</b>是系统/服务端强制层:LAN 直连恒开(🔒),私有服务走隧道由服务端下发(🔒),国内直连可开关。</p>
<p><b>我的规则</b>是用户自定义、可拖动排序的有序表,<b>首命中生效</b><b>FINAL</b> 是兜底动作。</p>
</div>
<div class="note">
<h3>② 添加 / 编辑规则</h3>
<p><b>类型</b>(域名/后缀/关键字/IP-CIDR/GeoIP/GeoSite)→ 填<b></b> → 选<b>动作</b>(直连/走隧道/拒绝)。</p>
<p>实时<b>预览</b>该规则会被翻译成的 sing-box 路由片段,让高级用户看得懂、放心。</p>
</div>
<div class="note">
<h3>③ 从文本导入</h3>
<p>粘贴 <code>类型,值,动作</code> 一行一条,兼容 Shadowrocket/Clash 直觉。批量建规则、迁移旧配置。</p>
<p>解析给出有效/跳过统计,非法行不静默吞。</p>
</div>
</div>
<h2>动作与类型对照(给你确认)</h2>
<table>
<tr><th>动作</th><th>含义</th><th>sing-box outbound</th></tr>
<tr><td><span class="tag t-direct">直连</span></td><td>不走隧道,本机物理网卡直接出网(国内站、CI 源、私网)</td><td><code>direct</code></td></tr>
<tr><td><span class="tag t-proxy">走隧道</span></td><td>经 pangolin 节点出网(需科学上网的站)</td><td><code>auto</code>(REALITY/Hy2 择优)</td></tr>
<tr><td><span class="tag t-reject">拒绝</span></td><td>直接阻断(广告/追踪/恶意域)</td><td><code>block</code></td></tr>
</table>
<table>
<tr><th>类型</th><th>值示例</th><th>匹配</th></tr>
<tr><td>域名</td><td><code>example.com</code></td><td>精确</td></tr>
<tr><td>域名后缀</td><td><code>aliyun.com</code></td><td>含子域(<code>*.aliyun.com</code>)</td></tr>
<tr><td>关键字</td><td><code>google</code></td><td>域名含该串</td></tr>
<tr><td>IP-CIDR</td><td><code>35.190.0.0/16</code></td><td>IP 段(v4/v6)</td></tr>
<tr><td>GeoIP 国家</td><td><code>CN</code></td><td>该国 IP 段(自托管规则集)</td></tr>
<tr><td>GeoSite 类别</td><td><code>category-ads</code> / <code>netflix</code></td><td>预置域名集</td></tr>
</table>
<p class="lead" style="margin-top:30px">需要你确认:①这套 UI/模型对不对路?②要不要「从文本导入」这一块(可后置)?③规则粒度够不够(要不要 per-app 分应用代理,shadowrocket 有,较重、建议后置)?确认后我出实现计划开发。设计取舍与实现细节见 <a href="configurable-proxy-spec.html" style="color:var(--accent2)">配置说明文档 →</a></p>
</div>
</body>
</html>