Plain:给哪吒 v0 面板换一个前端 —— 功能介绍与 Docker 部署教程

哪吒面板(本文以 nezha-zero v0.20.29 为例)自带的前端胜在稳定,但界面比较「原生」:想一眼看全所有节点的速率、流量、价格和到期,再配上像样的延迟图与流媒体解锁状态,就得自己动手换前端。Plain 就是这样一个第三方前端主题:基于开源项目 monitor-theme-design(作者 tom2almighty,MIT 协议)深度定制,整页扁平化,带 7 套配色主题、顶部统计栏和点阵世界地图,节点详情里的解锁与已部署业务都是到节点上实查的。成品跑在 plain.passall.us,这篇文章把它的功能和完整部署方法一次讲清。

一、功能介绍

7 套配色主题。冰蓝、绯红、翡翠、琥珀、暮紫(默认)、霓虹、碳黑,每套都有浅色与深色两种模式,顶栏可切换浅色 / 深色 / 跟随系统,选择会记住,下次打开还是你选的那套。

整页扁平化。去掉全部装饰阴影、渐变、毛玻璃与顶部高光,一律实色,看久了不累眼;选中的分组标签保持蓝色实色,一眼能认出当前在看哪个分组。

顶部统计栏。在线节点数、全网实时网速、累计流量、最忙(🔥)、最快(⚡)一字排开,全部按当前选中的分组统计,切换分组即时重算——分组看板不用再自己心算总量。

节点列表。一行一台机器:状态、名称、位置、系统、在线时长、负载、网速(实时上下行)、流量(累计上下行)、CPU、内存、硬盘、续费、到期。本组最忙的节点标 🔥、最快标 ⚡,状态点和行名同步变色;点任意一行展开详情,同时只展开一个,不会越点越乱。

点阵世界地图。列表下方是一张 canvas 点阵世界地图,节点按国家 / 城市坐标落点,在线绿圈、离线红圈,最忙与最快同样带 🔥⚡ 脉动;切换分组时地图标记跟随当前分组。

详情 4 个标签页。网络延迟(面板监控任务的历史曲线,1 小时 / 6 小时 / 24 小时三档,外加前端从实时流自攒的资源图)、系统概览、解锁、已部署业务。

解锁看状态。Netflix、Disney+、HBO Max、YouTube、ChatGPT、Claude、Gemini 等主流服务,IPv4 一行、IPv6 一行分开列;节点 IP 在前端脱敏(v4 只显前两段,v6 只显前缀),截图分享出去也不怕泄露地址。

已部署业务是实查的。不是一张写死的静态清单,而是真的到节点上查服务在不在跑:✓ 在跑 / ✗ 停了 / ? 未知,附上次检测时间;点「重新检测」排队重查,查完自动刷新;实查中新发现的服务会自动上榜,不用改前端代码。

流量校准。面板的累计流量跟商家面板对不上时(换 IP、重装、统计口径差异都很常见),配套的校准服务可以按月写入校准值修正,校准密码只存哈希。

二、部署教程

准备工作:一台在跑的 nezha-zero 面板(dashboard,按 v0.20.x 开发验证;v1 的接口与页面结构不同,不适用),一台能跑 Docker 的主机,本地构建需要 Node.js(建议 20+)。本项目只是公开页前端,不含面板本身。

第 1 步:构建前端

git clone https://github.com/ypkin/plain-nezha.git
cd plain-nezha
npm ci
npm run build

产物在 dist/ 目录(不进 git)。注意:构建产物里没有写死任何面板地址,面板地址是容器启动时才注入的环境变量,以后换面板只改配置重启容器,不用重新构建。

第 2 步:改部署配置

打开 deploy/docker-compose.yml,把环境变量改成你自己的:

services:
  design:
    build:
      context: ..
      dockerfile: deploy/Dockerfile
    image: plain-frontend:latest
    container_name: plain-frontend
    restart: unless-stopped
    ports:
      - "127.0.0.1:8086:80"
    environment:
      # 哪吒面板地址,必须以 / 结尾;按你自己的面板地址改
      - NEZHA=http://127.0.0.1:8008/
      - NEZHA_ORIGIN=http://127.0.0.1:8008
      # 以下三个是本站点之外的配套小服务(均可留空对应功能、或删掉模板里对应 location):
      # 流量校准服务、解锁聚合服务、已部署业务汇总服务
      - CAL=http://127.0.0.1:8090/
      - UNLOCK=http://127.0.0.1:8091/
      - SERVICES=http://127.0.0.1:8092/
  • NEZHA 是面板地址,必须以 / 结尾。同机部署就填 http://127.0.0.1:8008/ 这类内网地址,不需要把面板端口暴露到公网。
  • NEZHA_ORIGIN 是面板主域名(不带尾斜杠),登录回调透传到这里。
  • CAL / UNLOCK / SERVICES 是三个配套小服务(流量校准、解锁聚合、业务汇总),不在本仓库内;没有就不配,但要把 deploy/default.conf.template 里对应的 location 整段删掉,只留空不删段容器会启动报错。

第 3 步:启动容器

docker compose -f deploy/docker-compose.yml up -d --build

容器监听 127.0.0.1:8086,本身不终结 TLS,下一步用你自己的反代对外发布。

第 4 步:反代域名

server {
    listen 443 ssl;
    server_name <your-frontend-domain>;

    location / {
        proxy_pass http://127.0.0.1:8086;
        proxy_http_version 1.1;
        # /ws 是 WebSocket,这两行必须透传,否则无法升级成 ws 连接
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $http_host;
    }
}

注意两点:/ws 是 WebSocket,Upgrade 两行必须透传;Host 一定要用 $http_host(保留端口),原因见下一节——这是整套部署最容易踩的坑。

三、对接面板的几个要点

架构只有一句话:前端是纯静态 SPA,浏览器永远不直连面板。所有面板数据都走容器内 nginx 同源透传,既没有跨域问题,面板的真实地址也不会暴露给访客。前端一共用 6 条通道对接面板:

  • GET /panel-snapshot 抓面板首页 HTML,提取内嵌数据包做首屏:节点列表、每个节点的 PublicNote 计费备注、站点名都来自这里(实时流里没有 PublicNote,这条不能少)。
  • WSS /ws 是面板的实时推送:在线状态、网速、流量、负载全靠它;断流 30 秒收不到数据就切 5 秒轮询快照兜底。
  • GET /api/v1/monitor/{id} 取延迟历史,也就是详情页那条延迟曲线;面板里没配监控任务就没有曲线。
  • /static/ 透传面板静态资源,快照页引用的脚本和样式走这里。
  • /oauth2 透传到面板主域名,处理 GitHub OAuth 等登录回调。
  • /login、/admin、/dashboard 不透传,直接 302 跳面板主域名。

登录为什么必须跳走?面板的 OAuth 回调只登记了一个域名,登录 cookie 也种在发起登录的那个域名上。如果在前端域名下透传 /login,回调落点和 cookie 对不上,面板会报「登录过程中发生错误」。所以顶栏的登录 / 后台入口只是把你送到面板域名去登录,本前端自身没有任何登录态。

/ws 的 Host 必须带端口。nezha-zero 的 /ws 握手做 Origin 检查:浏览器 Origin 的 host 必须等于转发后的请求 Host。nginx 的 $host 会剥掉端口——浏览器连的是 wss://域名:8443/ws,转发的 Host 却成了不带端口的域名,对不上就 403。表现很有迷惑性:快照是普通 HTTP 不受影响,所以列表数据一切正常,只有实时状态全灭、全节点看着离线。修法就是模板里那样,一律 proxy_set_header Host $http_host。走标准 443/80 时两者相同不会触发,用 IP:端口或非标准端口访问时必踩。

联调检查清单(按顺序查):① 首页能列出节点 → 快照通道通;② 等几秒网速数字会变 → /ws 通,数字不动先看浏览器控制台 ws 是不是 403;③ 点详情有延迟曲线 → monitor 接口通且面板配了监控任务;④ 点顶栏用户图标能跳到面板域名登录页 → 登录跳转通。

四、说明与致谢

Plain 基于 monitor-theme-design 定制,MIT 协议开源,页脚保留原作者 tom2almighty 署名;面板本体是 nezha-zero(v0 系列)。节点的计费信息(价格、到期、流量套餐、国家)来自面板里每个节点的 PublicNote 字段,没填的节点显示为 Free / ∞。配套的流量校准、解锁聚合、业务汇总三个小服务不在本仓库内,需要另外部署,没有它们时对应标签页和按钮自动不出现,不影响列表与详情的基本功能。