diff --git a/decnet/web/api.py b/decnet/web/api.py index 372d830..8cb4418 100644 --- a/decnet/web/api.py +++ b/decnet/web/api.py @@ -123,3 +123,14 @@ async def get_logs( "offset": offset, "data": logs } + + +class StatsResponse(BaseModel): + total_logs: int + unique_attackers: int + active_deckies: int + + +@app.get("/api/v1/stats", response_model=StatsResponse) +async def get_stats(current_user: str = Depends(get_current_user)) -> dict[str, Any]: + return await repo.get_stats_summary() diff --git a/decnet/web/sqlite_repository.py b/decnet/web/sqlite_repository.py index 9d8dc45..7982993 100644 --- a/decnet/web/sqlite_repository.py +++ b/decnet/web/sqlite_repository.py @@ -95,9 +95,14 @@ class SQLiteRepository(BaseRepository): row = await cursor.fetchone() unique_attackers: int = row["unique_attackers"] if row else 0 + async with db.execute("SELECT COUNT(DISTINCT decky) as active_deckies FROM logs") as cursor: + row = await cursor.fetchone() + active_deckies: int = row["active_deckies"] if row else 0 + return { "total_logs": total_logs, - "unique_attackers": unique_attackers + "unique_attackers": unique_attackers, + "active_deckies": active_deckies } async def get_user_by_username(self, username: str) -> Optional[dict[str, Any]]: diff --git a/tests/test_web_api.py b/tests/test_web_api.py index fd87f21..4d81e9e 100644 --- a/tests/test_web_api.py +++ b/tests/test_web_api.py @@ -72,3 +72,26 @@ def test_get_logs_success() -> None: assert "data" in data assert data["total"] >= 0 assert isinstance(data["data"], list) + +def test_get_stats_unauthorized() -> None: + with TestClient(app) as client: + response = client.get("/api/v1/stats") + assert response.status_code == 401 + +def test_get_stats_success() -> None: + with TestClient(app) as client: + login_response = client.post( + "/api/v1/auth/login", + json={"username": "admin", "password": "admin"} + ) + token = login_response.json()["access_token"] + + response = client.get( + "/api/v1/stats", + headers={"Authorization": f"Bearer {token}"} + ) + assert response.status_code == 200 + data = response.json() + assert "total_logs" in data + assert "unique_attackers" in data + assert "active_deckies" in data