multi-use-api / app /routers /testers /server_status.py
sameerbanchhor's picture
Upload folder using huggingface_hub
2064c2f verified
from fastapi import APIRouter, status
from fastapi.responses import JSONResponse, HTMLResponse
import platform
import psutil
# Initialize the router for this feature
router = APIRouter(
prefix="/server-status",
tags=["Server Info"],
responses={404: {"description": "Not found"}},
)
# ==========================
# πŸ“Š API Endpoint
# ==========================
@router.get(
"/full-info",
summary="Get complete server/PC status and information",
status_code=status.HTTP_200_OK
)
def get_server_status():
"""
Retrieves detailed hardware and software status of the server.
"""
# --- System Information ---
system_info = {
"system": platform.system(),
"node_name": platform.node(),
"release": platform.release(),
"version": platform.version(),
"machine": platform.machine(),
"processor": platform.processor(),
}
# --- CPU Information ---
# Note: cpu_percent(interval=None) is non-blocking but requires a previous call
# or it returns 0.0 on first call. For an API, interval=0.1 is a good compromise.
freq = psutil.cpu_freq()
cpu_info = {
"physical_cores": psutil.cpu_count(logical=False),
"total_cores": psutil.cpu_count(logical=True),
"cpu_usage_percent": psutil.cpu_percent(interval=0.1),
"cpu_frequency_mhz": f"{freq.current:.2f}" if freq else "N/A",
}
# --- Memory Information (RAM) ---
virtual_memory = psutil.virtual_memory()
memory_info = {
"total_gb": f"{virtual_memory.total / (1024**3):.2f}",
"available_gb": f"{virtual_memory.available / (1024**3):.2f}",
"used_gb": f"{virtual_memory.used / (1024**3):.2f}",
"usage_percent": virtual_memory.percent,
}
# --- Disk Usage Information (Root partition) ---
try:
disk_usage = psutil.disk_usage('/')
disk_info = {
"total_gb": f"{disk_usage.total / (1024**3):.2f}",
"used_gb": f"{disk_usage.used / (1024**3):.2f}",
"free_gb": f"{disk_usage.free / (1024**3):.2f}",
"usage_percent": disk_usage.percent,
}
except Exception:
disk_info = {"error": "Could not read disk usage"}
# --- Network Information (Simple) ---
net_io = psutil.net_io_counters()
network_info = {
"bytes_sent_mb": f"{net_io.bytes_sent / (1024**2):.2f}",
"bytes_recv_mb": f"{net_io.bytes_recv / (1024**2):.2f}",
}
response_data = {
"system_information": system_info,
"cpu_status": cpu_info,
"memory_status": memory_info,
"disk_status": disk_info,
"network_io": network_info,
}
return JSONResponse(content=response_data, status_code=status.HTTP_200_OK)
# ==========================
# πŸ–₯️ Dashboard UI Endpoint
# ==========================
@router.get("/ui", response_class=HTMLResponse)
async def server_status_ui():
html_content = """
<!DOCTYPE html>
<html lang="en" data-theme="light">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Server Monitor | by Sam</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700&family=JetBrains+Mono:wght@500&display=swap" rel="stylesheet">
<style>
:root {
--primary: #3b82f6;
--primary-hover: #2563eb;
--bg-body: #f8fafc;
--bg-card: #ffffff;
--text-main: #0f172a;
--text-sub: #64748b;
--border: #e2e8f0;
--shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1);
--success: #10b981;
--warning: #f59e0b;
--danger: #ef4444;
}
[data-theme="dark"] {
--primary: #60a5fa;
--bg-body: #0f172a;
--bg-card: #1e293b;
--text-main: #f8fafc;
--text-sub: #94a3b8;
--border: #334155;
--shadow: 0 10px 15px -3px rgb(0 0 0 / 0.5);
}
* { box-sizing: border-box; margin: 0; padding: 0; transition: all 0.3s ease; }
body {
font-family: 'Plus Jakarta Sans', sans-serif;
background-color: var(--bg-body);
color: var(--text-main);
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
padding: 2rem 1rem;
}
.navbar {
width: 100%; max-width: 1000px; display: flex;
justify-content: space-between; align-items: center; margin-bottom: 2rem;
}
.brand { font-size: 1.5rem; font-weight: 800; display: flex; align-items: center; gap: 0.5rem; }
.brand span { color: var(--primary); }
.theme-toggle {
background: var(--bg-card); border: 1px solid var(--border);
color: var(--text-main); padding: 8px; border-radius: 50%; cursor: pointer;
}
/* Grid Layout */
.dashboard {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 1.5rem;
width: 100%;
max-width: 1000px;
}
.card {
background: var(--bg-card);
border-radius: 16px;
padding: 1.5rem;
border: 1px solid var(--border);
box-shadow: var(--shadow);
}
.card-header {
font-size: 0.9rem; font-weight: 600; color: var(--text-sub);
text-transform: uppercase; letter-spacing: 0.5px;
margin-bottom: 1rem; display: flex; justify-content: space-between;
}
.stat-value {
font-size: 2rem; font-weight: 700; color: var(--text-main);
font-family: 'JetBrains Mono', monospace;
}
.stat-sub { font-size: 0.9rem; color: var(--text-sub); margin-top: 0.2rem; }
/* Progress Bars */
.progress-container {
height: 12px; background: var(--border); border-radius: 6px;
margin-top: 1rem; overflow: hidden;
}
.progress-bar {
height: 100%; width: 0%; background: var(--primary);
border-radius: 6px; transition: width 0.5s ease-in-out;
}
/* System List */
.sys-list { list-style: none; }
.sys-list li {
display: flex; justify-content: space-between;
padding: 0.5rem 0; border-bottom: 1px solid var(--border);
font-size: 0.9rem;
}
.sys-list li:last-child { border-bottom: none; }
.sys-label { color: var(--text-sub); }
.sys-val { font-weight: 600; }
/* Footer */
.dev-footer {
margin-top: 3rem; text-align: center; font-size: 0.9rem; color: var(--text-sub);
padding: 1rem; border-top: 1px solid var(--border); width: 100%; max-width: 600px;
}
.dev-badge {
display: inline-block; background: var(--bg-card); padding: 5px 15px;
border-radius: 20px; border: 1px solid var(--border); font-weight: 500; margin-top: 5px;
}
.dev-name { color: var(--primary); font-weight: 700; }
.refresh-dot {
width: 10px; height: 10px; background-color: var(--success);
border-radius: 50%; display: inline-block; animation: pulse 2s infinite;
}
@keyframes pulse { 0% { opacity: 1; } 50% { opacity: 0.4; } 100% { opacity: 1; } }
</style>
</head>
<body>
<nav class="navbar">
<div class="brand">πŸ“Š Server Monitor <span class="refresh-dot" title="Live Updates"></span></div>
<button class="theme-toggle" onclick="toggleTheme()" id="themeBtn">πŸŒ™</button>
</nav>
<div class="dashboard">
<div class="card">
<div class="card-header">
<span>CPU Usage</span>
<span id="cpuCores">-- Cores</span>
</div>
<div class="stat-value" id="cpuPercent">0%</div>
<div class="progress-container">
<div class="progress-bar" id="cpuBar"></div>
</div>
<div class="stat-sub" id="cpuFreq">Freq: -- MHz</div>
</div>
<div class="card">
<div class="card-header">Memory (RAM)</div>
<div class="stat-value" id="memPercent">0%</div>
<div class="progress-container">
<div class="progress-bar" id="memBar"></div>
</div>
<div class="stat-sub" id="memDetails">Used: -- / -- GB</div>
</div>
<div class="card">
<div class="card-header">Disk (Root)</div>
<div class="stat-value" id="diskPercent">0%</div>
<div class="progress-container">
<div class="progress-bar" id="diskBar"></div>
</div>
<div class="stat-sub" id="diskDetails">Free: -- GB</div>
</div>
<div class="card">
<div class="card-header">Network I/O</div>
<div class="sys-list">
<li>
<span class="sys-label">⬇️ Received</span>
<span class="sys-val" id="netRecv">-- MB</span>
</li>
<li>
<span class="sys-label">⬆️ Sent</span>
<span class="sys-val" id="netSent">-- MB</span>
</li>
</div>
</div>
<div class="card" style="grid-column: 1 / -1;">
<div class="card-header">System Information</div>
<ul class="sys-list" id="sysList">
</ul>
</div>
</div>
<footer class="dev-footer">
<div>Designed & Developed by</div>
<div class="dev-badge">
<span class="dev-name">Sameer Banchhor</span> | Data Scientist
</div>
</footer>
<script>
// Theme Logic
const html = document.documentElement;
const themeBtn = document.getElementById('themeBtn');
function toggleTheme() {
const current = html.getAttribute('data-theme');
const next = current === 'light' ? 'dark' : 'light';
html.setAttribute('data-theme', next);
themeBtn.textContent = next === 'light' ? 'πŸŒ™' : 'β˜€οΈ';
localStorage.setItem('theme', next);
}
const savedTheme = localStorage.getItem('theme') || 'light';
html.setAttribute('data-theme', savedTheme);
themeBtn.textContent = savedTheme === 'light' ? 'πŸŒ™' : 'β˜€οΈ';
// Data Fetching Logic
async function fetchData() {
try {
const res = await fetch('/server-status/full-info');
const data = await res.json();
updateUI(data);
} catch (error) {
console.error("Failed to fetch status:", error);
}
}
function updateUI(data) {
// CPU
const cpuP = data.cpu_status.cpu_usage_percent;
document.getElementById('cpuPercent').textContent = cpuP + '%';
document.getElementById('cpuBar').style.width = cpuP + '%';
setColor('cpuBar', cpuP);
document.getElementById('cpuCores').textContent = `${data.cpu_status.physical_cores}P / ${data.cpu_status.total_cores}L Cores`;
document.getElementById('cpuFreq').textContent = `Freq: ${data.cpu_status.cpu_frequency_mhz} MHz`;
// RAM
const memP = data.memory_status.usage_percent;
document.getElementById('memPercent').textContent = memP + '%';
document.getElementById('memBar').style.width = memP + '%';
setColor('memBar', memP);
document.getElementById('memDetails').textContent = `Used: ${data.memory_status.used_gb} / ${data.memory_status.total_gb} GB`;
// Disk
const diskP = data.disk_status.usage_percent;
document.getElementById('diskPercent').textContent = diskP + '%';
document.getElementById('diskBar').style.width = diskP + '%';
setColor('diskBar', diskP);
document.getElementById('diskDetails').textContent = `Free: ${data.disk_status.free_gb} GB`;
// Network
document.getElementById('netRecv').textContent = data.network_io.bytes_recv_mb + ' MB';
document.getElementById('netSent').textContent = data.network_io.bytes_sent_mb + ' MB';
// System Info (Only populate once if empty)
const sysList = document.getElementById('sysList');
if(sysList.children.length === 0) {
const info = data.system_information;
for (const [key, value] of Object.entries(info)) {
const li = document.createElement('li');
li.innerHTML = `<span class="sys-label">${key}</span><span class="sys-val">${value}</span>`;
sysList.appendChild(li);
}
}
}
function setColor(id, percent) {
const el = document.getElementById(id);
if(percent < 60) el.style.backgroundColor = 'var(--primary)';
else if(percent < 85) el.style.backgroundColor = 'var(--warning)';
else el.style.backgroundColor = 'var(--danger)';
}
// Init
fetchData();
// Refresh every 2 seconds
setInterval(fetchData, 2000);
</script>
</body>
</html>
"""
return html_content