Add URL health checks from config
This commit is contained in:
@@ -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"
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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())
|
||||||
|
|||||||
@@ -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
38
services/http_checks.py
Normal 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
|
||||||
Reference in New Issue
Block a user