# 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' ```