- fix: matrix/sms_voipms notifications blocked the event loop on timeout; make send_notification async, dispatch all channel drivers as non-blocking tasks (asyncio.to_thread for sync drivers, asyncio.wait_for for async); update all call sites to fire-and-forget via create_task - feat: add /about page with version, runtime, uptime counter, and repo link - fix: hbc_mini plugin data format now matches full hbc client so Host Overview displays memory, disk, and network metrics correctly Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,11 @@
|
||||
"""HTTP server implementation using aiohttp and jinja2."""
|
||||
|
||||
import asyncio
|
||||
import datetime
|
||||
import json
|
||||
import platform
|
||||
import socket
|
||||
import sys
|
||||
import time
|
||||
import urllib.parse
|
||||
import os
|
||||
@@ -111,6 +115,7 @@ async def start(
|
||||
This function is intended to be awaited inside the main asyncio event loop.
|
||||
"""
|
||||
get_now = get_now or (lambda: time.time())
|
||||
_start_epoch = time.time()
|
||||
|
||||
async def old_index(request):
|
||||
_require_auth_redirect(request)
|
||||
@@ -806,6 +811,48 @@ async def start(
|
||||
)
|
||||
return web.Response(text=body, content_type="text/html")
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# About page
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
async def about_page(request):
|
||||
"""GET /about — version, runtime, and project information."""
|
||||
current_user, _ = _require_auth_redirect(request)
|
||||
pkg_dir = os.path.dirname(__file__)
|
||||
templates_dir = config.get("templates_dir", os.path.join(pkg_dir, "templates"))
|
||||
env = jinja2.Environment(loader=jinja2.FileSystemLoader(templates_dir))
|
||||
from hbd import __version__ as hbd_version
|
||||
|
||||
uptime_secs = int(time.time() - _start_epoch)
|
||||
days, rem = divmod(uptime_secs, 86400)
|
||||
hours, rem = divmod(rem, 3600)
|
||||
mins, secs = divmod(rem, 60)
|
||||
if days:
|
||||
uptime_str = f"{days}d {hours}h {mins}m"
|
||||
elif hours:
|
||||
uptime_str = f"{hours}h {mins}m {secs}s"
|
||||
else:
|
||||
uptime_str = f"{mins}m {secs}s"
|
||||
|
||||
start_dt = datetime.datetime.fromtimestamp(_start_epoch)
|
||||
start_time_str = start_dt.strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
tmpl = env.get_template("about.html")
|
||||
body = tmpl.render(
|
||||
title="About - Heartbeat",
|
||||
header="About",
|
||||
hbd_version=hbd_version,
|
||||
python_version=f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro} ({platform.python_implementation()})",
|
||||
server_hostname=socket.gethostname(),
|
||||
start_epoch=int(_start_epoch),
|
||||
start_time_str=start_time_str,
|
||||
uptime_str=uptime_str,
|
||||
host_count=len(hbdclass.Host.hosts),
|
||||
current_user=current_user.to_dict() if current_user else None,
|
||||
active_page="about",
|
||||
)
|
||||
return web.Response(text=body, content_type="text/html")
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Settings page (admin only)
|
||||
# -------------------------------------------------------------------------
|
||||
@@ -859,6 +906,7 @@ async def start(
|
||||
web.get("/live", live),
|
||||
web.get("/plugins", plugins_page),
|
||||
web.get("/alerts", alerts_page),
|
||||
web.get("/about", about_page),
|
||||
web.get("/profile", profile_page),
|
||||
web.get("/settings", settings_page),
|
||||
web.get("/static/{path:.*}", static),
|
||||
|
||||
Reference in New Issue
Block a user