Files
stun-nav/README.md
2026-07-12 17:13:38 +08:00

462 lines
8.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 无服务器部署
## 项目结构
```text
.
├── 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 或更高版本。
检查版本:
```bash
node -v
```
### 2. 启动服务
```bash
WEBHOOK_TOKEN=换成你自己的随机密钥 PORT=8080 npm start
```
参数说明:
| 参数 | 说明 |
| --- | --- |
| `WEBHOOK_TOKEN` | 管理和 webhook 密钥,必须设置成随机字符串 |
| `PORT` | 服务端口,默认 `8080` |
启动后访问:
```text
http://服务器IP:8080/
http://服务器IP:8080/admin
```
### 3. 本地数据保存位置
本地版本数据保存在:
```text
data/links.json
```
这个文件包含导航信息和页面设置,默认不会提交到 GitHub。
### 4. 反向代理建议
如果你用 Nginx、Caddy、Lucky Web 服务等反向代理,建议:
- 使用 HTTPS
- 不要把后台裸露给陌生人
- `WEBHOOK_TOKEN` 使用足够长的随机字符串
## EdgeOne Pages 部署
适合让导航页本身拥有稳定公网入口,例如:
```text
https://dh.example.com/
https://dh.example.com/admin
```
### 1. 导入仓库
在 EdgeOne Pages / Makers 中创建项目,导入本仓库。
项目已经包含:
```text
edgeone.json
edge-functions/api/[[default]].js
```
`edgeone.json` 会把静态输出目录设置为:
```text
public
```
并把:
```text
/admin
```
重写到:
```text
/admin.html
```
### 2. 创建 KV
创建一个 KV Namespace,并绑定到 Pages 项目。
绑定变量名必须填写:
```text
STUN_NAV_KV
```
EdgeOne 版本会把所有数据保存到 KV 的这个 key:
```text
stun_nav_store
```
### 3. 配置环境变量
在 EdgeOne 项目的环境变量中配置:
```text
WEBHOOK_TOKEN=换成你自己的随机密钥
```
这个密钥用于:
- Lucky webhook 更新导航
- 后台保存页面设置
- 后台新增、编辑、删除导航
### 4. 部署后访问
```text
https://你的域名/
https://你的域名/admin
```
首次打开 `/admin`,填写 `WEBHOOK_TOKEN`,之后密钥只会保存在当前浏览器本地。
## 后台使用
后台地址:
```text
/admin
```
后台包含:
- `页面设置`:修改浏览器标题、顶部标识、首页标题、首页副标题
- `新增导航`:手动新增一个导航入口
- `导航列表`:查看、编辑、删除已有导航
字段说明:
| 字段 | 说明 |
| --- | --- |
| `ID` | 导航唯一标识,例如 `alist`、`fnnas` |
| `名称` | 首页显示名称 |
| `分组` | 首页卡片分组 |
| `地址` | 点击跳转地址 |
| `备注` | 可选说明 |
如果编辑时修改了 `ID`,系统会保存新 ID,并删除旧 ID 对应的记录。
## Lucky Webhook 配置
Webhook 接口格式:
```text
/api/webhook/:id
```
例如:
```text
/api/webhook/alist
/api/webhook/fnnas
```
其中 `alist`、`fnnas` 就是导航项 ID。
### 方式一:GET URL 参数
这是最简单的方式。
接口地址:
```text
https://你的域名/api/webhook/fnnas?token=你的随机密钥&name=FNNAS&group=NAS&url=http://{STUN_你的STUN规则名_ADDR}
```
请求方法:
```text
GET
```
请求头:
```text
不用填
```
### 方式二:POST JSON
接口地址:
```text
https://你的域名/api/webhook/fnnas?token=你的随机密钥
```
请求方法:
```text
POST
```
请求头:
```text
Content-Type: application/json
```
请求体:
```json
{
"name": "FNNAS",
"group": "NAS",
"url": "http://{STUN_你的STUN规则名_ADDR}",
"note": "家里 NAS"
}
```
### 方式三:拆分 host 和 port
如果你想分别传 IP 和端口:
```json
{
"name": "FNNAS",
"group": "NAS",
"protocol": "http",
"host": "{STUN_你的STUN规则名_IP}",
"port": "{STUN_你的STUN规则名_PORT}"
}
```
最终会拼成:
```text
http://IP:PORT
```
## Lucky STUN 变量说明
Lucky 的 STUN 全局变量不是系统环境变量,而是 Lucky webhook 中可用的占位符。
格式:
```text
{STUN_规则名_ADDR}
{STUN_规则名_IP}
{STUN_规则名_PORT}
```
这里的 `规则名` 必须替换成 Lucky 里 STUN 穿透规则列表中的真实规则名。
例如:
```text
STUN 规则名:fnnas
完整地址:{STUN_fnnas_ADDR}
IP:{STUN_fnnas_IP}
端口:{STUN_fnnas_PORT}
```
如果你的规则名是:
```text
AList-Web
```
就写:
```text
{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` |
如果同时提供多个地址字段,优先级是:
```text
url > addr > host/ip + port
```
## API 说明
### 获取导航列表
```bash
curl "https://你的域名/api/services"
```
返回页面设置和导航列表。
### 新增或更新导航
```bash
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"}'
```
### 删除导航
```bash
curl -X DELETE "https://你的域名/api/services/fnnas?token=你的随机密钥"
```
### 修改页面设置
```bash
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`。
正确示例:
```text
https://你的域名/api/webhook/fnnas?token=xxx&name=FNNAS&url=http://{STUN_fnnas_ADDR}
```
### 2. EdgeOne 上返回 KV 相关错误怎么办?
检查 KV 是否已绑定,绑定变量名必须是:
```text
STUN_NAV_KV
```
### 3. 后台提示 invalid webhook token 怎么办?
检查后台输入的密钥是否等于部署时设置的:
```text
WEBHOOK_TOKEN
```
### 4. Lucky 变量没有替换怎么办?
确认 Lucky webhook 配置位置支持全局变量,并确认 STUN 规则名完全一致。
例如规则名是 `fnnas`,就写:
```text
{STUN_fnnas_ADDR}
```
不要写成:
```text
{STUN_你的服务名_ADDR}
```
除非你的 STUN 规则名真的叫 `你的服务名`。
### 5. 页面看起来没有更新怎么办?
浏览器可能缓存了旧资源,可以尝试:
```text
https://你的域名/admin?fresh=1
```
或者强制刷新浏览器。
项目的静态资源已经加了版本号,并在 `edgeone.json` 中设置了 `Cache-Control: no-cache`。
## 安全建议
- 不要使用简单密码当 `WEBHOOK_TOKEN`
- 不要把 `WEBHOOK_TOKEN` 发给别人
- 如果密钥泄露,立即在部署平台更换
- 后台 `/admin` 暴露在公网时,建议配合 EdgeOne 访问控制或其他鉴权
- 本地部署时不要把 `data/links.json` 提交到公开仓库
## 开发
本地启动:
```bash
npm start
```
开发模式:
```bash
npm run dev
```
语法检查:
```bash
node --check server.mjs
node --check public/app.js
node --check public/admin.js
node --check 'edge-functions/api/[[default]].js'
```