netx/web/WEB.md
oliver 0d894efe5e Refactor web UI: feature-first structure, tokenized styles, modern blue theme.
Structure
- pages/ -> features/<domain>/{Page, components, hooks, lib}; 95 files moved,
  333 imports rewritten, verified with tsc.
- services/api.ts -> services/api/<domain>.ts (250 exports preserved),
  types.ts -> types/<domain>.ts, i18n zh/en -> locales/<lang>/<namespace>.ts
  (2696 messages deep-equal before/after).
- ESLint rule: shared layers must not import features/.

Styles
- index.css (16k lines) split losslessly into styles/{base,layout,components,
  pages,legacy,theme}; compiled CSS was byte-identical before tokenizing.
- ~930 hard-coded colors replaced by --nx-* tokens (theme/tokens.css); WebCRT
  off-palette colors unified; fixed dark-on-dark text introduced by mapping.
- theme/theme.css: modern blue design language (rounded shapes, gradients,
  unified buttons/fields/tables/modals), form-grid alignment fix, flat nav list
  without tree indent, soft selected state instead of left accent bar.

Docs: WEB.md documents code structure and style rules.
Not touched: the large Biz*/Webcrt/Topology page components (to be split later).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-12 03:17:51 +08:00

16 KiB
Raw Blame History

NetX Web 前端规范

技术栈

  • React 19 + TypeScript + Vite
  • React Router(工作台 + 模块页)
  • TanStack Query(服务端状态)
  • 自研 i18n(src/i18n/,无额外依赖)

代码结构

按领域(feature)组织,依赖方向 features → 共享层,反向禁止(ESLint no-restricted-imports 强制)。

src/
  main.tsx  App.tsx             # 入口、路由表(lazy 引入各 feature 页面)
  features/<领域>/              # 业务模块,kebab-case 目录名
    XxxPage.tsx                 #   路由入口(XxxLayout.tsx 为带子路由的壳)
    components/                 #   该领域私有组件(含 modals/)
    hooks/                      #   该领域私有 hooks(useXxx.ts)
    lib/                        #   纯逻辑(无 React),可单测
  layout/                       # 应用壳:AppLayout、顶栏、ErrorBoundary
  components/ (+ ui/)           # 跨领域复用 UI(无业务 API 调用)
  hooks/  utils/                # 跨领域 hooks / 纯工具
  services/api/                 # 后端 API,按领域分文件,index.ts 汇总导出
  types/                        # 后端契约类型,按领域分文件,index.ts 汇总导出
  i18n/locales/{zh,en}/<ns>.ts  # 文案,一个命名空间一个文件
  config/                       # 导航与模块注册表(modules / networkNav / auditNav)
  constants/queryKeys.ts        # React Query key
  styles/                       # 见「样式规范」

现有领域:auth workbench managed-ne ume collect config-sync webcrt topology network port-traffic cutover(业务状态/对比/割接) audit users api-tokens sessions。

约定:

  • 只被一个领域用到的代码放进该领域的 components/ hooks/ lib/;被两个以上领域用到才上移到共享层。
  • 跨领域只引用对方的页面级导出或 components/ lib/ 下的文件;现有合理依赖:* → auth、topology/ume → managed-ne。
  • 共享层(components hooks utils services i18n types constants)不得 import features/。
  • 新增 API:写进 services/api/<领域>.ts;新增类型:写进 types/<领域>.ts;调用方仍 from "…/services/api" / "…/types"。
  • 单个组件函数超过 ~500 行视为需要拆分:状态/副作用抽成 hooks/,纯计算抽成 lib/,区块抽成 components/。 (BizComparePage WebcrtPage BizStatePage BizMigrationPage TopologyPage 等仍是待拆分的单体组件。)

样式规范

src/index.css 只是入口:按顺序 @import styles/ 下的文件,导入顺序即层叠顺序,不要调换。

styles/
  theme/tokens.css   # 唯一的色板/圆角/阴影来源(--nx-*),并把旧 --nm-* 映射到它
  base/              # 全局基础(滚动条、body)
  layout/            # 应用壳、网络/审计壳(左侧导航)
  components/        # 共享原语(按钮、输入、表格、卡片、pill…)
  pages/<领域>.css   # 页面样式,类名带领域前缀(wb-* topo-* webcrt-* bs-* pt-*…)
  legacy/            # 历史遗留的覆盖层,逐步并入 components/,不要往里加新规则
  theme/theme.css    # 设计语言层,必须最后加载

设计方向:现代蓝——沿用 NetX 原有深海军蓝色板、柔和渐变面板、8/12px 圆角、蓝色强调色(主按钮用渐变), 胶囊状态标签;hover 有轻微抬升和蓝色光晕,focus 用柔和焦点环。

规则:

  • 颜色一律用 var(--nx-*);半透明用 rgb(var(--nx-ink-rgb) / .6),不要写 #0f172a / rgba(15,23,42,.6)。
  • 状态色用语义变量 --nx-ok / --nx-warn / --nx-bad。
  • 次要文字至少用 --nx-text-faint(在 --nx-surface-1 上对比度 ≥ 4.5:1)。
  • 顶栏高度固定 48px(--nx-header-h),多个页面用 calc(100vh - 48px) 布局。
  • 新样式放进所属 pages/ 或 components/ 文件;只有跨页面的视觉语言才改 theme/theme.css。
  • 避免 !important 和"覆盖全局 button"式规则;需要时先在 components/ 里改原语。

路由与模块

路径 页面 moduleId
/ 工作台 —
/ume UME 同步 ume
/ne 网元管理(CRUD / 连通性) ne
/network 网络管理(左栏 + 子路由) network
/network/devices 设备列表(只读:托管 + UME) network
/network/alarms 告警信息 network
/network/configs 配置信息(同步快照) network
/network/webcrt redirect → /webcrt(保留 query) —
/network/topology redirect → /topology —
/network/tasks/collect 采集任务 network
/network/tasks/config-sync 配置同步 network
/network/tasks/port-traffic 端口流量监控(设备管理) network
/network/tasks/port-traffic/wall 流量大屏列表(打开独立页签) network
/network/tasks/biz-state 业务状态监控(LLDP / VRF 关联采集) network
/network/tasks/biz-compare 业务状态比对(模板 + 任务 + pre/post 结果) network
/port-traffic/wall/:boardId 流量大屏专有页签(无网络侧栏) port-traffic-wall
/topology 拓扑管理(模式化编辑器:选择/平移/拖动/连线、框选、自动布局、拖放添加) topology
/webcrt WebCRT 终端 webcrt
/collect redirect → /network/tasks/collect —

菜单树见 config/networkNav.ts。新增网络子页:

  1. 在 networkNav.ts 增加叶子项
  2. 在 App.tsx /network 下增加子 <Route>
  3. 补充 i18n network.*

新增工作台模块仍改 config/modules.ts:

  1. 在 MODULES 增加一项(moduleId、path、section、i18n key、iconTone)
  2. 在 App.tsx 增加 <Route path="..." element={...} />
  3. 在 i18n/locales/zh/<ns>.ts 与 en/<ns>.ts 补充文案(新命名空间还需在各自 index.ts 注册)
  4. 工作台卡片由 modulesInSection 自动生成,点击走 openOrFocusModule

跨标签页导航

  • 工作台 → 模块:openOrFocusModule(同 moduleId 仅一个标签,已打开则聚焦)
  • 模块 → 工作台:顶栏四格图标,returnToWorkbench(聚焦原工作台标签;已关闭则新开)
  • 流量大屏:列表在网络管理内;打开单板用 openNewModuleWindow(/port-traffic/wall/:boardId),与 network 单例页签隔离
  • 实现:utils/tabChannel.ts + 命名窗口 netx-module-{id} / netx-workbench

i18n

  • 所有面向用户的文案走 useI18n().t("dotted.key")
  • 表头/字段名等技术列可保持英文
  • 语言存在 localStorage(netx-locale),顶栏 ⋯ 切换

数据请求

  • API 仅写在 services/api/<领域>.ts
  • Query key 集中在 constants/queryKeys.ts
  • 失效缓存用 prefix key(如 queryKeys.umeSyncStatusAll)
  • 顶栏连接状态:App 轮询 GET /v1/integrations/status(5s),展示 netx api 与 DSH alarm hub
  • 关键告警推送:NetX DSH alarm hub(/v1/integrations/dsh-alarm/ws)为服务器,Netx Ops 外拨订阅;UME 页展示多订阅连接列表

网元管理(独立于 UME)

  • API:/v1/managed-ne/*(CRUD、导入、连通性测试)
  • 环境变量:NETX_CREDENTIAL_SECRET_KEY(Fernet,用于加密存储 SSH 密码)
  • 导入列:device_type,ip,username,password,port,protocol,name,vendor
  • 连通性测试成功后会始终用探测到的设备名覆盖「名称」

批量采集

  • API:/v1/ne-collections/*(仅 connect_status=pass 的网元可参与)
  • 采集日志目录:NETX_NE_COLLECTION_DATA_DIR(默认 data/ne_collections)
  • 命令每行一条,# 为注释;输出格式与旧版 NetX 采集 .txt 一致

配置同步

  • API:/v1/config-sync/*(策略、看板、周期、快照)
  • 存储:PostgreSQL ne_config_snapshot / ne_config_history(zlib BYTEA)
  • 范围:ManagedNE + UME CLI 目标;厂商固定只读命令矩阵
  • 调度:NETX_CONFIG_SYNC_SCHEDULER_ENABLED(默认开),周期天数策略可配(默认 3 天)
  • 默认策略:enabled=false(首次无自动任务,需在页面手动开启周期调度或点「立即同步」)
  • 单飞:同一时刻只允许一个 running|pending|paused 周期;上轮未结束时不会开启新周期
  • 周期状态:全部任务跑完即为 success;单网元失败只计入 fail_count,不把整轮标为失败
  • 崩溃续跑:启动时把中断的 running 任务重新入队并继续,占用单飞槽位,避免与新周期重叠
  • 进程启动宽限:NETX_CONFIG_SYNC_STARTUP_GRACE_SEC(默认 3600)仅约束新建自动周期,不影响续跑
  • 前端:/network/tasks/config-sync(看板)+ /network/configs(查看)

端口流量监控

  • API:/v1/port-traffic/devices/*(设备 CRUD、interfaces、samples、compare、dashboard);discover/ports 仍按网元拉取接口
  • 设备中心:每台网元(source+ne_id)一份监控配置;周期/保留/启停挂在设备上;接口归属于该设备
  • 唯一性:同一物理口 (source, ne_id, ifname) 全局仅允许一个 active 监控行,避免重复 CLI 采集
  • 调度:按设备到期;一轮采集对该设备 登录一次 后批量 show interface
  • 手工映射:大屏可选其它设备接口作基线叠图;可与周期偏移叠加
  • 厂商:ZTE / 华为 / 思科;解析速率 bit/s;缺厂商 util 时按 bps/bw 回算
  • 调度开关:NETX_PORT_TRAFFIC_SCHEDULER_ENABLED(默认开),tick NETX_PORT_TRAFFIC_SCHEDULER_TICK_SEC(默认 15)
  • 保留:按设备 retention_days 清理过期 sample(周对比建议 ≥8 天)
  • 前端:/network/tasks/port-traffic(设备列表 / 向导 / 编辑 / 采集日志);/network/tasks/port-traffic/wall(大屏列表);/port-traffic/wall/:boardId(独立大屏页签,openNewModuleWindow,与网络管理页签互不影响)
  • 定制大屏:/v1/port-traffic/boards*(Board + Panel);整板 PUT panels 保存;刷新/换页不丢;图数据仍走 compare
  • 采集日志:GET /v1/port-traffic/devices/{id}/events;失败写入 port_traffic_event,列表操作可查看
  • 支持拓扑深链:?ne_id=&source=managed|ume&ifname= 打开向导并预填网元

业务状态监控(biz_state)

  • API:/v1/biz-state/profiles、/discover、/tasks*、/batches*、/batches/{id}/export、/compare/*
  • ParseProfile:命令模板 + TextFSM + 回调 + schema;LLDP 与拓扑 共享解析(lldp_shared),业务流程写批次表,拓扑写 Fabric
  • Phase1:LLDP 邻居快照;建任务默认启用对应厂商 LLDP profile;支持自定义只采不解析行
  • Phase2:比对模板(多监控表 / Key·接口·比对字段;空比对=仅条目)、端口映射、CompareJob 列表与详情、先选批次再按 Excel sheet 看 pre/post、/network/tasks/biz-compare
  • API 补充:GET /v1/biz-state/compare/metrics;模板 metrics[] 多表结构
  • Phase3:VRF 发现(*.vrf_list)供带参采集绑定;PUT …/items/{id}/bindings
  • 调度:NETX_BIZ_STATE_SCHEDULER_ENABLED(默认开),tick NETX_BIZ_STATE_SCHEDULER_TICK_SEC
  • 双车道采集:监控项 collect_lane=light|heavy(UI「采集车道」列)。轻/重各一条 SSH;整轮墙钟上限 = RUN_TIMEOUT_CAP(命令多时易顶满)。
    • 轻车道:NETX_NE_COLLECT_READ_TIMEOUT_SEC(单条,默认 120)· NETX_NE_COLLECT_RUN_TIMEOUT_CAP_SEC(整轮,默认 600≈10min)
    • 重车道:NETX_BIZ_STATE_HEAVY_READ_TIMEOUT_SEC(单条,默认 1500)· NETX_BIZ_STATE_HEAVY_RUN_TIMEOUT_CAP_SEC(整轮,默认 2400≈40min)
    • 批次约 40 分钟以 *_timeout (2400s) / partial 结束时,优先加大 NETX_BIZ_STATE_HEAVY_RUN_TIMEOUT_CAP_SEC(见 .env.example)
  • 停止采集:POST /v1/biz-state/tasks/{id}/collect/stop(排队直接取消;执行中命令间隙中止)
  • 前端:/network/tasks/biz-state(勾选监控项 / 发现 VRF / 批次 / 导出 zip)

拓扑管理(Fabric + 站点目录,对齐厂商)

  • 三层库存:运维 managed_ne(凭据/采集/WebCRT)· EMS ume_inventory_ne · 拓扑投影 topo_fabric_node(上图/LLDP/分类)。Fabric 由 ensure 按需创建,与运维表不是同一张表。
  • 删除运维网元(手工 / WebCRT / ume_sync ManagedNE 同一路径):先解绑 fabric.managed_ne_id(及 view membership)。若该 Fabric 节点同时仍绑 UME → 变为 UME-only,保留节点与直连边;若解绑后已无 managed 且无 UME(完全孤儿)→ 硬删 Fabric 节点并级联直连边 / placements。UME 库存 reconcile 对称解绑 ume_ne_id,同样在完全孤儿时 purge。历史悬空引用与孤儿 GC:POST /fabric/reconcile-links(cleanup-duplicates 先 merge/absorb 再扫孤儿);后台默认每 6h 跑一次(fabric_reconcile_scheduler_*,可关)。
  • 画布:移出本图仅影响 placement(保存落库);连线/删边本地排队、保存才写/删 Fabric(Undo 仅保存前有效);topology/lldp 占位可右键硬删(ManagedNE+Fabric+边)。未匹配且未建占位的对端不上 Fabric 边。
  • 事实库:topo_fabric_node / topo_fabric_edge(按 5 万网元 / 100 万链路设计;物理层仅 LLDP)
  • 站点树:topo_folder(系统隐藏 root;用户新建站点/区域,无默认「未分区」)
  • 拓扑图:topo_view.kind=physical|custom(同站点下平级;建区域后需手工/MCP 建画布)+ topo_view_node
  • 边界:图 filter.membership(max_nodes / expand_hops / frozen);project-neighbors / populate 不得无界灌全网。画布「投影邻居 / 发现后投影」默认 dry_run:只进本地脏图,保存时 addTopologyViewNodes 才落库。
  • 连线模式:网元中心锚点拖拽连线;默认直线 straight(与发现边同一附着点)。
  • API:/v1/topology/tree、/folders*、/fabric/*、/views*(含 populate、kind)
  • 前端:左侧站点→物理/自定义图;右侧目录浏览,打开本图进设备画布;「添加网元」支持勾选批量上图(也可拖放单台)
  • MCP:以 queryTopologyEdges 为主查询 Fabric;画布编辑走 Web
  • BGP / 隧道 / L2VPN:layer 预留,实现 TODO

分类(清单打标)

  • Fabric 主键:topo_fabric_node.level(major.minor,越小越靠外);role 为 floor(level) 同步别名
  • 预设:0 外部 / 1 核心 / 2 汇聚 / 3 接入;子层如 1.1、2.1
  • 主流程:清单 + 正则 → 批量写 level/区域;支持单行编辑
  • API:GET /fabric/nodes(level / level_major / role / unmatched=level)、POST …/match、POST …/tags/bulk(level)、PATCH …/tags
  • Agent:classifyTopologyFabricNodes(netx-topology MCP)
  • 前端:网络管理 →「分类」

WebCRT

  • API:POST /v1/webcrt/sessions(ne_id 或 ume_ne_id;可选 encoding/post_login_commands/async_connect)、WS /v1/webcrt/sessions/{id}/ws、DELETE /v1/webcrt/sessions/{id}
  • SFTP(直连 SSH):POST /v1/webcrt/sftp/list|download|upload
  • 目标列表复用 /v1/cli/targets(托管 + UME,搜索分页;source=all|managed|ume)
  • 凭据:托管走网元自身账号;UME 走 CLI 连接模板(resolve_cli_target)
  • 前端:CRT 风格左右分栏(收藏/最近 + 多标签终端);粘贴节流、选区复制、Break、Ctrl+F、Button Bar、广播、编码/字号
  • WS:stdout 二进制帧合批;stdin 短合批;建连可异步(POST 立即返回,进度经 WS connecting→connected)
  • 审计 / 会话日志:NETX_WEBCRT_DATA_DIR(audit.jsonl + sessions/*.log)
  • 限流 / 保活:NETX_WEBCRT_MAX_SESSIONS、NETX_WEBCRT_IDLE_TIMEOUT_SEC、NETX_WEBCRT_KEEPALIVE_SEC、NETX_WEBCRT_ANTI_IDLE_SEC

Toast

  • 使用 ToastProvider(main.tsx)+ useToast()
  • 禁止页面内单独维护一套 toast 状态

样式

  • 全局样式:见「样式规范」(index.css 为入口,令牌在 styles/theme/tokens.css)
  • 不使用 CSS Modules;类名 BEM 风格:app-brand__logo、wb-card

构建

cd web && npm run build

开发:npm run dev(需后端 API 或 Vite proxy 配置)