STUN Nav
一个给 Lucky STUN 使用的个人导航页。
Lucky 的 STUN 外网地址变化后,通过 webhook 把最新地址推送到这个项目;导航页和后台会读取保存的最新地址,点击卡片即可跳转到当前可用入口。
项目支持两种部署方式:
- 本地 / 家中服务器部署:
server.mjs+data/links.json - EdgeOne Pages 部署:静态页面 + Edge Functions + KV
功能
- 自动接收 Lucky webhook,更新导航地址
- 首页按分组展示服务卡片
- 后台
/admin手动新增、编辑、删除导航 - 后台修改页面标题、顶部标识、首页标题、首页副标题
- 支持 GET、POST JSON、表单等多种 webhook 方式
- 支持 EdgeOne Pages 无服务器部署
项目结构
.
├── public/ # 前端页面
│ ├── index.html # 首页
│ ├── admin.html # 后台
│ ├── app.js
│ ├── admin.js
│ └── styles.css
├── server.mjs # 本地 Node 服务
├── data/links.json # 本地数据文件,已被 .gitignore 忽略
├── edge-functions/api/[[default]].js # EdgeOne API
├── edgeone.json # EdgeOne Pages 配置
├── EDGEONE.md # EdgeOne 部署说明
└── package.json
本地部署
适合部署在家中服务器、NAS、VPS、Docker 容器等环境。
1. 安装 Node.js
需要 Node.js 18 或更高版本。
检查版本:
node -v
2. 启动服务
WEBHOOK_TOKEN=换成你自己的随机密钥 PORT=8080 npm start
参数说明:
| 参数 | 说明 |
|---|---|
WEBHOOK_TOKEN |
管理和 webhook 密钥,必须设置成随机字符串 |
PORT |
服务端口,默认 8080 |
启动后访问:
http://服务器IP:8080/
http://服务器IP:8080/admin
3. 本地数据保存位置
本地版本数据保存在:
data/links.json
这个文件包含导航信息和页面设置,默认不会提交到 GitHub。
4. 反向代理建议
如果你用 Nginx、Caddy、Lucky Web 服务等反向代理,建议:
- 使用 HTTPS
- 不要把后台裸露给陌生人
WEBHOOK_TOKEN使用足够长的随机字符串
EdgeOne Pages 部署
适合让导航页本身拥有稳定公网入口,例如:
https://dh.example.com/
https://dh.example.com/admin
1. 导入仓库
在 EdgeOne Pages / Makers 中创建项目,导入本仓库。
项目已经包含:
edgeone.json
edge-functions/api/[[default]].js
edgeone.json 会把静态输出目录设置为:
public
并把:
/admin
重写到:
/admin.html
2. 创建 KV
创建一个 KV Namespace,并绑定到 Pages 项目。
绑定变量名必须填写:
STUN_NAV_KV
EdgeOne 版本会把所有数据保存到 KV 的这个 key:
stun_nav_store
3. 配置环境变量
在 EdgeOne 项目的环境变量中配置:
WEBHOOK_TOKEN=换成你自己的随机密钥
这个密钥用于:
- Lucky webhook 更新导航
- 后台保存页面设置
- 后台新增、编辑、删除导航
4. 部署后访问
https://你的域名/
https://你的域名/admin
首次打开 /admin,填写 WEBHOOK_TOKEN,之后密钥只会保存在当前浏览器本地。
后台使用
后台地址:
/admin
后台包含:
页面设置:修改浏览器标题、顶部标识、首页标题、首页副标题新增导航:手动新增一个导航入口导航列表:查看、编辑、删除已有导航
字段说明:
| 字段 | 说明 |
|---|---|
ID |
导航唯一标识,例如 alist、fnnas |
名称 |
首页显示名称 |
分组 |
首页卡片分组 |
地址 |
点击跳转地址 |
备注 |
可选说明 |
如果编辑时修改了 ID,系统会保存新 ID,并删除旧 ID 对应的记录。
Lucky Webhook 配置
Webhook 接口格式:
/api/webhook/:id
例如:
/api/webhook/alist
/api/webhook/fnnas
其中 alist、fnnas 就是导航项 ID。
方式一:GET URL 参数
这是最简单的方式。
接口地址:
https://你的域名/api/webhook/fnnas?token=你的随机密钥&name=FNNAS&group=NAS&url=http://{STUN_你的STUN规则名_ADDR}
请求方法:
GET
请求头:
不用填
方式二:POST JSON
接口地址:
https://你的域名/api/webhook/fnnas?token=你的随机密钥
请求方法:
POST
请求头:
Content-Type: application/json
请求体:
{
"name": "FNNAS",
"group": "NAS",
"url": "http://{STUN_你的STUN规则名_ADDR}",
"note": "家里 NAS"
}
方式三:拆分 host 和 port
如果你想分别传 IP 和端口:
{
"name": "FNNAS",
"group": "NAS",
"protocol": "http",
"host": "{STUN_你的STUN规则名_IP}",
"port": "{STUN_你的STUN规则名_PORT}"
}
最终会拼成:
http://IP:PORT
Lucky STUN 变量说明
Lucky 的 STUN 全局变量不是系统环境变量,而是 Lucky webhook 中可用的占位符。
格式:
{STUN_规则名_ADDR}
{STUN_规则名_IP}
{STUN_规则名_PORT}
这里的 规则名 必须替换成 Lucky 里 STUN 穿透规则列表中的真实规则名。
例如:
STUN 规则名:fnnas
完整地址:{STUN_fnnas_ADDR}
IP:{STUN_fnnas_IP}
端口:{STUN_fnnas_PORT}
如果你的规则名是:
AList-Web
就写:
{STUN_AList-Web_ADDR}
Webhook 字段
| 字段 | 说明 | 示例 |
|---|---|---|
id |
服务唯一标识,也可写在 /api/webhook/:id |
fnnas |
name |
页面显示名称 | FNNAS |
group |
页面分组 | NAS |
url |
完整跳转地址 | http://1.2.3.4:12345 |
addr |
地址加端口 | 1.2.3.4:12345 |
host / ip |
主机或 IP | 1.2.3.4 |
port |
端口 | 12345 |
protocol / scheme |
协议,默认 http |
https |
path |
跳转路径 | /admin |
note |
备注 | 家里 NAS |
如果同时提供多个地址字段,优先级是:
url > addr > host/ip + port
API 说明
获取导航列表
curl "https://你的域名/api/services"
返回页面设置和导航列表。
新增或更新导航
curl -X POST "https://你的域名/api/webhook/fnnas?token=你的随机密钥" \
-H "Content-Type: application/json" \
--data '{"name":"FNNAS","group":"NAS","url":"http://1.2.3.4:12345"}'
删除导航
curl -X DELETE "https://你的域名/api/services/fnnas?token=你的随机密钥"
修改页面设置
curl -X PUT "https://你的域名/api/settings?token=你的随机密钥" \
-H "Content-Type: application/json" \
--data '{"browserTitle":"导航","brand":"HOME","title":"家庭服务","subtitle":"我的服务入口"}'
常见问题
1. 只访问 /api/webhook/fnnas?token=xxx 为什么新增失败?
因为只传了 ID 和 token,没有传导航地址。
至少需要传 url、addr,或者 host + port。
正确示例:
https://你的域名/api/webhook/fnnas?token=xxx&name=FNNAS&url=http://{STUN_fnnas_ADDR}
2. EdgeOne 上返回 KV 相关错误怎么办?
检查 KV 是否已绑定,绑定变量名必须是:
STUN_NAV_KV
3. 后台提示 invalid webhook token 怎么办?
检查后台输入的密钥是否等于部署时设置的:
WEBHOOK_TOKEN
4. Lucky 变量没有替换怎么办?
确认 Lucky webhook 配置位置支持全局变量,并确认 STUN 规则名完全一致。
例如规则名是 fnnas,就写:
{STUN_fnnas_ADDR}
不要写成:
{STUN_你的服务名_ADDR}
除非你的 STUN 规则名真的叫 你的服务名。
5. 页面看起来没有更新怎么办?
浏览器可能缓存了旧资源,可以尝试:
https://你的域名/admin?fresh=1
或者强制刷新浏览器。
项目的静态资源已经加了版本号,并在 edgeone.json 中设置了 Cache-Control: no-cache。
安全建议
- 不要使用简单密码当
WEBHOOK_TOKEN - 不要把
WEBHOOK_TOKEN发给别人 - 如果密钥泄露,立即在部署平台更换
- 后台
/admin暴露在公网时,建议配合 EdgeOne 访问控制或其他鉴权 - 本地部署时不要把
data/links.json提交到公开仓库
开发
本地启动:
npm start
开发模式:
npm run dev
语法检查:
node --check server.mjs
node --check public/app.js
node --check public/admin.js
node --check 'edge-functions/api/[[default]].js'