f9934bd4f1
保存 Telegram 时同步至 .env,health_monitor 支持 MySQL 回退, 设置页展示巡检状态,部署时自动安装 cron。 Co-authored-by: Cursor <cursoragent@cursor.com>
115 lines
3.5 KiB
Python
115 lines
3.5 KiB
Python
"""Sync Telegram credentials for Layer-3 host patrol (health_monitor.sh)."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
import os
|
|
import shutil
|
|
from pathlib import Path
|
|
|
|
from server.infrastructure.env_file import read_env_var, upsert_env_vars
|
|
|
|
logger = logging.getLogger("nexus.ops_patrol")
|
|
|
|
NEXUS_ROOT = Path(__file__).resolve().parents[3]
|
|
ENV_FILE = NEXUS_ROOT / ".env"
|
|
|
|
_TELEGRAM_ENV_KEYS = (
|
|
"NEXUS_TELEGRAM_BOT_TOKEN",
|
|
"NEXUS_TELEGRAM_CHAT_ID",
|
|
)
|
|
|
|
|
|
def _persist_env_path() -> Path:
|
|
raw = os.environ.get("NEXUS_PERSIST_DIR", "/var/lib/nexus").strip() or "/var/lib/nexus"
|
|
return Path(raw) / ".env"
|
|
|
|
|
|
def _host_env_prod_path() -> Path | None:
|
|
host_root = os.environ.get("NEXUS_HOST_ROOT", "").strip()
|
|
if not host_root:
|
|
return None
|
|
return Path(host_root) / "docker" / ".env.prod"
|
|
|
|
|
|
def _build_updates(token: str | None, chat_id: str | None) -> dict[str, str]:
|
|
updates: dict[str, str] = {}
|
|
if token and token.strip():
|
|
updates["NEXUS_TELEGRAM_BOT_TOKEN"] = token.strip()
|
|
if chat_id and str(chat_id).strip():
|
|
updates["NEXUS_TELEGRAM_CHAT_ID"] = str(chat_id).strip()
|
|
return updates
|
|
|
|
|
|
def _sync_to_persist_volume() -> bool:
|
|
if not ENV_FILE.is_file():
|
|
return False
|
|
dest = _persist_env_path()
|
|
try:
|
|
dest.parent.mkdir(parents=True, exist_ok=True)
|
|
shutil.copy2(ENV_FILE, dest)
|
|
dest.chmod(0o600)
|
|
return True
|
|
except OSError as exc:
|
|
logger.warning("Failed to sync .env to persist volume %s: %s", dest, exc)
|
|
return False
|
|
|
|
|
|
def read_telegram_env_status() -> dict[str, bool]:
|
|
"""Report whether Layer-3 readable env files contain Telegram credentials."""
|
|
app_token = bool(read_env_var(ENV_FILE, "NEXUS_TELEGRAM_BOT_TOKEN"))
|
|
app_chat = bool(read_env_var(ENV_FILE, "NEXUS_TELEGRAM_CHAT_ID"))
|
|
|
|
host_token = False
|
|
host_chat = False
|
|
host_path = _host_env_prod_path()
|
|
if host_path is not None:
|
|
host_token = bool(read_env_var(host_path, "NEXUS_TELEGRAM_BOT_TOKEN"))
|
|
host_chat = bool(read_env_var(host_path, "NEXUS_TELEGRAM_CHAT_ID"))
|
|
|
|
env_synced = (app_token and app_chat) or (host_token and host_chat)
|
|
return {
|
|
"app_env_token": app_token,
|
|
"app_env_chat_id": app_chat,
|
|
"host_env_prod_token": host_token,
|
|
"host_env_prod_chat_id": host_chat,
|
|
"env_synced": env_synced,
|
|
}
|
|
|
|
|
|
def sync_telegram_to_env(token: str | None, chat_id: str | None) -> dict[str, bool | str]:
|
|
"""Write Telegram credentials to /app/.env, persist volume, and host .env.prod."""
|
|
updates = _build_updates(token, chat_id)
|
|
if not updates:
|
|
return {
|
|
"app_env": False,
|
|
"persist_env": False,
|
|
"host_env_prod": False,
|
|
"skipped": "telegram_not_configured",
|
|
}
|
|
|
|
result: dict[str, bool | str] = {
|
|
"app_env": False,
|
|
"persist_env": False,
|
|
"host_env_prod": False,
|
|
}
|
|
|
|
try:
|
|
if upsert_env_vars(ENV_FILE, updates):
|
|
ENV_FILE.chmod(0o600)
|
|
result["app_env"] = True
|
|
result["persist_env"] = _sync_to_persist_volume()
|
|
except OSError as exc:
|
|
logger.warning("Failed to update %s: %s", ENV_FILE, exc)
|
|
|
|
host_path = _host_env_prod_path()
|
|
if host_path is not None:
|
|
try:
|
|
if host_path.is_file() and upsert_env_vars(host_path, updates):
|
|
host_path.chmod(0o600)
|
|
result["host_env_prod"] = True
|
|
except OSError as exc:
|
|
logger.warning("Failed to update host .env.prod %s: %s", host_path, exc)
|
|
|
|
return result
|