feat(config): section statistiques — BDD, médias, entités
- GET /api/admin/stats : taille BDD (pg_database_size), nb+poids photos/audio (scan filesystem), nb notes/todos/listes (requêtes SQL directes) - ConfigPage : grille 3 colonnes todos/notes/listes + 2 tuiles médias + ligne BDD Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -4,13 +4,50 @@ from datetime import datetime
|
||||
from pathlib import Path
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from fastapi import APIRouter, HTTPException
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from sqlalchemy import text
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.core.config import settings
|
||||
from app.core.database import get_session
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
def _dir_stats(path: Path) -> dict:
|
||||
files = [f for f in path.rglob("*") if f.is_file() and not f.name.startswith(".")]
|
||||
return {"count": len(files), "size_bytes": sum(f.stat().st_size for f in files)}
|
||||
|
||||
|
||||
@router.get("/stats")
|
||||
async def get_stats(session: AsyncSession = Depends(get_session)):
|
||||
# Taille de la BDD
|
||||
db_size = (await session.execute(text("SELECT pg_database_size(current_database())"))).scalar()
|
||||
|
||||
# Compteurs
|
||||
notes_count = (await session.execute(text("SELECT COUNT(*) FROM notes.items"))).scalar()
|
||||
todos_count = (await session.execute(text("SELECT COUNT(*) FROM todos.items"))).scalar()
|
||||
lists_count = (await session.execute(text("SELECT COUNT(*) FROM shopping.lists"))).scalar()
|
||||
|
||||
# Médias sur le disque
|
||||
uploads = settings.upload_path
|
||||
photos = _dir_stats(uploads / "images" / "originals") if (uploads / "images" / "originals").exists() else {"count": 0, "size_bytes": 0}
|
||||
audio = _dir_stats(uploads / "audio") if (uploads / "audio").exists() else {"count": 0, "size_bytes": 0}
|
||||
|
||||
return {
|
||||
"db_size_bytes": db_size,
|
||||
"media": {
|
||||
"photos": photos,
|
||||
"audio": audio,
|
||||
},
|
||||
"counts": {
|
||||
"notes": notes_count,
|
||||
"todos": todos_count,
|
||||
"shopping_lists": lists_count,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def _pg_env() -> dict:
|
||||
url = urlparse(settings.database_url.replace("+asyncpg", ""))
|
||||
env = os.environ.copy()
|
||||
|
||||
Reference in New Issue
Block a user