106 lines
4.1 KiB
Python
106 lines
4.1 KiB
Python
from typing import List, Dict, Any
|
|
from sqlalchemy import func, select
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
from models import LogEntry
|
|
|
|
class StatsAnalyzer:
|
|
@staticmethod
|
|
async def overview(session: AsyncSession) -> Dict[str, Any]:
|
|
total = (await session.execute(select(func.count()).select_from(LogEntry))).scalar() or 0
|
|
fw = (await session.execute(
|
|
select(func.count()).select_from(LogEntry).where(LogEntry.log_type == "firewall")
|
|
)).scalar() or 0
|
|
px = (await session.execute(
|
|
select(func.count()).select_from(LogEntry).where(LogEntry.log_type == "proxy")
|
|
)).scalar() or 0
|
|
return {
|
|
"total_entries": total,
|
|
"firewall_entries": fw,
|
|
"proxy_entries": px,
|
|
}
|
|
|
|
@staticmethod
|
|
async def top_sources(session: AsyncSession, limit: int = 20) -> List[Dict[str, Any]]:
|
|
stmt = (
|
|
select(LogEntry.source_ip, func.count().label("cnt"))
|
|
.where(LogEntry.source_ip.isnot(None))
|
|
.group_by(LogEntry.source_ip)
|
|
.order_by(func.count().desc())
|
|
.limit(limit)
|
|
)
|
|
rows = await session.execute(stmt)
|
|
return [{"source_ip": r[0], "count": r[1]} for r in rows]
|
|
|
|
@staticmethod
|
|
async def top_destinations(session: AsyncSession, limit: int = 20) -> List[Dict[str, Any]]:
|
|
stmt = (
|
|
select(LogEntry.destination_ip, func.count().label("cnt"))
|
|
.where(LogEntry.destination_ip.isnot(None))
|
|
.group_by(LogEntry.destination_ip)
|
|
.order_by(func.count().desc())
|
|
.limit(limit)
|
|
)
|
|
rows = await session.execute(stmt)
|
|
return [{"destination_ip": r[0], "count": r[1]} for r in rows]
|
|
|
|
@staticmethod
|
|
async def top_ports(session: AsyncSession, limit: int = 20) -> List[Dict[str, Any]]:
|
|
stmt = (
|
|
select(LogEntry.destination_port, func.count().label("cnt"))
|
|
.where(LogEntry.destination_port.isnot(None))
|
|
.group_by(LogEntry.destination_port)
|
|
.order_by(func.count().desc())
|
|
.limit(limit)
|
|
)
|
|
rows = await session.execute(stmt)
|
|
return [{"destination_port": r[0], "count": r[1]} for r in rows]
|
|
|
|
@staticmethod
|
|
async def top_urls(session: AsyncSession, limit: int = 20) -> List[Dict[str, Any]]:
|
|
stmt = (
|
|
select(LogEntry.url, func.count().label("cnt"))
|
|
.where(LogEntry.url.isnot(None))
|
|
.group_by(LogEntry.url)
|
|
.order_by(func.count().desc())
|
|
.limit(limit)
|
|
)
|
|
rows = await session.execute(stmt)
|
|
return [{"url": r[0], "count": r[1]} for r in rows]
|
|
|
|
@staticmethod
|
|
async def action_distribution(session: AsyncSession) -> List[Dict[str, Any]]:
|
|
stmt = (
|
|
select(LogEntry.action, func.count().label("cnt"))
|
|
.where(LogEntry.action.isnot(None))
|
|
.group_by(LogEntry.action)
|
|
.order_by(func.count().desc())
|
|
)
|
|
rows = await session.execute(stmt)
|
|
return [{"action": r[0], "count": r[1]} for r in rows]
|
|
|
|
@staticmethod
|
|
async def timeline(session: AsyncSession, granularity: str = "hour") -> List[Dict[str, Any]]:
|
|
if granularity == "hour":
|
|
fmt = "%Y-%m-%d %H:00"
|
|
else:
|
|
fmt = "%Y-%m-%d"
|
|
# SQLite strftime
|
|
stmt = (
|
|
select(func.strftime(fmt, LogEntry.timestamp).label("bucket"), func.count().label("cnt"))
|
|
.where(LogEntry.timestamp.isnot(None))
|
|
.group_by("bucket")
|
|
.order_by("bucket")
|
|
)
|
|
rows = await session.execute(stmt)
|
|
return [{"time_bucket": r[0], "count": r[1]} for r in rows]
|
|
|
|
@staticmethod
|
|
async def unique_counts(session: AsyncSession) -> Dict[str, int]:
|
|
src = (await session.execute(
|
|
select(func.count(func.distinct(LogEntry.source_ip)))
|
|
)).scalar() or 0
|
|
dst = (await session.execute(
|
|
select(func.count(func.distinct(LogEntry.destination_ip)))
|
|
)).scalar() or 0
|
|
return {"unique_sources": src, "unique_destinations": dst}
|