Add URL health checks from config

This commit is contained in:
2026-02-07 22:31:37 +03:00
parent 2a76488902
commit dd6dc8040b
5 changed files with 77 additions and 2 deletions

View File

@@ -28,4 +28,6 @@ docker:
tg-admin-bot: "tg-admin-bot" tg-admin-bot: "tg-admin-bot"
# Explicit list used by legacy modules # Explicit list used by legacy modules
containers: containers:
tg-admin-bot: "tg-admin-bot" tg-admin-bot:
name: "tg-admin-bot"
url: "http://127.0.0.1:8080/health"

View File

@@ -17,7 +17,7 @@ async def help_cmd(msg: Message):
"🐳 Docker — управление контейнерами\n" "🐳 Docker — управление контейнерами\n"
"📦 Backup — restic бэкапы\n" "📦 Backup — restic бэкапы\n"
"🧉 Artifacts — критичные образы (Clonezilla, NAND)\n" "🧉 Artifacts — критичные образы (Clonezilla, NAND)\n"
"⚙️ System — диски, безопасность, reboot\n\n" "⚙️ System — диски, безопасность, URL, reboot\n\n"
"Inline-кнопки используются для выбора контейнеров.", "Inline-кнопки используются для выбора контейнеров.",
reply_markup=menu_kb, reply_markup=menu_kb,
parse_mode="Markdown", parse_mode="Markdown",

View File

@@ -4,6 +4,9 @@ from app import dp
from auth import is_admin_msg from auth import is_admin_msg
from keyboards import system_kb from keyboards import system_kb
from system_checks import security, disks from system_checks import security, disks
from app import cfg
from services.http_checks import get_url_checks, check_url
import asyncio
@dp.message(F.text == "💽 Disks") @dp.message(F.text == "💽 Disks")
@@ -16,3 +19,34 @@ async def sd(msg: Message):
async def sec(msg: Message): async def sec(msg: Message):
if is_admin_msg(msg): if is_admin_msg(msg):
await msg.answer(security(), reply_markup=system_kb) await msg.answer(security(), reply_markup=system_kb)
@dp.message(F.text == "🌐 URLs")
async def urls(msg: Message):
if not is_admin_msg(msg):
return
checks = list(get_url_checks(cfg))
if not checks:
await msg.answer("⚠️ Нет URL для проверки", reply_markup=system_kb)
return
await msg.answer("⏳ Проверяю URL…", reply_markup=system_kb)
async def worker():
tasks = [asyncio.to_thread(check_url, url) for _, url in checks]
results = await asyncio.gather(*tasks)
lines = ["🌐 URLs\n"]
for (alias, url), (ok, status, ms, err) in zip(checks, results):
if ok:
lines.append(f"🟢 {alias}: {status} ({ms}ms)")
elif status is not None:
lines.append(f"🔴 {alias}: {status} ({ms}ms)")
else:
reason = err or "error"
lines.append(f"🔴 {alias}: {reason} ({ms}ms)")
await msg.answer("\n".join(lines), reply_markup=system_kb)
asyncio.create_task(worker())

View File

@@ -49,6 +49,7 @@ artifacts_kb = ReplyKeyboardMarkup(
system_kb = ReplyKeyboardMarkup( system_kb = ReplyKeyboardMarkup(
keyboard=[ keyboard=[
[KeyboardButton(text="💽 Disks"), KeyboardButton(text="🔐 Security")], [KeyboardButton(text="💽 Disks"), KeyboardButton(text="🔐 Security")],
[KeyboardButton(text="🌐 URLs")],
[KeyboardButton(text="🔄 Reboot")], [KeyboardButton(text="🔄 Reboot")],
[KeyboardButton(text="⬅️ Назад")], [KeyboardButton(text="⬅️ Назад")],
], ],

38
services/http_checks.py Normal file
View File

@@ -0,0 +1,38 @@
import time
from typing import Iterable, Tuple
from urllib.error import HTTPError, URLError
from urllib.request import Request, urlopen
def get_url_checks(cfg) -> Iterable[Tuple[str, str]]:
docker_cfg = cfg.get("docker", {})
containers = docker_cfg.get("containers", {})
for alias, value in containers.items():
if isinstance(value, str):
continue
if isinstance(value, dict):
url = value.get("url")
if url:
yield alias, url
def check_url(url: str, timeout: int = 5) -> Tuple[bool, int | None, int | None, str | None]:
start = time.time()
req = Request(url, headers={"User-Agent": "tg-admin-bot"})
try:
with urlopen(req, timeout=timeout) as resp:
status = int(resp.status)
except HTTPError as e:
status = int(e.code)
elapsed_ms = int((time.time() - start) * 1000)
return False, status, elapsed_ms, None
except URLError as e:
elapsed_ms = int((time.time() - start) * 1000)
return False, None, elapsed_ms, str(e.reason)
except Exception as e:
elapsed_ms = int((time.time() - start) * 1000)
return False, None, elapsed_ms, str(e)
elapsed_ms = int((time.time() - start) * 1000)
ok = status < 400
return ok, status, elapsed_ms, None