feat(platform): add audit logs and activity feed

This commit is contained in:
Schubert Ferenc 2026-07-03 00:04:44 +02:00
parent 92cb8d1286
commit c816e9869d
34 changed files with 1592 additions and 43 deletions

View file

@ -0,0 +1,104 @@
from datetime import datetime
from sqlalchemy import Select, func, select
from sqlalchemy.orm import Session
from app.models.audit import AuditLog
class AuditRepository:
@staticmethod
def create(
db: Session,
*,
actor_user_id: int | None,
actor_username: str,
action: str,
entity_type: str,
entity_id: int | None,
entity_label: str,
ip_address: str,
user_agent: str,
before_data: dict | list | None = None,
after_data: dict | list | None = None,
metadata: dict | list | None = None,
) -> AuditLog:
audit_log = AuditLog(
actor_user_id=actor_user_id,
actor_username=actor_username,
action=action,
entity_type=entity_type,
entity_id=entity_id,
entity_label=entity_label,
ip_address=ip_address,
user_agent=user_agent,
before_data=before_data,
after_data=after_data,
metadata_data=metadata,
)
db.add(audit_log)
db.commit()
db.refresh(audit_log)
return audit_log
@staticmethod
def query(
*,
entity_type: str | None = None,
actor_user_id: int | None = None,
action: str | None = None,
date_from: datetime | None = None,
date_to: datetime | None = None,
) -> Select[tuple[AuditLog]]:
query = select(AuditLog)
if entity_type:
query = query.where(AuditLog.entity_type == entity_type)
if actor_user_id is not None:
query = query.where(AuditLog.actor_user_id == actor_user_id)
if action:
query = query.where(AuditLog.action == action)
if date_from:
query = query.where(AuditLog.created_at >= date_from)
if date_to:
query = query.where(AuditLog.created_at <= date_to)
return query
@staticmethod
def list(
db: Session,
*,
page: int,
page_size: int,
entity_type: str | None = None,
actor_user_id: int | None = None,
action: str | None = None,
date_from: datetime | None = None,
date_to: datetime | None = None,
) -> tuple[list[AuditLog], int]:
base_query = AuditRepository.query(
entity_type=entity_type,
actor_user_id=actor_user_id,
action=action,
date_from=date_from,
date_to=date_to,
)
total = db.scalar(select(func.count()).select_from(base_query.subquery())) or 0
items = list(
db.scalars(
base_query
.order_by(AuditLog.created_at.desc())
.offset((page - 1) * page_size)
.limit(page_size)
)
)
return items, total
@staticmethod
def latest(db: Session, limit: int = 10) -> list[AuditLog]:
return list(
db.scalars(
select(AuditLog)
.order_by(AuditLog.created_at.desc())
.limit(limit)
)
)