From 5eaa7dcc945590c96e28e6924458fef1e75f5891 Mon Sep 17 00:00:00 2001 From: zeer Date: Sun, 12 Jul 2026 17:13:38 +0800 Subject: [PATCH] Expand deployment and usage documentation --- EDGEONE.md | 73 ++++++--- README.md | 466 ++++++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 461 insertions(+), 78 deletions(-) diff --git a/EDGEONE.md b/EDGEONE.md index a9016f4..890cd66 100644 --- a/EDGEONE.md +++ b/EDGEONE.md @@ -1,63 +1,88 @@ # EdgeOne Pages 部署说明 -这个项目已经包含 EdgeOne Pages 版本: +这是 STUN Nav 的 EdgeOne Pages 专用部署说明。完整使用文档见 `README.md`。 -- 静态页面:`public/` -- Edge Functions:`edge-functions/api/[[default]].js` -- EdgeOne 配置:`edgeone.json` +## 文件结构 -## 需要在 EdgeOne 配置的内容 +```text +public/ # 静态页面 +edge-functions/api/[[default]].js # /api/* 接口 +edgeone.json # EdgeOne 配置 +``` -1. 创建一个 EdgeOne Pages / Makers 项目,并导入这个仓库。 -2. 创建并绑定 KV Namespace。 -3. 绑定 KV 时,变量名填写: +## 部署步骤 + +1. 在 EdgeOne Pages / Makers 中创建项目。 +2. 导入 GitHub 仓库。 +3. 确认构建输出目录为: + +```text +public +``` + +4. 创建 KV Namespace。 +5. 给项目绑定 KV,变量名填写: ```text STUN_NAV_KV ``` -4. 配置环境变量: +6. 添加环境变量: ```text WEBHOOK_TOKEN=换成你自己的随机密钥 ``` -5. 部署后访问: +7. 部署项目。 + +## 访问地址 ```text https://你的域名/ https://你的域名/admin ``` -## Lucky Webhook 示例 +## Lucky Webhook -简单 URL 参数方式: +GET 参数方式: ```text -https://你的域名/api/webhook/alist?token=你的随机密钥&name=AList&group=文件&url=http://{STUN_你的STUN规则名_ADDR} +https://你的域名/api/webhook/fnnas?token=你的随机密钥&name=FNNAS&group=NAS&url=http://{STUN_fnnas_ADDR} ``` -JSON 方式: +POST JSON 方式: ```text -URL: https://你的域名/api/webhook/alist?token=你的随机密钥 +URL: https://你的域名/api/webhook/fnnas?token=你的随机密钥 Method: POST Content-Type: application/json Body: { - "name": "AList", - "group": "文件", - "url": "http://{STUN_你的STUN规则名_ADDR}", - "note": "家中文件服务" + "name": "FNNAS", + "group": "NAS", + "url": "http://{STUN_fnnas_ADDR}", + "note": "家里 NAS" } ``` ## 数据存储 -EdgeOne 版本不再使用 `data/links.json`,所有数据会存到 KV 的 `stun_nav_store` 这个 key 里。 +EdgeOne 版本的数据保存在 KV 的这个 key 中: -本地 Node 版本仍然保留,可以继续用: - -```bash -WEBHOOK_TOKEN=你的随机密钥 PORT=8080 npm start +```text +stun_nav_store ``` + +不会使用本地的 `data/links.json`。 + +## 故障排查 + +如果 `/api/services` 报 KV 错误,优先检查 KV 绑定变量名是不是: + +```text +STUN_NAV_KV +``` + +如果 webhook 返回 `missing service url, addr, or host+port`,说明请求里没有传服务地址,需要增加 `url`、`addr`,或 `host + port`。 + +如果后台保存失败,检查输入的管理密钥是否和环境变量 `WEBHOOK_TOKEN` 一致。 diff --git a/README.md b/README.md index fad8b1f..50f3e56 100644 --- a/README.md +++ b/README.md @@ -1,62 +1,291 @@ -# STUN 导航页 +# STUN Nav -一个给 Lucky STUN 使用的个人导航页。Lucky 的 STUN 地址变化后,通过 webhook 把最新地址推到这里;浏览器打开导航页时,按钮会跳转到最新地址。 +一个给 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 -WEBHOOK_TOKEN=换成一串随机密钥 PORT=8080 npm start +node -v ``` -然后访问: +### 2. 启动服务 -```text -http://服务器IP:8080 +```bash +WEBHOOK_TOKEN=换成你自己的随机密钥 PORT=8080 npm start ``` -管理页: +参数说明: + +| 参数 | 说明 | +| --- | --- | +| `WEBHOOK_TOKEN` | 管理和 webhook 密钥,必须设置成随机字符串 | +| `PORT` | 服务端口,默认 `8080` | + +启动后访问: ```text +http://服务器IP:8080/ http://服务器IP:8080/admin ``` -打开管理页后填写 `WEBHOOK_TOKEN`,即可手动新增、修改、删除导航项,也可以修改浏览器标题、顶部标识、首页标题和副标题。密钥只保存在当前浏览器本地。 +### 3. 本地数据保存位置 -## 部署到 EdgeOne Pages +本地版本数据保存在: -项目已经包含 EdgeOne Pages 改造版本,见 `EDGEONE.md`。 +```text +data/links.json +``` -核心变化: +这个文件包含导航信息和页面设置,默认不会提交到 GitHub。 -- 静态页面从 `public/` 部署 -- `/api/*` 由 `edge-functions/api/[[default]].js` 提供 -- 数据保存到 EdgeOne KV,绑定变量名需要设置为 `STUN_NAV_KV` -- 管理密钥使用环境变量 `WEBHOOK_TOKEN` +### 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 配置 -推荐让 Lucky 发送 JSON: +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 -URL: http://你的导航页地址:8080/api/webhook/alist?token=换成一串随机密钥 -Method: POST Content-Type: application/json -Body: +``` + +请求体: + +```json { - "name": "AList", - "group": "文件", + "name": "FNNAS", + "group": "NAS", "url": "http://{STUN_你的STUN规则名_ADDR}", - "note": "家中文件服务" + "note": "家里 NAS" } ``` -也可以不用 JSON,直接用 URL 参数: +### 方式三:拆分 host 和 port -```text -http://你的导航页地址:8080/api/webhook/alist?token=换成一串随机密钥&name=AList&group=文件&url=http://{STUN_你的STUN规则名_ADDR} +如果你想分别传 IP 和端口: + +```json +{ + "name": "FNNAS", + "group": "NAS", + "protocol": "http", + "host": "{STUN_你的STUN规则名_IP}", + "port": "{STUN_你的STUN规则名_PORT}" +} ``` -Lucky 的全局变量需要把 `规则名` 替换成 STUN 穿透规则列表里的真实规则名,不是导航页服务名,也不是环境变量。例如你的 STUN 规则名叫 `alist-web`,就写 `{STUN_alist-web_ADDR}`。 +最终会拼成: + +```text +http://IP:PORT +``` + +## Lucky STUN 变量说明 + +Lucky 的 STUN 全局变量不是系统环境变量,而是 Lucky webhook 中可用的占位符。 + +格式: ```text {STUN_规则名_ADDR} @@ -64,40 +293,169 @@ Lucky 的全局变量需要把 `规则名` 替换成 STUN 穿透规则列表里 {STUN_规则名_PORT} ``` -## Webhook 支持的字段 +这里的 `规则名` 必须替换成 Lucky 里 STUN 穿透规则列表中的真实规则名。 -| 字段 | 说明 | -| --- | --- | -| `id` | 服务唯一标识;也可以写在 `/api/webhook/:id` 里 | -| `name` | 页面显示名称 | -| `group` | 页面分组 | -| `url` | 完整跳转地址,例如 `http://1.2.3.4:12345` | -| `addr` | 地址加端口,例如 `1.2.3.4:12345` | -| `host` / `ip` + `port` | 分开发送主机和端口 | -| `protocol` / `scheme` | `http` 或 `https`,默认 `http` | -| `path` | 跳转路径,例如 `/admin` | -| `note` | 备注 | +例如: -收到的数据会保存在 `data/links.json`。 - -## 删除导航 - -删除某个导航项时,用它的 `id` 调用删除接口。`id` 默认来自 `/api/webhook/:id` 里的那一段,例如 `/api/webhook/alist` 创建的导航,删除时就是: - -```bash -curl -X DELETE "http://你的导航页地址:8080/api/services/alist?token=换成一串随机密钥" +```text +STUN 规则名:fnnas +完整地址:{STUN_fnnas_ADDR} +IP:{STUN_fnnas_IP} +端口:{STUN_fnnas_PORT} ``` -删除刚才测试的百度示例: +如果你的规则名是: -```bash -curl -X DELETE "http://你的导航页地址:8080/api/services/baidu?token=换成一串随机密钥" +```text +AList-Web ``` -如果不想调用接口,也可以停掉服务后直接编辑 `data/links.json`,删除对应对象,再重新启动服务。 +就写: + +```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`。 -- 不建议直接把导航页暴露到公网裸奔,最好放到 HTTPS 反代后面。 -- 如果导航页本身也通过 STUN 暴露,至少给入口加访问控制或复杂路径。 +- 不要使用简单密码当 `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' +```