Files
monitor/main.py
2026-01-04 16:22:27 +01:00

122 lines
4.2 KiB
Python

from fastapi import FastAPI
from fastapi.responses import FileResponse
from fastapi.staticfiles import StaticFiles
from fastapi.middleware.cors import CORSMiddleware
import os
from dotenv import load_dotenv
from monitor_logic import get_ssh_data, check_web_service, check_testarena_backend_status, recover_testarena_backend, send_email_notification
import asyncio
from datetime import datetime
load_dotenv()
app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_methods=["*"],
allow_headers=["*"],
)
# Serve static files from the current directory
app.mount("/static", StaticFiles(directory="."), name="static")
# Configuration from environment variables
PC_HOST = "asf-server.duckdns.org"
PC_PORT = 49152
PC_USER = "asf"
PC_PASS = os.getenv("PC_PASS", "ASF")
PI_HOST = "rpi-asf-tb.duckdns.org"
PI_PORT = 2222
PI_USER = "asf_tb"
PI_PASS = os.getenv("PI_PASS", "ASF_TB")
SMTP_USER = "support@nabd-co.com"
SMTP_PASS = "zwziglbpxyfogafc"
WEB_SERVICES = [
{"name": "Gitea", "url": "https://gitea.nabd-co.com/"},
{"name": "OpenProject", "url": "https://openproject.nabd-co.com/"},
{"name": "Draw.io", "url": "https://drawio.nabd-co.com/"},
{"name": "TestArena", "url": "https://testarena.nabd-co.com/"},
{"name": "TBM", "url": "https://tbm.asf.nabd-co.com/"},
{"name": "Board", "url": "https://board.nabd-co.com/"},
]
# Global state to store latest status
LATEST_STATUS = {
"pc": {"status": "offline"},
"pi": {"status": "offline"},
"services": [],
"last_update": None
}
async def monitor_services_task():
while True:
try:
# Run SSH checks
pc_res = await asyncio.to_thread(get_ssh_data, PC_HOST, PC_PORT, PC_USER, PC_PASS)
pi_res = await asyncio.to_thread(get_ssh_data, PI_HOST, PI_PORT, PI_USER, PI_PASS)
# Run web checks
web_results = []
for s in WEB_SERVICES:
res = await asyncio.to_thread(check_web_service, s["url"])
web_results.append({**s, **res})
if res["status"] != "online":
await asyncio.to_thread(send_email_notification, SMTP_USER, SMTP_PASS, s["name"], res["status"])
# Check TestArena Backend
ta_res = await asyncio.to_thread(check_testarena_backend_status)
if ta_res["status"] != "online":
# Recovery loop
for attempt in range(1, 6):
print(f"Attempting recovery for TestArena Backend (Attempt {attempt})...")
success = await asyncio.to_thread(recover_testarena_backend, PC_HOST, PC_PORT, PC_USER, PC_PASS, attempt)
if success:
# Wait a bit for services to start
await asyncio.sleep(10)
ta_res = await asyncio.to_thread(check_testarena_backend_status)
if ta_res["status"] == "online":
print("TestArena Backend recovered successfully.")
break
if ta_res["status"] != "online":
print("TestArena Backend recovery failed after 5 attempts.")
await asyncio.to_thread(send_email_notification, SMTP_USER, SMTP_PASS, "TestArena Backend", ta_res["status"])
web_results.append({
"name": "TestArena Backend",
"url": "http://asf-server.duckdns.org:8080/api/system/status",
**ta_res
})
LATEST_STATUS["pc"] = pc_res
LATEST_STATUS["pi"] = pi_res
LATEST_STATUS["services"] = web_results
LATEST_STATUS["last_update"] = datetime.now().isoformat()
except Exception as e:
print(f"Monitor task error: {e}")
await asyncio.sleep(300) # Run every 5 minutes
@app.on_event("startup")
async def startup_event():
asyncio.create_task(monitor_services_task())
@app.get("/")
async def read_index():
return FileResponse("monitor.html")
@app.get("/api/status")
async def get_status():
return LATEST_STATUS
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)