Feat : Lot of stuff
All checks were successful
Build and Push Docker Images / docker (push) Successful in 51s
All checks were successful
Build and Push Docker Images / docker (push) Successful in 51s
This commit is contained in:
Binary file not shown.
@@ -26,7 +26,8 @@ from pydantic import BaseModel
|
||||
|
||||
# ─── Config ───────────────────────────────────────────────────────────────────
|
||||
|
||||
DB_FILE = Path(os.getenv("DB_FILE", "data/monitor.db"))
|
||||
DB_FILE = Path(os.getenv("DB_FILE", "data/monitor.db"))
|
||||
EXPECTED_AGENT_VERSION = os.getenv("EXPECTED_AGENT_VERSION", "1.1.0")
|
||||
|
||||
# ─── Ring buffer de stats (en mémoire) ───────────────────────────────────────
|
||||
_STATS_MAX_POINTS = 120 # 10 min à 5 s d'intervalle
|
||||
@@ -102,6 +103,13 @@ class SettingUpdateRequest(BaseModel):
|
||||
value: str
|
||||
|
||||
|
||||
class PurgeRequest(BaseModel):
|
||||
table: str # 'vps_stats' | 'login_logs' | 'all'
|
||||
period: str # 'last_24h' | 'last_7d' | 'last_30d' | 'all' | 'custom'
|
||||
from_ts: int | None = None # epoch seconds, utilisé si period == 'custom'
|
||||
to_ts: int | None = None # epoch seconds, utilisé si period == 'custom'
|
||||
|
||||
|
||||
# ─── SQLite ───────────────────────────────────────────────────────────────────
|
||||
|
||||
@contextmanager
|
||||
@@ -360,27 +368,40 @@ async def fetch_vps_status(vps: dict) -> dict:
|
||||
except Exception:
|
||||
system = None
|
||||
|
||||
# Version de l'agent (endpoint sans auth)
|
||||
try:
|
||||
version_res = await agent_get(vps, "/version")
|
||||
agent_version = version_res.get("version", "unknown")
|
||||
except Exception:
|
||||
agent_version = "unknown"
|
||||
|
||||
return {
|
||||
"id": vps["id"],
|
||||
"name": vps["name"],
|
||||
"host": vps["host"],
|
||||
"description": vps.get("description", ""),
|
||||
"online": True,
|
||||
"containers": containers_res,
|
||||
"system": system,
|
||||
"tags": vps.get("tags", []),
|
||||
"id": vps["id"],
|
||||
"name": vps["name"],
|
||||
"host": vps["host"],
|
||||
"description": vps.get("description", ""),
|
||||
"online": True,
|
||||
"containers": containers_res,
|
||||
"system": system,
|
||||
"tags": vps.get("tags", []),
|
||||
"agent_version": agent_version,
|
||||
"expected_agent_version": EXPECTED_AGENT_VERSION,
|
||||
"agent_up_to_date": agent_version == EXPECTED_AGENT_VERSION,
|
||||
}
|
||||
except Exception as e:
|
||||
return {
|
||||
"id": vps["id"],
|
||||
"name": vps["name"],
|
||||
"host": vps["host"],
|
||||
"description": vps.get("description", ""),
|
||||
"online": False,
|
||||
"error": str(e),
|
||||
"containers": [],
|
||||
"system": None,
|
||||
"tags": vps.get("tags", []),
|
||||
"id": vps["id"],
|
||||
"name": vps["name"],
|
||||
"host": vps["host"],
|
||||
"description": vps.get("description", ""),
|
||||
"online": False,
|
||||
"error": str(e),
|
||||
"containers": [],
|
||||
"system": None,
|
||||
"tags": vps.get("tags", []),
|
||||
"agent_version": None,
|
||||
"expected_agent_version": EXPECTED_AGENT_VERSION,
|
||||
"agent_up_to_date": False,
|
||||
}
|
||||
|
||||
|
||||
@@ -591,6 +612,74 @@ def admin_list_users(_: Annotated[dict, Depends(require_admin)]):
|
||||
]
|
||||
|
||||
|
||||
@app.get("/api/admin/db/info")
|
||||
def admin_db_info(_: Annotated[dict, Depends(require_admin)]):
|
||||
"""Retourne des informations sur le contenu de la base de données."""
|
||||
with get_db() as conn:
|
||||
def _table_info(table: str) -> dict:
|
||||
row = conn.execute(
|
||||
f"SELECT COUNT(*) AS cnt, MIN(ts) AS oldest, MAX(ts) AS newest FROM {table}"
|
||||
).fetchone()
|
||||
return {
|
||||
"count": row["cnt"],
|
||||
"oldest_ts": row["oldest"],
|
||||
"newest_ts": row["newest"],
|
||||
}
|
||||
return {
|
||||
"vps_stats": _table_info("vps_stats"),
|
||||
"login_logs": _table_info("login_logs"),
|
||||
}
|
||||
|
||||
|
||||
_ALLOWED_TABLES = frozenset({"vps_stats", "login_logs"})
|
||||
_ALLOWED_PERIODS = frozenset({"last_24h", "last_7d", "last_30d", "all", "custom"})
|
||||
|
||||
|
||||
@app.delete("/api/admin/db/purge")
|
||||
def admin_db_purge(body: PurgeRequest, _: Annotated[dict, Depends(require_admin)]):
|
||||
"""Supprime des entrées de la base de données selon la table et la période choisies."""
|
||||
if body.table not in _ALLOWED_TABLES and body.table != "all":
|
||||
raise HTTPException(status_code=400, detail="Table inconnue")
|
||||
if body.period not in _ALLOWED_PERIODS:
|
||||
raise HTTPException(status_code=400, detail="Période inconnue")
|
||||
|
||||
tables = list(_ALLOWED_TABLES) if body.table == "all" else [body.table]
|
||||
|
||||
now = int(time.time())
|
||||
period_cutoff = {
|
||||
"last_24h": now - 24 * 3600,
|
||||
"last_7d": now - 7 * 24 * 3600,
|
||||
"last_30d": now - 30 * 24 * 3600,
|
||||
}
|
||||
|
||||
deleted: dict[str, int] = {}
|
||||
with get_db() as conn:
|
||||
for tbl in tables:
|
||||
if body.period == "all":
|
||||
cur = conn.execute(f"DELETE FROM {tbl}") # nosec – tbl validated above
|
||||
elif body.period == "custom":
|
||||
if body.from_ts is None or body.to_ts is None:
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail="Période personnalisée : from_ts et to_ts sont requis",
|
||||
)
|
||||
if body.from_ts > body.to_ts:
|
||||
raise HTTPException(status_code=400, detail="from_ts doit être ≤ to_ts")
|
||||
cur = conn.execute(
|
||||
f"DELETE FROM {tbl} WHERE ts >= ? AND ts <= ?", # nosec
|
||||
(body.from_ts, body.to_ts),
|
||||
)
|
||||
else:
|
||||
cutoff = period_cutoff[body.period]
|
||||
cur = conn.execute(
|
||||
f"DELETE FROM {tbl} WHERE ts >= ?", # nosec
|
||||
(cutoff,),
|
||||
)
|
||||
deleted[tbl] = cur.rowcount
|
||||
|
||||
return {"deleted": deleted, "status": "ok"}
|
||||
|
||||
|
||||
# ─── Routes VPS ───────────────────────────────────────────────────────────────
|
||||
|
||||
@app.get("/api/vps")
|
||||
@@ -756,3 +845,18 @@ async def compose_update(
|
||||
return await r.json()
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=502, detail=str(e))
|
||||
|
||||
|
||||
@app.post("/api/vps/{vps_id}/agent/update")
|
||||
async def agent_self_update(
|
||||
vps_id: str,
|
||||
_: Annotated[dict, Depends(get_current_user)] = None,
|
||||
):
|
||||
"""Déclenche la mise à jour de l'agent sur le VPS."""
|
||||
vps = next((v for v in load_vps() if v["id"] == vps_id), None)
|
||||
if not vps:
|
||||
raise HTTPException(status_code=404, detail="VPS introuvable")
|
||||
try:
|
||||
return await agent_post(vps, "/self-update")
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=502, detail=str(e))
|
||||
|
||||
Reference in New Issue
Block a user