Skip to content

Latest commit

 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 

Repository files navigation

NodePulse

多协议节点聚合、订阅分发与真实测速面板。整个项目只有 index.jspackage.json 两个文件,零第三方依赖,Node.js 18+ 直接跑。

node deps single file license

它解决什么问题

手里攒了一堆节点链接和订阅,想知道哪些还活着、哪些真的快,然后把筛出来的喂给 Clash / sing-box。这类面板不少,NodePulse 想多回答两个问题:

  • 节点回中国这一段有多快。 大部分面板测的是「节点 → Cloudflare」,可你实际用的是「你 → 节点 → 目标站」。境内带宽是另一条腿,量出来才知道慢在哪。
  • 这个节点到底翻不翻得出去。 默认的延迟检测地址 www.gstatic.com/generate_204 在墙内本来就通 —— 所以延迟绿了、评分很高的节点,照样可能打不开 Google。「节点可用」不等于「能翻墙」,这两件事得分开测。

另外这一整套在手机上也能做完:绑一个 Telegram 机器人,加节点、测速、查结果、拿订阅都不用开面板。

功能

节点管理

  • 支持 ss / ssr / vmess / vless / trojan / hysteria / hysteria2(hy2) / tuic
  • 单条粘贴、批量粘贴、远端订阅导入(自动识别 base64、明文链接、Clash YAML)
  • 导入带 SSRF 防护:默认拒绝内网地址,可用 allowPrivateTargets 放开
  • 自动去重,支持启用 / 禁用 / 批量删除
  • 节点地址在界面上打码显示

测速:四趟,顺序有讲究

顺序 测什么 范围 并发
1 延迟 + 抖动 全部节点 满并发
2 墙外带宽 只测连得上的 ≤ 2
3 翻墙探测 只测连得上的 满并发(多地址并行)
4 境内延迟 + 带宽 只测连得上的 ≤ 2

带宽测试会把上行占满。要是把轻量探测和下载塞在一起跑,探测会被挤到超时,然后你看到的结论是「这个节点翻不出去」—— 其实是自己把自己饿死了。所以分四趟。

每轮开始前先量一次本机直连基准,节点的数字才有参照:如果直连本身就是墙外 0/3,那所有节点都 0/3 的时候你知道该去看代理链路,而不是怀疑机场。基准显示在状态栏里。

其他几条:

  • 有 sing-box 内核时走真实代理测速:每个节点起一个 mixed 入站,配一条专属路由规则,兜底出站是个死 socks,防止流量偷偷走直连把速度测虚高。
  • 没有内核(或协议不被内核支持,比如 ssr)就降级成 TCP 握手测延迟,带宽留空、状态标 degraded,不会假装测到了。
  • 内核起不来时用二分法定位到具体是哪个节点的配置有问题,剩下的照测,不会整批放弃。
  • 内核可以自动下载(GitHub Release,支持镜像加速),也可以直接指定本地可执行文件路径。
  • 支持定时自动测速,默认 30 分钟一轮。

评分与订阅

状态判定:

状态 含义
ok 延迟通,且跑出了带宽
degraded 延迟通,但没跑出带宽
fail 一个延迟样本都没拿到

评分由状态权重、带宽、延迟三项加权得出。境内和墙外的数据不参与评分,只做展示 —— 免得你改一下探测地址,订阅里的节点就悄悄换了一批。订阅筛选也只排除 fail

订阅输出四种格式,地址形如 <base>/sub/<token>/<format>

格式 说明
base64 通用 base64 链接列表
clash Clash 配置,含 url-test 自动选择分组
singbox sing-box 配置,含 url-test 分组
plain 明文链接,一行一个

订阅地址带随机 token,可一键轮换(旧地址立刻失效);/sub 有按 IP 的限流。可以选择发全部节点还是只发评分最高的 N 个,以及是否包含还没测过的节点。

Telegram 机器人

不想开面板的时候,用手机把这套东西完整跑一遍:加节点、测速、看结果、拿订阅。

绑定过程不用手抄 chat id:面板「设置 → Telegram 机器人」里填上 @BotFather 给的 Token 并保存,会显示一个 8 位绑定码,去跟机器人说 /bind <绑定码> 就完事了。绑定码一次性,用掉立刻轮换;没绑定的会话什么都问不出来,只会收到一句「只服务已绑定的会话」(一分钟最多一句,免得被当成回声墙)。

命令 说明
/bind <绑定码> 绑定当前会话
/nodes [页码] 列出节点,每行带一个 6 位短号
/add <分享链接> 添加节点,可以多行一次贴一批
/import <订阅地址> 从远端订阅导入
/del/on/off <短号|all> 删除、启用、停用
/test [短号...] 开始一轮检测,不带参数就测全部启用的
/status 当前进度、内核状态、直连基准
/result [短号] 结果总览;带短号看单个节点的明细
/sub [格式] 聚合订阅链接
/unbind 把当前会话从白名单里去掉

直接粘贴分享链接(不带 /add)等同于 /add。节点一律用短号寻址而不是列表序号 —— 序号会随着增删漂移,你照着上一条消息点 /del 3,删掉的可能是别的节点。

几个刻意的取舍:

  • /sub 只发链接,不发节点正文。 链接泄漏了可以一键轮换令牌,正文一旦贴进聊天记录,里面的节点凭据就永久留在 Telegram 的服务器上了。
  • 消息是纯文本,不用 Markdown。 节点名字里带一个 *[ 就会让 Telegram 的解析器整条报错,不值得。回复太长会按行切片,每片 3500 字以内(Telegram 单条上限 4096)。
  • Token 不回显。 存进 config.json(0600),面板只显示 <botId>:***/api/state、审计日志、错误信息里出现的 Token 都会被替换掉。设置里留空表示不改动,要清掉得勾「清除 Token」。
  • 重启会丢掉停机期间的消息。 启动时先 offset=-1 把积压跳过去 —— 半小时前发的 /test 现在补跑没有意义。
  • 走长轮询,不用 webhook。 面板不需要公网入口,也不需要 HTTPS 证书。

api.telegram.org 在中国大陆不通。如果面板跑在境内,把 apiBase 换成你自己的反代地址(Cloudflare Workers 之类都行),否则机器人起不来 —— 状态栏会显示连不上的原因。

面板

  • 单页应用,登录后原地切换,不刷新不跳转
  • 节点表 14 列,境内、墙外各占一列,鼠标悬停能看到每个探测地址的明细(HTTP 状态码、首字节耗时,或者失败原因)
  • 严格 CSP(default-src 'none' + 每请求 nonce),所有 DOM 写入走 textContent
  • 设置页里能试连机器人、重新生成绑定码、把已绑会话一个个点掉
  • 静态资源按内容哈希打指纹,带 ETag 和 304 协商

快速开始

git clone <这个仓库>
cd nodepulse
npm start

首次启动会在服务端日志里打印一次随机初始密码,之后不再打印,记下来。用户名默认 admin

想自己定密码:

# Linux / macOS
NODEPULSE_PASSWORD='换成你的强密码' npm start
# Windows PowerShell
$env:NODEPULSE_PASSWORD = '换成你的强密码'
npm start

然后浏览器打开 http://<你的地址>:26228,登录后:粘贴节点或订阅地址 → 点「开始测速」→ 复制订阅链接。

面板默认监听 0.0.0.0。直接暴露到公网之前,请自己在前面套一层 HTTPS 反向代理,并把配置里的 trustProxy 打开、publicBaseUrl 填上(订阅链接会用它来拼地址)。

想用手机操作的话,去 @BotFather 建个机器人拿到 Token,填进「设置 → Telegram 机器人」保存(Token 只能在面板或 config.json 里配,没有对应的环境变量),然后拿面板上显示的绑定码跟机器人说一句 /bind <绑定码>。命令清单见上面的机器人一节。

环境变量

变量 作用 默认
NODEPULSE_DATA_DIR 数据目录 <项目目录>/data
NODEPULSE_USERNAME 登录用户名 admin
NODEPULSE_PASSWORD 登录密码 首次启动随机生成并打印一次
SERVER_PORT / PORT 监听端口 26228
SERVER_IP 订阅链接里用的对外地址 自动探测
SINGBOX_PATH 指定 sing-box 可执行文件 空(自动下载)
SINGBOX_MIRROR 内核下载镜像前缀
GITHUB_TOKEN 拉 Release 列表时避免被限流

数据目录

data/
├── config.json    # 配置(含 Bot Token,权限 0600)
├── data.json      # 节点、测速历史、订阅 token
├── audit.log      # 审计日志(密码一类字段已脱敏)
├── bin/           # 自动下载的 sing-box
└── run/           # 测速时生成的临时内核配置

老版本留下的 <项目目录>/bin<项目目录>/config.json 仍然认,不用手动迁移。

配置项

面板里改就行,会写进 data/config.json。下面是默认值。

server

默认 说明
port 26228 监听端口
host 0.0.0.0 监听地址
trustProxy false 是否信任 X-Forwarded-For
publicBaseUrl 对外访问地址,影响订阅链接

auth

默认 说明
username admin 用户名
sessionHours 12 会话有效期(小时)
maxLoginFails 5 连续失败几次锁定
lockMinutes 15 锁定时长(分钟)

subscription

默认 说明
mode all all 全发,best 只发最好的几个
bestCount 3 best 模式发几个
includeUntested true 是否包含未测过的节点
cnDirect true 生成的规则里国内流量走直连(GEOIP,CN,DIRECT

test

默认 说明
healthUrl https://www.gstatic.com/generate_204 延迟检测地址
downloadUrl https://speed.cloudflare.com/__down?bytes=20000000 墙外测速文件
latencySamples 3 每个节点测几次延迟
timeoutMs 8000 单次请求超时
downloadSeconds 6 墙外测速最长时间
downloadMaxBytes 8 MB 墙外测速最多下多少
concurrency 4 并发数
batchSize 8 每批起多少个入站
intervalMinutes 30 自动测速间隔
basePort 39100 测速用的本地起始端口

test:境内速度与翻墙探测

默认 说明
cnCheck true 是否测境内延迟与带宽
cnHealthUrl http://connect.rom.miui.com/generate_204 境内延迟地址
cnDownloadUrl https://mirrors.huaweicloud.com/ubuntu/ls-lR.gz 境内测速文件
cnSeconds 3 境内测速最长时间(1–30)
cnMaxBytes 4 MB 境内测速最多下多少
gfwCheck true 是否探测墙外可达性
gfwUrls google / youtube / openai 三个 探测地址,最多 6 个

境内地址拿不到 204 就直接跳过下载 —— 连个空响应都回不来,再等一轮几 MB 的超时纯属浪费时间。

singBox

默认 说明
enabled true 关掉就全部走 TCP 握手测延迟
autoDownload true 自动下载内核
version latest 想锁版本就填 1.14.0 这种
binaryPath 指定本地可执行文件
mirror 下载镜像前缀

importer

默认 说明
allowPrivateTargets false 是否允许导入内网地址的订阅
timeoutMs 15000 拉订阅超时
maxBytes 2 MB 订阅内容大小上限

telegram

默认 说明
enabled false 开关,关掉立刻停掉长轮询
token BotFather 给的 Token,不回显
apiBase https://api.telegram.org Bot API 地址,境内换成反代
notifyOnFinish true 每轮测速跑完主动推结果
requestTimeoutMs 20000 单次 Bot API 请求超时
allowedChats [] 已绑定的会话,只能靠 /bind 或面板增删
bindCode 自动生成 一次性绑定码,绑成功后自动轮换

allowedChatsbindCode 不接受面板的设置提交 —— 白名单只能通过绑定码或者面板上的解绑按钮变动,免得一次误保存把一串会话放进来。

接口

面板自己用的就是这些,写接口需要登录会话,另外还会校验同源。

GET

路径 说明
/healthz 健康检查,不需要登录
/ 面板页面
/assets/app.css/assets/app.js 静态资源,带指纹和 ETag
/sub/sub/<token>/<format> 订阅,按 IP 限流
/api/state 全量状态:节点、进度、配置、订阅链接、机器人状态
/api/audit 审计日志

POST

路径 说明
/api/login/api/logout 登录、登出
/api/nodes/add/api/nodes/import 粘贴添加、远端订阅导入
/api/nodes/toggle/api/nodes/delete 启用/禁用、删除
/api/test 开始一轮测速
/api/settings 保存配置
/api/subscription/rotate 轮换订阅 token
/api/kernel/check 检查/下载内核
/api/telegram/check 试连 Bot API(getMe
/api/telegram/code 重新生成绑定码
/api/telegram/unbind 解绑某个会话,或者全部清空
/api/password 改密码

安全

  • 密码用 scrypt 加盐哈希,不存明文
  • 登录连续失败会按 IP 锁定
  • 会话 cookie 是 HttpOnly + SameSite=Strict,HTTPS 下自动加 Secure
  • 所有写接口双重把关:会话 + 同源校验(CSRF)
  • 响应头:Cache-Control: no-storeX-Content-Type-Options: nosniffReferrer-Policy: no-referrerX-Frame-Options: DENY、COOP/CORP same-originPermissions-Policy,HTTPS 下带 HSTS
  • CSP 是 default-src 'none',脚本靠每请求 nonce 放行,connect-src 'self'frame-ancestors 'none'
  • 审计日志写入前会把密码、token 一类字段替换掉
  • Bot Token 在日志、审计、接口响应、错误信息里都会被换成 <botId>:***
  • 机器人默认谁都不理:只有在白名单里的会话能下命令,绑定码试错按会话限速(10 分钟 5 次)
  • 导入订阅默认拒绝内网地址,防止拿面板当内网扫描器

几点说明

  • 测速要占满带宽,跑的时候别指望同时干别的。
  • 翻墙探测的判定是「拿到任何 HTTP 状态码就算通过」。api.openai.com 未鉴权返回 401 也算翻出去了 —— 真被墙的站点连状态码都拿不到,只会是 RST 或者超时。
  • 境内和墙外的结果不影响评分,也不影响订阅筛选,纯展示。
  • ssr 没法走内核测速,只会有 TCP 延迟。

开发

npm run check   # 等价于 node --check index.js

index.js 里的 HTML、CSS、前端 JS 都是模板字符串。在这些模板字符串里写反斜杠要写两遍'\\n'/[\\s,]+/),少一个就被外层模板吃掉,前端整个脚本会报 Invalid or unexpected token

License

MIT

About

使用翼龙面板搭建多协议节点聚合、订阅分发与真实测速面板。整个项目只有 `index.js` 和 `package.json` 两个文件,零第三方依赖,Node.js 18+ 直接跑。并可直接连接telegram bot进行操作

Resources

Stars

7 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages