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 asyncio
import os
from contextlib import asynccontextmanager from contextlib import asynccontextmanager
from datetime import datetime, timedelta from datetime import datetime, timedelta
from typing import Any, Dict, List from typing import Any, Dict, List
@@ -29,6 +30,15 @@ from .water_scraper_v3 import EnhancedWaterMonitorScraper
logger = get_logger(__name__) 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 # Global application state
app_state = { app_state = {
@@ -176,59 +186,7 @@ async def background_scraping_task():
@app.get("/", response_class=HTMLResponse) @app.get("/", response_class=HTMLResponse)
async def root(): async def root():
"""Root endpoint with basic dashboard""" """Root endpoint with basic dashboard"""
html_content = """ return HTMLResponse(content=DASHBOARD_HTML)
<!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)
@app.get("/health", response_model=HealthResponse) @app.get("/health", response_model=HealthResponse)