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:
@@ -17,9 +17,14 @@ spec.loader.exec_module(app)
|
||||
|
||||
|
||||
class LogRetentionTest(unittest.TestCase):
|
||||
def make_store(self, path: Path):
|
||||
store = app.Store(path)
|
||||
self.addCleanup(store.close)
|
||||
return store
|
||||
|
||||
def test_check_records_are_pruned_per_proxy(self):
|
||||
with tempfile.TemporaryDirectory() as td:
|
||||
store = app.Store(Path(td) / "monitor.db")
|
||||
store = self.make_store(Path(td) / "monitor.db")
|
||||
|
||||
async def run():
|
||||
for i in range(5):
|
||||
@@ -35,7 +40,7 @@ class LogRetentionTest(unittest.TestCase):
|
||||
|
||||
def test_clear_logs_removes_checks_events_and_notification_logs(self):
|
||||
with tempfile.TemporaryDirectory() as td:
|
||||
store = app.Store(Path(td) / "monitor.db")
|
||||
store = self.make_store(Path(td) / "monitor.db")
|
||||
|
||||
async def seed_and_clear():
|
||||
await store.add_check("node-a", True, 1, None, "203.0.113.1")
|
||||
@@ -57,6 +62,7 @@ class LogRetentionTest(unittest.TestCase):
|
||||
cfg = Path(td) / "config.json"
|
||||
cfg.write_text('{"proxies": [], "notifications": {"channels": []}}', encoding="utf-8")
|
||||
application = app.App(cfg)
|
||||
self.addCleanup(application.store.close)
|
||||
settings = asyncio.run(application.update_settings({"max_checks_per_proxy": 7}))
|
||||
self.assertEqual(settings["max_checks_per_proxy"], 7)
|
||||
saved = app.load_json(cfg)
|
||||
|
||||
Reference in New Issue
Block a user