Add FastAPI backend and React frontend subprojects
Backend: - FastAPI app with 17 REST endpoints covering dashboard, monitoring, media index, file browser, and jobs - Reuses existing clients/domain/services unchanged - pydantic-settings config, dependency injection, CORS setup - Auto-generated OpenAPI docs at /docs Frontend: - Vite + React + TypeScript SPA - @tanstack/react-query for data fetching with polling - ag-grid-react for media table and file browser - recharts for monitoring charts - Tailwind CSS styling - 4 pages: Dashboard, Monitoring, Media, File Browser - Typed API client matching all backend endpoints Also: - docs/MIGRATION_PLAN.md with full architecture plan - Updated .gitignore for both subprojects - Streamlit app preserved for now (can coexist)
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
"""Monitoring router — metrics, collector controls, disk space."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import time
|
||||
from typing import Any
|
||||
|
||||
from fastapi import APIRouter, Depends
|
||||
|
||||
from dependencies import get_ssh_client
|
||||
from clients.ssh import RemoteSSHClient
|
||||
from clients.resources import (
|
||||
disk_space,
|
||||
read_resource_metrics,
|
||||
resource_collector_debug_info,
|
||||
resource_collector_status,
|
||||
restart_resource_collector,
|
||||
start_resource_collector,
|
||||
stop_resource_collector,
|
||||
)
|
||||
from config import get_settings
|
||||
|
||||
router = APIRouter(prefix="/api/monitoring", tags=["monitoring"])
|
||||
|
||||
|
||||
@router.get("/status")
|
||||
def get_status(ssh: RemoteSSHClient = Depends(get_ssh_client)) -> dict[str, str]:
|
||||
"""Return collector running status."""
|
||||
return {"status": resource_collector_status(ssh)}
|
||||
|
||||
|
||||
@router.get("/metrics")
|
||||
def get_metrics(
|
||||
max_lines: int = 1000,
|
||||
last_seconds: int = 3600,
|
||||
ssh: RemoteSSHClient = Depends(get_ssh_client),
|
||||
) -> dict[str, Any]:
|
||||
"""Return resource metric samples from the remote collector."""
|
||||
rows = read_resource_metrics(ssh, max_lines=max_lines)
|
||||
cutoff_ts = time.time() - last_seconds
|
||||
filtered = [row for row in rows if float(row.get("ts", 0)) >= cutoff_ts]
|
||||
return {
|
||||
"samples": filtered,
|
||||
"total_samples": len(rows),
|
||||
"filtered_samples": len(filtered),
|
||||
"cutoff_ts": cutoff_ts,
|
||||
}
|
||||
|
||||
|
||||
@router.get("/disk")
|
||||
def get_disk_space(ssh: RemoteSSHClient = Depends(get_ssh_client)) -> dict[str, Any]:
|
||||
"""Return disk space for the configured media root."""
|
||||
settings = get_settings()
|
||||
path = settings.media_root or "/"
|
||||
return disk_space(ssh, path)
|
||||
|
||||
|
||||
@router.post("/start")
|
||||
def post_start(ssh: RemoteSSHClient = Depends(get_ssh_client)) -> dict[str, str]:
|
||||
"""Start the remote resource collector."""
|
||||
message = start_resource_collector(ssh)
|
||||
return {"message": message}
|
||||
|
||||
|
||||
@router.post("/stop")
|
||||
def post_stop(ssh: RemoteSSHClient = Depends(get_ssh_client)) -> dict[str, str]:
|
||||
"""Stop the remote resource collector."""
|
||||
message = stop_resource_collector(ssh)
|
||||
return {"message": message}
|
||||
|
||||
|
||||
@router.post("/restart")
|
||||
def post_restart(ssh: RemoteSSHClient = Depends(get_ssh_client)) -> dict[str, str]:
|
||||
"""Restart the remote resource collector."""
|
||||
message = restart_resource_collector(ssh)
|
||||
return {"message": message}
|
||||
|
||||
|
||||
@router.get("/diagnostics")
|
||||
def get_diagnostics(ssh: RemoteSSHClient = Depends(get_ssh_client)) -> dict[str, str]:
|
||||
"""Return collector debug info for troubleshooting."""
|
||||
return {"diagnostics": resource_collector_debug_info(ssh)}
|
||||
Reference in New Issue
Block a user