97 lines
3.2 KiB
Python
97 lines
3.2 KiB
Python
"""Watchdog service: report loading, process checks, trigger bookkeeping.
|
|
|
|
Mostly pure (no Flask imports). `trigger_watchdog_run` uses subprocess only;
|
|
the caller (route) is responsible for flash/redirect.
|
|
"""
|
|
|
|
import logging
|
|
import os
|
|
import sqlite3
|
|
import subprocess
|
|
import time
|
|
|
|
import config
|
|
from repositories import watchdog_repo
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
# 简单防重: 模块级变量记录上次触发时间戳, cooldown 秒内重复 POST 返回失败
|
|
_last_trigger_ts = 0.0
|
|
|
|
|
|
def parse_days(raw, default=7):
|
|
"""Clamp days query param to 1-90; non-numeric falls back to default."""
|
|
try:
|
|
value = int(raw)
|
|
except (TypeError, ValueError):
|
|
return default
|
|
return max(1, min(90, value))
|
|
|
|
|
|
def load_watchdog_reports(watchdog_conn, days=7):
|
|
"""Return recent watchdog reports; [] when db/table missing or unreadable."""
|
|
if not os.path.exists(config.WATCHDOG_DB):
|
|
return []
|
|
try:
|
|
return watchdog_repo.list_reports(watchdog_conn, days=days)
|
|
except sqlite3.Error:
|
|
logger.exception("failed to read watchdog db")
|
|
return []
|
|
|
|
|
|
def is_watchdog_running():
|
|
"""True if a python process running daily_watchdog.py already exists."""
|
|
try:
|
|
out = subprocess.run(
|
|
["pgrep", "-f", "daily_watchdog.py"],
|
|
capture_output=True, text=True, timeout=5,
|
|
).stdout
|
|
except Exception:
|
|
logger.exception("watchdog pgrep failed")
|
|
return False
|
|
for pid in out.split():
|
|
try:
|
|
with open(f"/proc/{pid}/cmdline", "rb") as f:
|
|
argv0 = f.read().split(b"\x00")[0].decode(errors="replace")
|
|
except OSError:
|
|
continue
|
|
if os.path.basename(argv0).startswith("python"):
|
|
return True
|
|
return False
|
|
|
|
|
|
def trigger_watchdog_run(cooldown=None):
|
|
"""Attempt to launch scripts/daily_watchdog.py in the background.
|
|
|
|
Returns (ok, status, message):
|
|
- (True, "started", msg) launched successfully
|
|
- (False, "cooldown", msg) hit the anti-repeat cooldown
|
|
- (False, "running", msg) an instance is already running
|
|
- (False, "error", msg) subprocess launch failed
|
|
"""
|
|
global _last_trigger_ts
|
|
cooldown = config.WATCHDOG_RUN_COOLDOWN if cooldown is None else cooldown
|
|
|
|
now = time.time()
|
|
if now - _last_trigger_ts < cooldown:
|
|
return False, "cooldown", "操作太频繁, 请稍后再试"
|
|
_last_trigger_ts = now
|
|
|
|
if is_watchdog_running():
|
|
return False, "running", "daily_watchdog.py 已在运行中, 本次触发被忽略"
|
|
|
|
try:
|
|
os.makedirs(os.path.dirname(config.WATCHDOG_RUN_LOG), exist_ok=True)
|
|
log_f = open(config.WATCHDOG_RUN_LOG, "a", encoding="utf-8")
|
|
env = dict(os.environ)
|
|
env["HOME"] = "/home/yi"
|
|
subprocess.Popen(
|
|
["/usr/bin/python3", config.WATCHDOG_SCRIPT],
|
|
stdout=log_f, stderr=subprocess.STDOUT,
|
|
cwd=config.BASE_DIR, env=env, start_new_session=True,
|
|
)
|
|
return True, "started", "已触发, 约 1-2 分钟后刷新页面查看新报告"
|
|
except Exception as exc:
|
|
logger.exception("failed to launch daily_watchdog.py")
|
|
return False, "error", f"触发失败: {exc}"
|