Move dashboard HTML out of web_api into a static file

Extract the inline root() dashboard markup into src/static/dashboard.html,
loaded once at import. Keeps HTML out of the Python module (web_api 616 -> 576
lines) with a defensive fallback if the file is missing.

Full <500 compliance for web_api still needs the endpoints split into
APIRouter modules; tracked as remaining #4 work.
This commit is contained in:
2026-07-22 14:28:34 +07:00
parent 08dc536c93
commit e5936d5717
2 changed files with 61 additions and 53 deletions
+50
View File
@@ -0,0 +1,50 @@
<!DOCTYPE html>
<html>
<head>
<title>Northern Thailand Ping River Monitor</title>
<style>
body { font-family: Arial, sans-serif; margin: 40px; }
.header { color: #2c3e50; border-bottom: 2px solid #3498db; padding-bottom: 10px; }
.section { margin: 20px 0; padding: 15px; border: 1px solid #ddd; border-radius: 5px; }
.status-healthy { color: #27ae60; }
.status-degraded { color: #f39c12; }
.status-unhealthy { color: #e74c3c; }
.endpoint { background: #f8f9fa; padding: 10px; margin: 5px 0; border-radius: 3px; }
.endpoint code { color: #2c3e50; }
</style>
</head>
<body>
<div class="header">
<h1>🏔️ Northern Thailand Ping River Monitor API</h1>
<p>Real-time water level monitoring system for the Ping River Basin in Northern Thailand</p>
</div>
<div class="section">
<h2>📊 Quick Status</h2>
<p>API is running and monitoring 16 water stations along the Ping River</p>
<p>Coverage: From Chiang Dao to Nakhon Sawan</p>
<p>Data collection interval: Every hour</p>
</div>
<div class="section">
<h2>🔗 API Endpoints</h2>
<div class="endpoint"><code>GET /health</code> - System health status</div>
<div class="endpoint"><code>GET /metrics</code> - Application metrics</div>
<div class="endpoint"><code>GET /stations</code> - List all monitoring stations</div>
<div class="endpoint"><code>POST /stations</code> - Add new monitoring station</div>
<div class="endpoint"><code>PUT /stations/{station_id}</code> - Update station information</div>
<div class="endpoint"><code>GET /measurements/latest</code> - Latest measurements</div>
<div class="endpoint"><code>GET /measurements/station/{station_code}</code> - Station-specific data</div>
<div class="endpoint"><code>POST /scrape/trigger</code> - Trigger manual data collection</div>
<div class="endpoint"><code>GET /scraping/status</code> - Scraping status</div>
<div class="endpoint"><code>GET /docs</code> - Interactive API documentation</div>
</div>
<div class="section">
<h2>📈 Monitoring</h2>
<p>• Grafana dashboards available for data visualization</p>
<p>• Health checks monitor database, API, and system resources</p>
<p>• Metrics collection for performance monitoring</p>
</div>
</body>
</html>
+11 -53
View File
@@ -4,6 +4,7 @@ FastAPI web interface for water monitoring system
"""
import asyncio
import os
from contextlib import asynccontextmanager
from datetime import datetime, timedelta
from typing import Any, Dict, List
@@ -29,6 +30,15 @@ from .water_scraper_v3 import EnhancedWaterMonitorScraper
logger = get_logger(__name__)
# Dashboard HTML is loaded once at import from src/static/dashboard.html.
_DASHBOARD_HTML_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), "static", "dashboard.html")
try:
with open(_DASHBOARD_HTML_PATH, encoding="utf-8") as _dashboard_file:
DASHBOARD_HTML = _dashboard_file.read()
except OSError as _dashboard_error: # pragma: no cover - defensive fallback
logger.error(f"Could not load dashboard HTML: {_dashboard_error}")
DASHBOARD_HTML = "<h1>Northern Thailand Ping River Monitor API</h1><p>See <code>/docs</code>.</p>"
# Global application state
app_state = {
@@ -176,59 +186,7 @@ async def background_scraping_task():
@app.get("/", response_class=HTMLResponse)
async def root():
"""Root endpoint with basic dashboard"""
html_content = """
<!DOCTYPE html>
<html>
<head>
<title>Northern Thailand Ping River Monitor</title>
<style>
body { font-family: Arial, sans-serif; margin: 40px; }
.header { color: #2c3e50; border-bottom: 2px solid #3498db; padding-bottom: 10px; }
.section { margin: 20px 0; padding: 15px; border: 1px solid #ddd; border-radius: 5px; }
.status-healthy { color: #27ae60; }
.status-degraded { color: #f39c12; }
.status-unhealthy { color: #e74c3c; }
.endpoint { background: #f8f9fa; padding: 10px; margin: 5px 0; border-radius: 3px; }
.endpoint code { color: #2c3e50; }
</style>
</head>
<body>
<div class="header">
<h1>🏔️ Northern Thailand Ping River Monitor API</h1>
<p>Real-time water level monitoring system for the Ping River Basin in Northern Thailand</p>
</div>
<div class="section">
<h2>📊 Quick Status</h2>
<p>API is running and monitoring 16 water stations along the Ping River</p>
<p>Coverage: From Chiang Dao to Nakhon Sawan</p>
<p>Data collection interval: Every hour</p>
</div>
<div class="section">
<h2>🔗 API Endpoints</h2>
<div class="endpoint"><code>GET /health</code> - System health status</div>
<div class="endpoint"><code>GET /metrics</code> - Application metrics</div>
<div class="endpoint"><code>GET /stations</code> - List all monitoring stations</div>
<div class="endpoint"><code>POST /stations</code> - Add new monitoring station</div>
<div class="endpoint"><code>PUT /stations/{station_id}</code> - Update station information</div>
<div class="endpoint"><code>GET /measurements/latest</code> - Latest measurements</div>
<div class="endpoint"><code>GET /measurements/station/{station_code}</code> - Station-specific data</div>
<div class="endpoint"><code>POST /scrape/trigger</code> - Trigger manual data collection</div>
<div class="endpoint"><code>GET /scraping/status</code> - Scraping status</div>
<div class="endpoint"><code>GET /docs</code> - Interactive API documentation</div>
</div>
<div class="section">
<h2>📈 Monitoring</h2>
<p>• Grafana dashboards available for data visualization</p>
<p>• Health checks monitor database, API, and system resources</p>
<p>• Metrics collection for performance monitoring</p>
</div>
</body>
</html>
"""
return HTMLResponse(content=html_content)
return HTMLResponse(content=DASHBOARD_HTML)
@app.get("/health", response_model=HealthResponse)