mirror of
https://github.com/andrey271192/vps_monitoring.git
synced 2026-09-20 11:55:34 +00:00
- Sectioned card layout: СЕРВЕРЫ, ДЕЙСТВИЯ, УВЕДОМЛЕНИЯ, СИСТЕМА - Expandable server cards with metrics detail + reboot/delete - Mute/unmute alerts (2h, 8h) with API support - Green/red pulsing status dots, colored badges - Toast notifications, loading spinner - Overview API endpoint for Mini App data - Mute integration with alerter system Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
125 lines
3.5 KiB
Python
125 lines
3.5 KiB
Python
from datetime import datetime, timedelta
|
|
|
|
from fastapi import APIRouter, HTTPException, Request
|
|
from fastapi.responses import RedirectResponse, JSONResponse
|
|
|
|
from server.auth import verify_password, create_token, require_auth
|
|
from server.config import load_settings, save_settings
|
|
from server.models import LoginForm, SettingsUpdate
|
|
|
|
router = APIRouter(tags=["auth"])
|
|
|
|
|
|
@router.post("/api/login")
|
|
async def login(form: LoginForm):
|
|
settings = load_settings()
|
|
if (
|
|
form.username == settings["admin_login"]
|
|
and form.password == settings["admin_password"]
|
|
):
|
|
token = create_token(form.username)
|
|
response = JSONResponse({"status": "ok"})
|
|
response.set_cookie("access_token", token, httponly=True, max_age=86400)
|
|
return response
|
|
raise HTTPException(401, "Invalid credentials")
|
|
|
|
|
|
@router.post("/api/logout")
|
|
async def logout():
|
|
response = RedirectResponse("/login", status_code=302)
|
|
response.delete_cookie("access_token")
|
|
return response
|
|
|
|
|
|
@router.get("/api/settings")
|
|
async def get_settings(request: Request):
|
|
require_auth(request)
|
|
settings = load_settings()
|
|
# Mask sensitive data
|
|
safe = {**settings}
|
|
if safe.get("telegram_bot_token"):
|
|
safe["telegram_bot_token"] = safe["telegram_bot_token"][:10] + "..."
|
|
return safe
|
|
|
|
|
|
@router.put("/api/settings")
|
|
async def update_settings(update: SettingsUpdate, request: Request):
|
|
require_auth(request)
|
|
settings = load_settings()
|
|
for key, val in update.model_dump(exclude_unset=True).items():
|
|
if val is not None:
|
|
settings[key] = val
|
|
save_settings(settings)
|
|
return {"status": "ok"}
|
|
|
|
|
|
# Mute alerts
|
|
mute_until: datetime | None = None
|
|
|
|
|
|
@router.post("/api/mute")
|
|
async def mute_alerts(request: Request):
|
|
global mute_until
|
|
require_auth(request)
|
|
body = await request.json()
|
|
hours = body.get("hours", 2)
|
|
mute_until = datetime.now() + timedelta(hours=hours)
|
|
return {"status": "ok", "muted_until": mute_until.isoformat()}
|
|
|
|
|
|
@router.post("/api/unmute")
|
|
async def unmute_alerts(request: Request):
|
|
global mute_until
|
|
require_auth(request)
|
|
mute_until = None
|
|
return {"status": "ok"}
|
|
|
|
|
|
@router.get("/api/mute/status")
|
|
async def mute_status(request: Request):
|
|
require_auth(request)
|
|
if mute_until and datetime.now() < mute_until:
|
|
return {"muted": True, "until": mute_until.isoformat()}
|
|
return {"muted": False}
|
|
|
|
|
|
@router.get("/api/overview")
|
|
async def overview(request: Request):
|
|
"""Full overview for Telegram Mini App."""
|
|
require_auth(request)
|
|
from server.config import load_servers
|
|
from server.services.monitor import get_all_metrics
|
|
|
|
servers = load_servers()
|
|
metrics = get_all_metrics()
|
|
|
|
srv_list = []
|
|
online_count = 0
|
|
for s in servers:
|
|
m = metrics.get(s["host"], {})
|
|
is_online = m.get("online", False)
|
|
if is_online:
|
|
online_count += 1
|
|
srv_list.append({
|
|
"id": s.get("id", ""),
|
|
"name": s["name"],
|
|
"host": s["host"],
|
|
"online": is_online,
|
|
"cpu": m.get("cpu_percent", 0),
|
|
"ram": m.get("ram_percent", 0),
|
|
"disk": m.get("disk_percent", 0),
|
|
"uptime": m.get("uptime", ""),
|
|
"load": m.get("load_average", ""),
|
|
})
|
|
|
|
is_muted = mute_until and datetime.now() < mute_until
|
|
|
|
return {
|
|
"servers": srv_list,
|
|
"total": len(servers),
|
|
"online": online_count,
|
|
"offline": len(servers) - online_count,
|
|
"muted": is_muted,
|
|
"muted_until": mute_until.isoformat() if is_muted else None,
|
|
}
|