feat: indie status page MVP -- FastAPI + SQLite
- 8 DB models (services, incidents, monitors, subscribers, etc.) - Full CRUD API for services, incidents, monitors - Public status page with live data - Incident detail page with timeline - API key authentication - Uptime monitoring scheduler - 13 tests passing - TECHNICAL_DESIGN.md with full spec
This commit is contained in:
commit
902133edd3
4655 changed files with 1342691 additions and 0 deletions
59
app/services/scheduler.py
Normal file
59
app/services/scheduler.py
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
"""Background scheduler for uptime monitoring using APScheduler."""
|
||||
|
||||
import asyncio
|
||||
import logging
|
||||
from datetime import datetime
|
||||
|
||||
from apscheduler.schedulers.asyncio import AsyncIOScheduler
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.database import async_session_factory
|
||||
from app.models.models import Monitor
|
||||
from app.services.uptime import check_monitor
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
_scheduler: AsyncIOScheduler | None = None
|
||||
|
||||
|
||||
async def _run_monitor_checks() -> None:
|
||||
"""Check all active monitors."""
|
||||
async with async_session_factory() as db:
|
||||
result = await db.execute(select(Monitor).where(Monitor.is_active == True)) # noqa: E712
|
||||
monitors = result.scalars().all()
|
||||
|
||||
for monitor in monitors:
|
||||
try:
|
||||
await check_monitor(monitor, db)
|
||||
except Exception as exc:
|
||||
logger.error(f"Monitor check failed for {monitor.url}: {exc}")
|
||||
|
||||
await db.commit()
|
||||
|
||||
|
||||
def start_scheduler() -> None:
|
||||
"""Start the APScheduler with periodic monitor checks."""
|
||||
global _scheduler
|
||||
if _scheduler is not None:
|
||||
return
|
||||
|
||||
_scheduler = AsyncIOScheduler()
|
||||
_scheduler.add_job(
|
||||
_run_monitor_checks,
|
||||
"interval",
|
||||
seconds=60,
|
||||
id="monitor_checks",
|
||||
replace_existing=True,
|
||||
)
|
||||
_scheduler.start()
|
||||
logger.info("Uptime monitoring scheduler started (interval: 60s)")
|
||||
|
||||
|
||||
def shutdown_scheduler() -> None:
|
||||
"""Gracefully shut down the scheduler."""
|
||||
global _scheduler
|
||||
if _scheduler is not None:
|
||||
_scheduler.shutdown(wait=False)
|
||||
_scheduler = None
|
||||
logger.info("Uptime monitoring scheduler stopped")
|
||||
Loading…
Add table
Add a link
Reference in a new issue