Fix node id collision, host normalization and blank error text

Three defects made the panel look broken to the operator:

- POST /api/proxies with a Chinese name slugged the id down to its ASCII
  digits ("香港01" -> "01"), collided with an existing node and returned
  HTTP 500 "proxy id already exists". Non-ASCII names now get sequential
  node-N ids; ASCII names keep a readable slug. Both paths de-duplicate.
- A host pasted as a URL ("http://192.168.3.71") went straight to DNS, so
  every check failed with "[Errno -2] Name does not resolve". normalize_host
  accepts host, host:port, scheme://user:pass@host:port and [IPv6]:port, and
  existing configs are healed at startup.
- str(asyncio.TimeoutError()) is empty, so a black-holed proxy showed a blank
  error in the UI and looked like an internal bug. describe_exc always
  produces a reason.

Also:
- Expected validation failures return 400 with an error message instead of
  500 + traceback; the frontend surfaces every API error as a toast.
- PUT/DELETE on an unknown proxy id return 404 instead of silently creating
  or reporting ok=false with 200.
- POST /api/check/<id> forces a check on disabled nodes instead of returning
  ok=true without doing anything.
- Port range is validated (1..65535).
- Store.close() lets tests release SQLite handles.
- 33 unittest cases cover id generation, host normalization, error text,
  startup migration and manual checks.
This commit is contained in:
Hermes
2026-08-23 14:07:37 +08:00
parent 68ad064f80
commit ce0aa22672
8 changed files with 468 additions and 26 deletions
+41 -1
View File
@@ -101,7 +101,6 @@ PUT /api/settings
DELETE /api/logs
POST /api/check/<id>
POST /api/notify/test
GET /api/proxies
POST /api/proxies
PUT /api/proxies/<id>
DELETE /api/proxies/<id>
@@ -112,12 +111,53 @@ DELETE /api/channels/<id>
POST /api/channels/<id>/test
```
节点列表通过 `GET /api/status` 返回(含运行状态和最近记录),没有单独的
`GET /api/proxies`。参数校验失败返回 `400` 并带 `error` 说明,仅未预期的内部
错误返回 `500`
## 节点字段说明
### Host 会被自动归一化
`host` 支持直接粘贴以下形式,服务端会拆成主机名 + 端口:
```text
192.168.1.10
192.168.1.10:7890
http://192.168.1.10:7890
socks5://user:pass@192.168.1.10:1080
[2001:db8::1]:1080
```
未显式指定 `port`(或仍为默认 `1080`)时采用 URL 中的端口。启动时也会自动修正
历史配置里残留的带协议前缀的 `host`,避免出现
`[Errno -2] Name does not resolve`
### 节点 id 生成规则
未显式提供 `id` 时由 `name` 推导:纯 ASCII 名字保留可读 slug(`HK Node 01`
`HK-Node-01`),含中文/emoji 的名字分配递增 `node-N`。两种路径都会自动去重,
不会因重名报错。`PUT` 编辑不会改变已有 id。
## 测试
```bash
python3 -m unittest discover -s tests
```
## Docker 镜像
```text
192.168.2.66:80/hermes/socks5-monitor:latest
```
上游基础镜像(`python:3.11-alpine`)拉取受限时,可用 `Dockerfile.patch` 在上一版
镜像之上只叠加应用源码做增量构建:
```bash
docker build -f Dockerfile.patch -t 192.168.2.66:80/hermes/socks5-monitor:latest .
```
## 数据目录
```text