ux
This commit is contained in:
@@ -1,5 +1,9 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime, timezone
|
||||
import os
|
||||
import platform
|
||||
|
||||
from flask import Blueprint, jsonify, request
|
||||
|
||||
from app.core_settings import get_settings
|
||||
@@ -7,10 +11,12 @@ from app.services.capabilities import build_capabilities
|
||||
from app.services.catalog import get_catalog
|
||||
from app.services.kiosk_settings import get_kiosk_settings_service
|
||||
from app.services.auth import get_auth_service
|
||||
from app.services.influx_http import InfluxHTTPService
|
||||
from app.utils.serialization import to_plain
|
||||
|
||||
|
||||
dashboard_blueprint = Blueprint("dashboard", __name__)
|
||||
_APP_STARTED_AT = datetime.now(timezone.utc)
|
||||
|
||||
|
||||
@dashboard_blueprint.get("/dashboard/config")
|
||||
@@ -78,3 +84,38 @@ def update_dashboard_kiosk_settings():
|
||||
return jsonify({"detail": str(exc)}), 403
|
||||
except ValueError as exc:
|
||||
return jsonify({"detail": str(exc)}), 400
|
||||
|
||||
|
||||
@dashboard_blueprint.get("/dashboard/diagnostics")
|
||||
def dashboard_diagnostics():
|
||||
settings = get_settings()
|
||||
influx_diagnostics = InfluxHTTPService(settings).diagnose()
|
||||
now = datetime.now(timezone.utc)
|
||||
payload = {
|
||||
"app": {
|
||||
"name": settings.app_name,
|
||||
"version": settings.version,
|
||||
"debug": settings.debug,
|
||||
"timezone": settings.timezone,
|
||||
"site_name": settings.site_name,
|
||||
"installed_power_kwp": settings.installed_power_kwp,
|
||||
"auth_enabled": settings.auth["enabled"],
|
||||
"started_at": _APP_STARTED_AT.isoformat(),
|
||||
"uptime_seconds": max(int((now - _APP_STARTED_AT).total_seconds()), 0),
|
||||
"python_version": platform.python_version(),
|
||||
"pid": os.getpid(),
|
||||
},
|
||||
"api": {
|
||||
"status": "ok",
|
||||
"prefix": settings.api_prefix,
|
||||
"cors_origins_count": len(settings.cors_origins),
|
||||
},
|
||||
"influx": influx_diagnostics,
|
||||
"storage": {
|
||||
"sqlite_path": settings.storage["sqlite_path"],
|
||||
"historical_import_enabled": settings.history["enabled"],
|
||||
"auto_sync_enabled": settings.history["auto_sync_enabled"],
|
||||
"default_chunk_days": settings.history["default_chunk_days"],
|
||||
},
|
||||
}
|
||||
return jsonify(to_plain(payload))
|
||||
|
||||
@@ -165,6 +165,34 @@ class InfluxHTTPService:
|
||||
logger.warning("Influx last_before error for %s: %s", metric.id, exc)
|
||||
return None
|
||||
|
||||
def diagnose(self) -> dict:
|
||||
config = self.settings.influx
|
||||
payload = {
|
||||
"status": "connected",
|
||||
"reachable": True,
|
||||
"database_exists": False,
|
||||
"url": self.base_url,
|
||||
"database": config["database"],
|
||||
"username_masked": _mask_secret(config.get("username") or ""),
|
||||
"verify_ssl": bool(config.get("verify_ssl", False)),
|
||||
"timeout_seconds": int(config.get("timeout_seconds", 15)),
|
||||
"error": None,
|
||||
}
|
||||
try:
|
||||
series = self._execute("SHOW DATABASES")
|
||||
databases: set[str] = set()
|
||||
for item in series:
|
||||
for row in self._rows_from_series(item):
|
||||
value = row.get("name")
|
||||
if isinstance(value, str):
|
||||
databases.add(value)
|
||||
payload["database_exists"] = config["database"] in databases
|
||||
except Exception as exc:
|
||||
payload["status"] = "error"
|
||||
payload["reachable"] = False
|
||||
payload["error"] = str(exc)
|
||||
return payload
|
||||
|
||||
def _single_value(self, query: str) -> SeriesPoint | None:
|
||||
try:
|
||||
series = self._execute(query)
|
||||
@@ -239,3 +267,11 @@ def _parse_time(value: str | None) -> datetime | None:
|
||||
return datetime.fromisoformat(value.replace("Z", "+00:00"))
|
||||
except ValueError:
|
||||
return None
|
||||
|
||||
|
||||
def _mask_secret(value: str) -> str:
|
||||
if not value:
|
||||
return ""
|
||||
if len(value) <= 2:
|
||||
return "*" * len(value)
|
||||
return value[:1] + ("*" * max(len(value) - 2, 1)) + value[-1:]
|
||||
|
||||
Reference in New Issue
Block a user