3ae96ef229
覆盖前端五端/后端/MySQL/弹性节点拓扑/Web安全/安全总纲,遵循 design/ 设计系统视觉呈现。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
153 lines
11 KiB
HTML
153 lines
11 KiB
HTML
<!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>
|