Add log retention settings and clear logs

This commit is contained in:
2026-05-28 23:36:39 +08:00
parent 680cb34000
commit 68ad064f80
5 changed files with 108 additions and 11 deletions
+30 -5
View File
@@ -59,6 +59,7 @@ def default_config() -> dict[str, Any]:
"target_port": 443,
"fail_threshold": 3,
"recover_threshold": 1,
"max_checks_per_proxy": 500,
"db_path": str(DEFAULT_DB),
"proxies": [],
"notifications": {"enabled": True, "timeout_seconds": 10, "channels": []},
@@ -74,6 +75,7 @@ def merge_defaults(cfg: dict[str, Any]) -> dict[str, Any]:
base["target_host"] = "www.baidu.com"
base["target_port"] = int(base.get("target_port") or 443)
base["interval_seconds"] = int(base.get("interval_seconds") or 300)
base["max_checks_per_proxy"] = max(1, int(base.get("max_checks_per_proxy") or 500))
base.setdefault("proxies", [])
for proxy in base["proxies"]:
if proxy.get("target_host") in (None, "", "1.1.1.1", "8.8.8.8", "google.com"):
@@ -180,12 +182,22 @@ class Store:
)
self.conn.commit()
async def add_check(self, proxy_id: str, ok: bool, latency_ms: int | None, error: str | None, exit_ip: str | None) -> None:
async def add_check(self, proxy_id: str, ok: bool, latency_ms: int | None, error: str | None, exit_ip: str | None, max_per_proxy: int | None = None) -> None:
async with self.lock:
self.conn.execute(
"insert into checks(proxy_id,ts,ok,latency_ms,error,exit_ip) values(?,?,?,?,?,?)",
(proxy_id, now_ts(), 1 if ok else 0, latency_ms, error, exit_ip),
)
if max_per_proxy:
self.conn.execute(
"""
delete from checks
where proxy_id=? and id not in (
select id from checks where proxy_id=? order by ts desc, id desc limit ?
)
""",
(proxy_id, proxy_id, max(1, int(max_per_proxy))),
)
self.conn.commit()
async def add_event(self, proxy_id: str, old_status: str | None, new_status: str, message: str) -> None:
@@ -205,7 +217,7 @@ class Store:
self.conn.commit()
def recent_checks(self, proxy_id: str, limit: int = 50) -> list[dict[str, Any]]:
rows = self.conn.execute("select * from checks where proxy_id=? order by ts desc limit ?", (proxy_id, limit)).fetchall()
rows = self.conn.execute("select * from checks where proxy_id=? order by ts desc, id desc limit ?", (proxy_id, limit)).fetchall()
return [dict(r) for r in rows]
def recent_events(self, limit: int = 50) -> list[dict[str, Any]]:
@@ -216,6 +228,15 @@ class Store:
rows = self.conn.execute("select * from notification_logs order by ts desc limit ?", (limit,)).fetchall()
return [dict(r) for r in rows]
async def clear_logs(self) -> dict[str, int]:
async with self.lock:
removed = {}
for table in ("checks", "events", "notification_logs"):
cur = self.conn.execute(f"delete from {table}")
removed[table] = cur.rowcount if cur.rowcount is not None else 0
self.conn.commit()
return removed
@dataclasses.dataclass
class RuntimeState:
@@ -538,6 +559,8 @@ class App:
out["fail_threshold"] = max(1, int(data.get("fail_threshold") or 3))
if "recover_threshold" in data:
out["recover_threshold"] = max(1, int(data.get("recover_threshold") or 1))
if "max_checks_per_proxy" in data:
out["max_checks_per_proxy"] = max(1, int(data.get("max_checks_per_proxy") or 500))
return out
async def update_settings(self, data: dict[str, Any]) -> dict[str, Any]:
@@ -554,7 +577,7 @@ class App:
await self.save_config()
for p in list(self.proxies):
await self.restart_proxy_task(p["id"])
return {k: self.config.get(k) for k in ["interval_seconds", "timeout_seconds", "target_host", "target_port", "fail_threshold", "recover_threshold"]}
return {k: self.config.get(k) for k in ["interval_seconds", "timeout_seconds", "target_host", "target_port", "fail_threshold", "recover_threshold", "max_checks_per_proxy"]}
async def upsert_proxy(self, data: dict[str, Any], proxy_id: str | None = None) -> dict[str, Any]:
existing = self.get_proxy(proxy_id) if proxy_id else None
@@ -600,7 +623,7 @@ class App:
proxy_id = proxy["id"]
st = self.state_for(proxy_id)
ok, latency, err, exit_ip = await check_proxy(proxy)
await self.store.add_check(proxy_id, ok, latency, err, exit_ip)
await self.store.add_check(proxy_id, ok, latency, err, exit_ip, int(self.config.get("max_checks_per_proxy") or 500))
st.last_ts = now_ts(); st.last_ok = ok; st.latency_ms = latency; st.error = err; st.exit_ip = exit_ip
fail_threshold = int(proxy.get("fail_threshold", self.config.get("fail_threshold", 3)))
recover_threshold = int(proxy.get("recover_threshold", self.config.get("recover_threshold", 1)))
@@ -650,7 +673,7 @@ class App:
items.append({"proxy": public_proxy(p), "state": dataclasses.asdict(st), "recent": self.store.recent_checks(sid, 20)})
return {
"ok": True, "now": now_ts(), "now_text": iso(),
"settings": {k: self.config.get(k) for k in ["interval_seconds", "timeout_seconds", "target_host", "target_port", "fail_threshold", "recover_threshold"]},
"settings": {k: self.config.get(k) for k in ["interval_seconds", "timeout_seconds", "target_host", "target_port", "fail_threshold", "recover_threshold", "max_checks_per_proxy"]},
"proxies": items,
"notifications": self.config.get("notifications", {}),
"events": self.store.recent_events(50),
@@ -696,6 +719,8 @@ async def handle_http(reader: asyncio.StreamReader, writer: asyncio.StreamWriter
status, ctype, resp = json_response(app.status_payload())
elif method == "PUT" and p == "/api/settings":
status, ctype, resp = json_response({"ok": True, "settings": await app.update_settings(data)})
elif method == "DELETE" and p == "/api/logs":
status, ctype, resp = json_response({"ok": True, "removed": await app.store.clear_logs()})
elif method == "POST" and p == "/api/proxies":
status, ctype, resp = json_response({"ok": True, "proxy": await app.upsert_proxy(data)}, 201)
elif method == "PUT" and p.startswith("/api/proxies/"):