外观
对等门户 apps/peering
NATLAN 的自助对等门户,生产部署 https://peering.natlan.io/。
面向其他 DN42 运营者:DN42 身份登录 → 选接入节点 → 提交对端参数 → 即时落成并渲染 可照抄的 WireGuard / BIRD 配置 → 状态页 BGP 红绿灯 → 自助拆除。此外提供一组只读 的全网态势看板。
技术形态与控制台同构:SvelteKit(adapter-static)纯客户端 SPA,同一套设计令牌与 共享组件。
1. 路由
| 路由 | 说明 |
|---|---|
/ | 落地页 / 登录入口 |
/auth/complete | 授权回跳落点:用一次性 handoff 码换门户会话 |
/nodes | 接入节点选择(主入口):节点地图 + 可接入点、区域、能力 |
/new | 新建对等请求:对端参数表单 |
/peerings | 当前身份名下的对等连接列表 |
/peerings/[id] | 单条对等详情:可照抄的配置、BGP 状态、流量 / 路由 / 时延观测、拆除入口 |
/network/traffic | 全网流量态势(只读镜像) |
/network/routing | 全网路由态势(只读镜像) |
/network/flaps | 全网抖动态势 + 「我这家 AS 抖不抖」 |
/account | 身份卡片、registry 声明刷新、登出 |
侧栏把 /network/* 三项收在「网络」分组下,接入节点是导航的第一项——导航按用户 意图排序,不按后端资源排序。
2. 身份与会话
门户自己不做身份验证。身份由 auth.natlan.io(OIDC issuer)提供:
门户 → GET /auth/login-url?next=… → 跳转 auth.natlan.io/authorize
↓(用户完成 DN42 身份验证与授权)
门户 /auth/complete ← 一次性 handoff 码
→ POST /auth/exchange → 门户会话令牌前端只经手两样东西:一次性 handoff 码与门户会话令牌。会话令牌存 sessionStorage(natlan.peering.session),随标签页消亡;它是这个应用唯一持有的 凭据,只出现在 Authorization 头里。
任何端点返回 401 都会清掉令牌并触发登出转场(setUnauthorizedHandler)。
3. 只读网络态势
/network/* 三个板块是控制台同名板块的门户镜像。后端复用了 UI 面的同一批 builder 函数、另挂 require_portal_session,因此响应形状与控制台逐字段一致——门户可以把 它直接喂给 $ui 里的同一批看板组件,不需要转换层。
脱敏口径:全网态势的 AS 号、前缀、流量占比、抖动明细不脱敏(这些本就在 DN42 registry 与 explorer 上公开)。后端做白名单投影收窄两处:
- WireGuard 对端的
public_key与endpoint(密钥材料与攻击面); - overview 节点行的 agent 遥测(内部运维信息)。
一个容易踩的口径差异:fleet 级流量只提供 6h / 24h(FleetTrafficRange), 而用户自己那条对等的观测是四档全收(TrafficRange)。
「我这家 AS 抖不抖」(/my/flaps)在无抖动痕迹时返回 200 + has_flaps: false 而不是 404——对用户来说「没抖」是正向结果,不该长得像出错。
4. 接口
基址 VITE_PEERING_API ?? https://api.natlan.io,前缀 /control/v1/autopeering。 另有一处直连 /control/v1/public/fleet(节点地图坐标与骨干链路),它不属于 autopeering 契约,调用方必须能在它失败时优雅降级。
完整端点表见 api/portal.md。
5. 技术栈
- SvelteKit 2 + Svelte 5(runes) + TypeScript,
adapter-static - 共享层
$ui:AppShell、看板、图表、地图、设计令牌 - 语言:英 / 简中两种(控制台是四种)
6. 部署
Cloudflare Git 关联 Worker(dn42-peering-webui),纯静态资产、无自定义 Worker 脚本——门户不发 CSP,因此不需要控制台那层 nonce 壳。
apps/peering/wrangler.jsonc 的三个要点:
| 配置 | 作用 |
|---|---|
routes: [{ pattern: "peering.natlan.io", custom_domain: true }] | 自定义域写在配置里,每次 Git 构建都重新声明 |
assets.not_found_handling: "single-page-application" | SPA 回退。/auth/complete 等路径没有构建期页面,必须靠它解析 |
workers_dev: false | 关掉第二入口 |
保留
wrangler.jsonc,不要引入_redirects。 Workers 资产会把_redirects里的 SPA 规则判成重定向死循环。
7. 本地对接后端
apps/peering/.env.local 写 VITE_PEERING_API=http://127.0.0.1:8000。
登录闭环额外需要两件事:后端 [portal] frontend_base_url 指向本地前端,且本地来源 在控制面 CORS 白名单内。详见 guides/point-to-another-backend.md。