import sqlite3 from pathlib import Path from typing import Any, Dict, List, Optional from app_state import StateStore def health_payload( data_dir: Path, session_dir: Path, state_store: StateStore, continuous_snapshot: Dict[str, Any], job_queue_size: int, account_ids: Optional[List[str]] = None, ) -> Dict[str, Any]: """ Return a health-check payload for the application. NOTE: This endpoint does NOT support message search or query parameters. Health concerns itself with filesystem, database connectivity, job queue, and account session state. Message search is handled by the message API endpoints (/api/channels/*/messages, /api/accounts/*/channels/*/messages). """ checks = { "data_dir": _dir_check(data_dir, writable=True), "session_dir": _dir_check(session_dir, writable=True), "state_file": _state_check(state_store), "sqlite": _sqlite_check(), "continuous": _continuous_check(continuous_snapshot), "job_queue": {"ok": True, "size": job_queue_size}, } # Per-account health if account_ids: account_checks: Dict[str, Any] = {} for acc_id in account_ids: acc_dir = data_dir / "accounts" / acc_id session_file = session_dir / f"{acc_id}.session" account_checks[acc_id] = { "data_dir": _dir_check(acc_dir), "session_file": { "ok": session_file.exists(), "path": str(session_file), }, } checks["accounts"] = { "ok": all( item.get("data_dir", {}).get("ok", False) for item in account_checks.values() ), "items": account_checks, } ok = all(item.get("ok", False) for item in checks.values()) return {"ok": ok, "status": "ok" if ok else "degraded", "checks": checks} def _dir_check(path: Path, writable: bool = False) -> Dict[str, Any]: path.mkdir(parents=True, exist_ok=True) ok = path.exists() and path.is_dir() payload: Dict[str, Any] = {"ok": ok, "path": str(path)} if writable: probe = path / ".healthcheck" try: probe.write_text("ok", encoding="utf-8") probe.unlink(missing_ok=True) payload["writable"] = True except OSError as exc: payload.update({"ok": False, "writable": False, "error": str(exc)}) return payload def _state_check(state_store: StateStore) -> Dict[str, Any]: try: state = state_store.load() return { "ok": True, "path": str(state_store.path), "has_api_credentials": bool(state.get("api_id") and state.get("api_hash")), "tracked_channels": len(state.get("channels", {})), } except Exception as exc: return {"ok": False, "path": str(state_store.path), "error": str(exc)} def _sqlite_check() -> Dict[str, Any]: try: conn = sqlite3.connect(":memory:") conn.execute("SELECT 1") conn.close() return {"ok": True} except sqlite3.Error as exc: return {"ok": False, "error": str(exc)} def _continuous_check(snapshot: Dict[str, Any]) -> Dict[str, Any]: running_accounts = snapshot.get("running_accounts") if running_accounts is None: running_accounts = snapshot return { "ok": True, "account_count": len(running_accounts), "accounts": { aid: { "running": bool((info.get("status") or info).get("running")), "enabled": bool((info.get("config") or {}).get("enabled", False)), "last_error": (info.get("status") or info).get("last_error"), } for aid, info in running_accounts.items() if isinstance(info, dict) }, }