118 lines
2.8 KiB
Python
118 lines
2.8 KiB
Python
"""
|
|
©AngelaMos | 2026
|
|
log_ctrl.py
|
|
"""
|
|
|
|
from typing import Any
|
|
|
|
from flask import Response, g
|
|
|
|
from app.config import settings
|
|
from app.core.streaming import publish_event, sse_generator
|
|
from app.engine.normalizer import normalize
|
|
from app.engine.severity import classify
|
|
from app.models.LogEvent import LogEvent
|
|
|
|
|
|
def list_logs() -> dict[str, Any]:
|
|
"""
|
|
Return paginated and filtered log events
|
|
"""
|
|
params = g.validated
|
|
filters = {}
|
|
if params.source_type:
|
|
filters["source_type"] = params.source_type
|
|
if params.severity:
|
|
filters["severity"] = params.severity
|
|
if params.source_ip:
|
|
filters["source_ip"] = params.source_ip
|
|
if params.event_type:
|
|
filters["event_type"] = params.event_type
|
|
|
|
qs = LogEvent.objects(**filters).order_by("-timestamp")
|
|
return LogEvent.paginate(
|
|
queryset = qs,
|
|
page = params.page,
|
|
per_page = params.per_page,
|
|
)
|
|
|
|
|
|
def get_log(log_id: str) -> LogEvent:
|
|
"""
|
|
Return a single log event by ID
|
|
"""
|
|
return LogEvent.get_by_id(log_id)
|
|
|
|
|
|
def ingest_log() -> LogEvent:
|
|
"""
|
|
Normalize, classify, persist, and publish a log event
|
|
"""
|
|
raw = g.validated.model_dump(exclude_none = True)
|
|
|
|
normalized = normalize(raw)
|
|
severity = classify(normalized)
|
|
|
|
event = LogEvent.create_event(
|
|
**normalized,
|
|
severity = severity,
|
|
)
|
|
|
|
publish_event(
|
|
settings.LOG_STREAM_KEY,
|
|
{
|
|
"id": str(event.id),
|
|
"timestamp": str(event.timestamp),
|
|
"source_type": event.source_type,
|
|
"severity": event.severity,
|
|
"event_type": event.event_type,
|
|
"source_ip": event.source_ip,
|
|
"dest_ip": event.dest_ip,
|
|
"hostname": event.hostname,
|
|
"username": event.username,
|
|
},
|
|
)
|
|
|
|
return event
|
|
|
|
|
|
def search_logs() -> dict[str, Any]:
|
|
"""
|
|
Full text search across log events
|
|
"""
|
|
params = g.validated
|
|
return LogEvent.search(
|
|
query = params.q,
|
|
page = params.page,
|
|
per_page = params.per_page,
|
|
)
|
|
|
|
|
|
def stream_logs() -> Response:
|
|
"""
|
|
SSE endpoint that tails the log Redis Stream
|
|
"""
|
|
return Response(
|
|
sse_generator(settings.LOG_STREAM_KEY,
|
|
event_type = "log"),
|
|
mimetype = "text/event-stream",
|
|
headers = {
|
|
"Cache-Control": "no-cache",
|
|
"X-Accel-Buffering": "no",
|
|
},
|
|
)
|
|
|
|
|
|
def pivot() -> list[Any]:
|
|
"""
|
|
Forensic pivot by IP, username, or hostname
|
|
"""
|
|
params = g.validated
|
|
if params.ip:
|
|
return LogEvent.get_by_source_ip(params.ip)
|
|
if params.username:
|
|
return LogEvent.get_by_username(params.username)
|
|
if params.hostname:
|
|
return LogEvent.get_by_hostname(params.hostname)
|
|
return []
|