feat: wire monitors, scheduler, alerts, and nav into app
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+90
-2
@@ -1,4 +1,5 @@
|
||||
from __future__ import annotations
|
||||
import asyncio
|
||||
from pathlib import Path
|
||||
from quart import Quart
|
||||
from .config import load_config
|
||||
@@ -33,16 +34,103 @@ def create_app(
|
||||
from unittest.mock import MagicMock
|
||||
app.db_sessionmaker = MagicMock()
|
||||
|
||||
# Alert pipeline
|
||||
from .core.alerts import init_alerts
|
||||
init_alerts(app)
|
||||
|
||||
# Register blueprints
|
||||
from .auth.routes import auth_bp
|
||||
app.register_blueprint(auth_bp)
|
||||
|
||||
from .dashboard.routes import dashboard_bp
|
||||
from .hosts.routes import hosts_bp
|
||||
from .alerts.routes import alerts_bp
|
||||
|
||||
app.register_blueprint(auth_bp)
|
||||
app.register_blueprint(dashboard_bp)
|
||||
app.register_blueprint(hosts_bp)
|
||||
app.register_blueprint(alerts_bp)
|
||||
|
||||
# Build task registry
|
||||
app._task_registry = []
|
||||
|
||||
if not testing:
|
||||
_register_core_tasks(app, cfg)
|
||||
|
||||
# Health check
|
||||
@app.get("/health")
|
||||
async def health():
|
||||
return {"status": "ok"}
|
||||
|
||||
# Start scheduler on first request
|
||||
@app.before_serving
|
||||
async def startup():
|
||||
if not testing:
|
||||
asyncio.create_task(_run_scheduler(app))
|
||||
|
||||
return app
|
||||
|
||||
|
||||
def _register_core_tasks(app: Quart, cfg: dict) -> None:
|
||||
from .core.scheduler import ScheduledTask
|
||||
|
||||
poll_interval = cfg.get("monitors", {}).get("poll_interval_seconds", 60)
|
||||
cleanup_interval = 3600 # hourly
|
||||
|
||||
async def run_ping_monitors():
|
||||
from sqlalchemy import select
|
||||
from .models.hosts import Host
|
||||
from .monitors.ping import ping_check
|
||||
async with app.db_sessionmaker() as session:
|
||||
async with session.begin():
|
||||
result = await session.execute(
|
||||
select(Host).where(Host.ping_enabled.is_(True))
|
||||
)
|
||||
for host in result.scalars().all():
|
||||
try:
|
||||
await ping_check(host, session)
|
||||
except Exception:
|
||||
app.logger.exception(f"Ping check failed for host {host.name!r}")
|
||||
|
||||
async def run_dns_monitors():
|
||||
from sqlalchemy import select
|
||||
from .models.hosts import Host
|
||||
from .monitors.dns import dns_check
|
||||
async with app.db_sessionmaker() as session:
|
||||
async with session.begin():
|
||||
result = await session.execute(
|
||||
select(Host).where(Host.dns_enabled.is_(True))
|
||||
)
|
||||
for host in result.scalars().all():
|
||||
try:
|
||||
await dns_check(host, session)
|
||||
except Exception:
|
||||
app.logger.exception(f"DNS check failed for host {host.name!r}")
|
||||
|
||||
async def run_cleanup():
|
||||
from .core.cleanup import run_cleanup as _cleanup
|
||||
await _cleanup(app)
|
||||
|
||||
app._task_registry.extend([
|
||||
ScheduledTask(
|
||||
name="ping_monitor",
|
||||
coro_factory=run_ping_monitors,
|
||||
interval_seconds=poll_interval,
|
||||
run_on_startup=True,
|
||||
),
|
||||
ScheduledTask(
|
||||
name="dns_monitor",
|
||||
coro_factory=run_dns_monitors,
|
||||
interval_seconds=poll_interval,
|
||||
run_on_startup=True,
|
||||
),
|
||||
ScheduledTask(
|
||||
name="data_cleanup",
|
||||
coro_factory=run_cleanup,
|
||||
interval_seconds=cleanup_interval,
|
||||
run_on_startup=False,
|
||||
),
|
||||
])
|
||||
|
||||
|
||||
async def _run_scheduler(app: Quart) -> None:
|
||||
from .core.scheduler import start_scheduler
|
||||
await start_scheduler(app._task_registry)
|
||||
|
||||
@@ -34,6 +34,8 @@
|
||||
<nav>
|
||||
<span class="brand">FabledNetMon</span>
|
||||
<a href="/">Dashboard</a>
|
||||
<a href="/hosts/">Hosts</a>
|
||||
<a href="/alerts/">Alerts</a>
|
||||
<a href="/logout">Logout ({{ session.username }})</a>
|
||||
</nav>
|
||||
{% endif %}
|
||||
|
||||
@@ -4,16 +4,26 @@
|
||||
<h1 style="margin-bottom:1.5rem;color:#c0c0ff;">Dashboard</h1>
|
||||
<div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:1rem;">
|
||||
<div class="card">
|
||||
<h3 style="color:#8080ff;margin-bottom:0.5rem;">Ping Monitor</h3>
|
||||
<p style="color:#606080;font-size:0.9rem;">No hosts configured yet.</p>
|
||||
<h3 style="color:#8080ff;margin-bottom:0.75rem;">Ping Monitor</h3>
|
||||
<p style="color:#606080;font-size:0.9rem;">
|
||||
<a href="/hosts/" style="color:#a0a0ff;">Manage hosts</a> to begin monitoring.
|
||||
</p>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h3 style="color:#8080ff;margin-bottom:0.5rem;">DNS Monitor</h3>
|
||||
<p style="color:#606080;font-size:0.9rem;">No hosts configured yet.</p>
|
||||
<h3 style="color:#8080ff;margin-bottom:0.75rem;">DNS Monitor</h3>
|
||||
<p style="color:#606080;font-size:0.9rem;">
|
||||
Enable DNS monitoring on a <a href="/hosts/" style="color:#a0a0ff;">host</a>.
|
||||
</p>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h3 style="color:#8080ff;margin-bottom:0.5rem;">Ansible</h3>
|
||||
<p style="color:#606080;font-size:0.9rem;">No playbook sources configured yet.</p>
|
||||
<h3 style="color:#8080ff;margin-bottom:0.75rem;">Alerts</h3>
|
||||
<p style="color:#606080;font-size:0.9rem;">
|
||||
<a href="/alerts/" style="color:#a0a0ff;">Configure alert rules</a> against any metric.
|
||||
</p>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h3 style="color:#8080ff;margin-bottom:0.75rem;">Ansible</h3>
|
||||
<p style="color:#606080;font-size:0.9rem;">Coming in Plan 3.</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
Reference in New Issue
Block a user