462 lines
8.6 KiB
Markdown
462 lines
8.6 KiB
Markdown
# 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'
|
||
```
|