Files
pangolin/doc/01-frontend.html
wangjia 3ae96ef229 docs: 新增整体架构设计文档(doc/,HTML 七章)
覆盖前端五端/后端/MySQL/弹性节点拓扑/Web安全/安全总纲,遵循 design/ 设计系统视觉呈现。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 23:57:49 +08:00

153 lines
11 KiB
HTML
Raw Permalink 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.0">
<title>01 前端架构 · 穿山甲架构设计</title>
<link rel="stylesheet" href="assets/style.css">
</head>
<body>
<header class="site-header"><div class="inner">
<a class="brand" href="index.html"><img src="assets/logo-mark.svg" alt="Pangolin"><span><span class="zh">穿山甲 · 架构设计</span><br><span class="en">Pangolin Architecture</span></span></a>
<nav class="site-nav">
<a href="index.html">总览</a>
<a href="01-frontend.html" class="active">01 前端</a>
<a href="02-backend.html">02 后端</a>
<a href="03-database.html">03 数据库</a>
<a href="04-infra.html">04 网络拓扑</a>
<a href="05-web-security.html">05 Web 安全</a>
<a href="06-security.html">06 安全总纲</a>
</nav>
<button class="theme-btn" id="themeBtn" title="切换深浅主题"><i data-lucide="moon"></i></button>
</div></header>
<main class="doc">
<div class="doc-hero">
<div class="overline">Chapter 01 · Frontend</div>
<h1>前端架构</h1>
<p class="lede">原则:性能、稳定性、用户体验优先;不同平台允许不同技术栈,但视觉与交互必须 100% 还原 <code>design/</code> 设计系统——以 <code>ui_kits/</code> React 原型为像素验收标准,以 <code>colors_and_type.css</code> 为令牌唯一真相源。</p>
</div>
<section id="stack">
<h2><span class="num">§1</span>分端技术栈选型</h2>
<div class="tbl-wrap"><table>
<thead><tr><th></th><th>技术栈</th><th>选型理由(性能 / 稳定 / UX</th><th>像素基准</th></tr></thead>
<tbody>
<tr>
<td><strong>移动 App</strong><br><span class="muted">iOS / Android</span></td>
<td><strong>Flutter</strong> + sing-box <code>libbox</code>gomobile 桥接)<br>iOS NetworkExtension / Android VpnService</td>
<td>需要系统级隧道能力,Web 栈不可行;Flutter 自绘渲染保证两端像素一致,<code>design/flutter/</code> 起步包(theme + widgets + main)拿来即跑。</td>
<td><code>ui_kits/mobile/</code></td>
</tr>
<tr>
<td><strong>iPad 客户端</strong></td>
<td><strong>Flutter</strong>——与移动 App 同一工程,宽度 ≥900 自适应切换为侧栏分栏布局</td>
<td>不是第五套代码:复用移动端全部原子组件与内核层,仅布局开关;横屏 1180×820 基准,左侧栏导航(触控尺寸,行高 ≥48px),连接页双栏(左大连接键 / 右信息列),节点双列网格。</td>
<td><code>ui_kits/tablet/</code></td>
</tr>
<tr>
<td><strong>桌面客户端</strong><br><span class="muted">Windows / macOS</span></td>
<td><strong>Flutter desktop</strong> + sing-box 子进程(TUN 模式)</td>
<td>与移动端共享 ~90% UI 代码(连接键、节点列表、统计、账户全复用),920×600 固定窗 + 侧栏布局照搬原型;避免 Electron 的内存与启动开销。</td>
<td><code>ui_kits/desktop/</code></td>
</tr>
<tr>
<td><strong>官网</strong></td>
<td><strong>Astro</strong>SSG)→ Cloudflare Pages</td>
<td>纯静态 = 极致加载性能 + 整站可秒级复制到任意备用域名(灾备关键,见 05 章);Astro 直接吃 JSX<code>ui_kits/website</code> 组件近乎逐字迁移。</td>
<td><code>ui_kits/website/</code></td>
</tr>
<tr>
<td><strong>Web 用户中心</strong></td>
<td><strong>Next.js</strong>(React,静态导出 + 客户端取数)</td>
<td><code>ui_kits/usercenter</code> 本身是 React——组件直接复用是保真上限;静态导出后同样可多镜像部署,动态数据全走 API。</td>
<td><code>ui_kits/usercenter/</code></td>
</tr>
</tbody>
</table></div>
<div class="callout"><b>为什么不强行统一一个栈</b>
客户端离不开系统级隧道与开机自启,必须是原生壳;Web 端的两个原型本身就是 React,重写成 Flutter Web 反而损失保真与首屏性能。五个产品面、四套实现(iPad 归入移动端工程的自适应断点),通过<strong>同一套设计令牌</strong>CSS 变量 ↔ <code>pangolin_theme.dart</code> 一一对应)保证视觉同源——栈不同,像素相同。</div>
</section>
<section id="client-arch">
<h2><span class="num">§2</span>客户端分层架构(移动 + 桌面共享)</h2>
<div class="diagram">
<div class="stack">
<div class="layer accent"><span class="t">UI 层(共享 ~90%</span><span class="d">flutter/widgets/:连接键三态 · 智能选择推荐卡 · 免费额度卡 · 节点列表 · 统计 · 账户;明暗双主题 + 中英单显;宽度 ≥900 自适应切侧栏分栏布局(iPad / 桌面同构)</span></div>
<div class="layer"><span class="t">状态层</span><span class="d">Riverpod:连接状态机(off / connecting / on)· 会话 · 节点目录(带 version)· 用量额度</span></div>
<div class="layer"><span class="t">服务层</span><span class="d">API client(域名池 + 重试退避 + 响应签名校验)· 节点目录缓存 · 订阅凭证存储(Keychain / Keystore</span></div>
<div class="layer"><span class="t">内核层</span><span class="d">sing-box libbox(移动:gomobile AAR/XCFramework;桌面:子进程 + TUN)· URLTest 智能选线 · Kill-switch</span></div>
<div class="layer"><span class="t">平台壳</span><span class="d">iOS NetworkExtension · Android VpnService(前台服务)· macOS/Win TUN 设备 + 开机自启</span></div>
</div>
<div class="cap">UI 与内核之间仅通过状态层通信;内核崩溃不拖垮 UI,UI 重启不掉隧道</div>
</div>
<ul>
<li><strong>连接状态机</strong>是核心:UI 三态(off / connecting / on)严格对应内核事件,禁止 UI 侧"乐观显示已连接"。</li>
<li><strong>智能选择</strong>(默认选中):sing-box URLTest 组在本地探测延迟自动选优,节点列表置顶推荐卡与原型一致。</li>
<li><strong>免费版额度</strong>:本地倒计时仅作展示,权威额度以 API 为准(见 02 章 <code>/v1/ads/unlock</code> 与 connect 校验)。</li>
<li><strong>默认安全</strong>Kill-switch 默认开启;DNS 走 DoH 防泄露;阻断 WebRTC 直连泄露(桌面端注入策略)。</li>
</ul>
</section>
<section id="fidelity">
<h2><span class="num">§3</span>100% 还原策略</h2>
<h3>3.1 令牌同源</h3>
<div class="tbl-wrap"><table>
<thead><tr><th></th><th>令牌载体</th><th>同步方式</th></tr></thead>
<tbody>
<tr><td>官网 / 用户中心</td><td><code>colors_and_type.css</code> 直接链入</td><td>原样引用,不复制不改写</td></tr>
<tr><td>移动 / 桌面</td><td><code>flutter/pangolin_theme.dart</code></td><td>与 CSS 一一对应的 Dart 镜像;改令牌必须两处同改(design/CLAUDE.md §6</td></tr>
</tbody>
</table></div>
<h3>3.2 像素基准与组件对照</h3>
<p>每个界面动手前先打开对应 React 原型比对;以下关键组件逐一对照验收:</p>
<ul>
<li><strong>核心连接键</strong>:off 暖灰底 + 虚线轨道环 / connecting 旋转弧 / on 绿底满环 + 圆内计时 + 光晕(过渡只用 <code>box-shadow</code>/<code>background-color</code>,禁 <code>transition: all</code></li>
<li><strong>智能选择推荐卡</strong>:常驻 accent-subtle 底 + clay 渐变 zap 图标 + 「推荐」胶囊,默认选中</li>
<li><strong>免费额度卡</strong>:剩余分钟 + 进度条(≤3 分钟变 warning 色)+「看广告开始使用」→ 解锁后变绿</li>
<li><strong>国家码块</strong>2 字母,无 emoji 国旗)、<strong>状态胶囊</strong>(色点 + 文字,无 emoji)、Lucide 细线图标</li>
<li><strong>Tab 滑动切换</strong>:>60px 且横向位移明显大于纵向,200ms 方向感知滑入;子页不响应</li>
<li><strong>iPad 分栏布局</strong>(对照 <code>ui_kits/tablet/</code>):左侧栏导航行高 ≥48px 触控尺寸;连接页双栏(左大连接键 / 右额度卡→当前节点→实时速率);节点页置顶智能推荐卡 + 双列网格(同桌面)</li>
</ul>
<h3>3.3 提交前验收清单(每个界面,引用 design/CLAUDE.md §9</h3>
<ul>
<li>颜色全部来自语义 token,无硬编码十六进制</li>
<li>明 / 暗两主题、中 / 英两语言(单显不并排)共四态验证</li>
<li>文案无铁律 13 红线词;套餐数字与 design/CLAUDE.md §7 一致</li>
<li>App / 官网内无任何支付表单,购买只引导到外部渠道</li>
</ul>
</section>
<section id="resilience">
<h2><span class="num">§4</span>客户端断网弹性</h2>
<p>设计目标:<strong>API 全灭、域名全被污染时,客户端仍能连上节点</strong>。详见 06 章断网应对矩阵,此处为客户端侧实现。</p>
<div class="tbl-wrap"><table>
<thead><tr><th>机制</th><th>实现</th><th>覆盖的故障</th></tr></thead>
<tbody>
<tr><td><strong>节点目录缓存</strong></td><td>每次成功拉取 <code>/v1/nodes</code> 后落盘(带 version 与时间戳);启动时先用缓存渲染并尝试连接,后台再刷新</td><td>API 暂时不可达</td></tr>
<tr><td><strong>API 端点池</strong></td><td>客户端内置:主域名 + 备用域名 N 个 + IP 直连兜底;按序故障转移,成功的端点置顶记忆</td><td>单个域名被墙 / 被污染</td></tr>
<tr><td><strong>DoH 解析</strong></td><td>域名解析优先走 DoH(多个提供方),绕开本地污染</td><td>DNS 污染</td></tr>
<tr><td><strong>签名端点更新</strong></td><td>定期从多镜像静态文件(Cloudflare Pages / GitHub 等)拉取 Ed25519 签名的端点列表,验签后合并进端点池</td><td>内置端点全部失效</td></tr>
<tr><td><strong>紧急逃生配置</strong></td><td>安装包内置 1–2 个应急节点参数(低速、仅够拉新目录),所有在线途径失效时启用</td><td>极端封锁</td></tr>
<tr><td><strong>应急公告</strong></td><td>客户端内公告位从签名静态 JSON 读取(多镜像),可引导用户更新或切换渠道</td><td>需要人工广播时</td></tr>
</tbody>
</table></div>
<div class="callout warn"><b>注意</b>所有兜底参数(IP、应急节点、签名公钥)属于敏感资产:随版本轮换、按渠道分包(不同分发渠道内置不同 IP 子集),泄露一个渠道不烧全部。</div>
</section>
</main>
<nav class="pager">
<a href="index.html"><span>上一章</span><b>← 总览</b></a>
<a class="next" href="02-backend.html"><span>下一章</span><b>02 后端设计 →</b></a>
</nav>
<footer class="colophon">穿山甲 · Pangolin — 内部架构设计文档 · 遵循 design/ 设计系统 · 2026-06</footer>
<script src="https://unpkg.com/lucide@latest/dist/umd/lucide.min.js"></script>
<script src="assets/doc.js"></script>
</body>
</html>