feat,refactor(core,backend,frontend): add simple frontend and a separate router for it
This commit is contained in:
@@ -1 +1,6 @@
|
||||
"""Web backend module for disk-checker API."""
|
||||
"""Web backend module for disk-checker API."""
|
||||
|
||||
from .web_routes import web_router
|
||||
from .routes import api_router
|
||||
|
||||
__all__ = ["web_router", "api_router"]
|
||||
@@ -1,56 +1,12 @@
|
||||
"""API routes for disk-checker web backend."""
|
||||
|
||||
import asyncio
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
from typing import List, Optional, Dict
|
||||
from fastapi import APIRouter, Request
|
||||
|
||||
from fastapi import APIRouter, UploadFile, File, HTTPException, Depends, Request
|
||||
from fastapi.responses import JSONResponse, FileResponse
|
||||
import pandas as pd
|
||||
|
||||
from disk_checker.core.extractor import load_latency_csv
|
||||
from disk_checker.core.helpers import percentile_table
|
||||
from disk_checker.core.tool_scanner import ToolInfo
|
||||
|
||||
api_router = APIRouter()
|
||||
|
||||
from disk_checker.plotting.plots import generate_plot
|
||||
from disk_checker.web.models import (
|
||||
JobStatusResponse,
|
||||
)
|
||||
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.get("/jobs/{job_id}", response_model=JobStatusResponse)
|
||||
async def get_job_status(job_id: str, request: Request):
|
||||
"""
|
||||
Check the status of a benchmark job.
|
||||
"""
|
||||
jobs = request.app.state.jobs
|
||||
if job_id not in jobs:
|
||||
raise HTTPException(status_code=404, detail="Job not found")
|
||||
|
||||
job = jobs[job_id]
|
||||
task = job["task"]
|
||||
|
||||
if task.done():
|
||||
result = task.result()
|
||||
return JobStatusResponse(**result)
|
||||
|
||||
return JobStatusResponse(job_id=job_id, status="running")
|
||||
|
||||
|
||||
@router.get("/jobs", response_model=List[str])
|
||||
async def list_jobs(request: Request):
|
||||
"""
|
||||
List all benchmark jobs.
|
||||
"""
|
||||
return list(request.app.state.jobs.keys())
|
||||
|
||||
|
||||
@router.get("/tools")
|
||||
@api_router.get("/tools")
|
||||
async def get_available_tools(request: Request):
|
||||
"""
|
||||
Get list of available benchmarking tools.
|
||||
|
||||
@@ -2,14 +2,31 @@
|
||||
|
||||
import asyncio
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from fastapi import FastAPI
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
from fastapi.templating import Jinja2Templates
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from contextlib import asynccontextmanager
|
||||
import logging
|
||||
|
||||
from disk_checker.core.orchestrator import Orchestrator
|
||||
from disk_checker.core.tool_scanner import scan_tools
|
||||
# Import and include routers
|
||||
from disk_checker.web.routes import api_router
|
||||
from disk_checker.web.web_routes import web_router
|
||||
|
||||
|
||||
# Get application root
|
||||
BASE_DIR = Path(__file__).resolve().parent.parent.parent.parent
|
||||
TEMPLATES_DIR = BASE_DIR / "templates"
|
||||
STATIC_DIR = BASE_DIR / "static"
|
||||
|
||||
# Setup templates
|
||||
templates = Jinja2Templates(directory=str(TEMPLATES_DIR))
|
||||
|
||||
# Setup logger
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -24,6 +41,10 @@ async def lifespan(app: FastAPI):
|
||||
app.state.available_tools = await asyncio.to_thread(scan_tools)
|
||||
logger.info(f"Available tools: {list(app.state.available_tools.keys())}")
|
||||
|
||||
# Store templates and static_dir path in app.state
|
||||
app.state.templates = templates
|
||||
app.state.static_dir = STATIC_DIR
|
||||
|
||||
logger.info("Disk Checker API started")
|
||||
|
||||
yield
|
||||
@@ -37,7 +58,13 @@ app = FastAPI(
|
||||
version="0.1.0",
|
||||
lifespan=lifespan,
|
||||
)
|
||||
# Setup static files
|
||||
app.mount("/static", StaticFiles(directory=str(STATIC_DIR)), name="static")
|
||||
# Include routers
|
||||
app.include_router(api_router, prefix="/api/v1")
|
||||
app.include_router(web_router)
|
||||
|
||||
# Add CORS (permissive by default)
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["*"],
|
||||
@@ -46,13 +73,8 @@ app.add_middleware(
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
|
||||
# Simple health endpoint
|
||||
@app.get("/health")
|
||||
async def health_check():
|
||||
"""Health check endpoint."""
|
||||
return {"status": "ok", "version": "0.1.0"}
|
||||
|
||||
|
||||
# Import and include routers
|
||||
from disk_checker.web.routes import router as api_router
|
||||
app.include_router(api_router, prefix="/api/v1")
|
||||
return {"status": "ok", "version": "0.1.0"}
|
||||
@@ -0,0 +1,19 @@
|
||||
from fastapi import APIRouter, Request
|
||||
from fastapi.templating import Jinja2Templates
|
||||
from typing import Dict
|
||||
|
||||
web_router = APIRouter()
|
||||
|
||||
@web_router.get("/")
|
||||
async def render_home(request: Request):
|
||||
"""
|
||||
Render the main page
|
||||
"""
|
||||
tools = request.app.state.available_tools
|
||||
return request.app.state.templates.TemplateResponse(
|
||||
name="index.html",
|
||||
request=request,
|
||||
context={
|
||||
"tools":tools
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,17 @@
|
||||
/* Basic styling for Disk Checker */
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
}
|
||||
|
||||
.card {
|
||||
transition: transform 0.2s;
|
||||
}
|
||||
|
||||
.card:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.badge {
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Disk Checker</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css"
|
||||
rel="stylesheet">
|
||||
<script src="https://unpkg.com/htmx.org@1.9.10"></script>
|
||||
<link rel="stylesheet" href="/static/css/style.css">
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
|
||||
<div class="container">
|
||||
<a class="navbar-brand" href="/">Disk Checker</a>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!--Available tools block, tools are loaded automatically-->
|
||||
<div class="container">
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,34 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container mt-4">
|
||||
<h2>Available Benchmarking Tools</h2>
|
||||
|
||||
<!-- Server-side rendered tools -->
|
||||
{% if tools %}
|
||||
<div class="row">
|
||||
{% for tool_name, tool in tools.items() %}
|
||||
<div class="col-md-4 mb-3">
|
||||
<div class="card {% if tool.available %}border-success{% else %}border-secondary{% endif %}">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">{{ tool.name }}</h5>
|
||||
<p class="card-text">
|
||||
{% if tool.available %}
|
||||
<span class="badge bg-success">Available</span>
|
||||
{% if tool.version %}
|
||||
<small class="text-muted">{{ tool.version }}</small>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<span class="badge bg-secondary">Not Installed</span>
|
||||
{% endif %}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div id="tools-content"></div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user