Files
rengine/shared/services/activity_log.py
T
Yogesh Ojha 69239333bc Refactor validation and DNS tools, implementation of onboarding wizard
- Added `clean_name` function to `validation.py` for name validation with max length.
- Simplified docstrings in `dnsx` client, parser, and service files for clarity.
- Enhanced error handling in `ViewDNSService` to prevent disabling keys on rate limits.
- Improved WHOIS service to handle concurrent inserts gracefully with integrity checks.
- Updated `Settings` class in worker config to inherit from `BaseAppSettings`.
- Added new dependencies in `pyproject.toml` for `apprise` and `cryptography`.
2026-06-21 21:56:20 +05:30

154 lines
4.6 KiB
Python

from __future__ import annotations
import uuid
from shared.config import BaseAppSettings
from shared.enums.activity import ActivityEvent, ActivityLevel
from shared.enums.sse import SSEChannel, SSEEventType
from shared.logging import get_logger
from shared.models.activity_log import ActivityLog
from shared.services.event_publisher import SyncEventPublisher
from shared.sse import sse_manager
from shared.utils.coerce import safe_uuid
logger = get_logger(__name__)
_sync_publisher = None
def _get_sync_publisher():
global _sync_publisher # noqa: PLW0603
if _sync_publisher is None:
settings = BaseAppSettings()
_sync_publisher = SyncEventPublisher(settings.redis_url)
return _sync_publisher
def _entry_to_sse_payload(entry: ActivityLog) -> dict:
return {
"id": str(entry.id),
"timestamp": entry.timestamp.isoformat(),
"level": entry.level.value
if hasattr(entry.level, "value")
else str(entry.level),
"event_type": entry.event_type.value
if hasattr(entry.event_type, "value")
else str(entry.event_type),
"title": entry.title,
"description": entry.description,
"project_id": str(entry.project_id) if entry.project_id else None,
"target_id": str(entry.target_id) if entry.target_id else None,
"user_id": str(entry.user_id) if entry.user_id else None,
}
class ActivityLogService:
def __init__(self, session) -> None:
self._session = session
def log(
self,
*,
event: ActivityEvent,
title: str,
description: str | None = None,
level: ActivityLevel = ActivityLevel.INFO,
project_id: uuid.UUID | str | None = None,
target_id: uuid.UUID | str | None = None,
user_id: uuid.UUID | str | None = None,
) -> ActivityLog:
entry = self._build_entry(
event=event,
title=title,
description=description,
level=level,
project_id=project_id,
target_id=target_id,
user_id=user_id,
)
self._session.add(entry)
self._session.flush()
logger.info("Activity logged: %s - %s", event.value, title[:80])
if entry.project_id:
self._publish_sync(entry)
return entry
async def log_async(
self,
*,
event: ActivityEvent,
title: str,
description: str | None = None,
level: ActivityLevel = ActivityLevel.INFO,
project_id: uuid.UUID | str | None = None,
target_id: uuid.UUID | str | None = None,
user_id: uuid.UUID | str | None = None,
) -> ActivityLog:
entry = self._build_entry(
event=event,
title=title,
description=description,
level=level,
project_id=project_id,
target_id=target_id,
user_id=user_id,
)
self._session.add(entry)
await self._session.flush()
logger.info("Activity logged: %s - %s", event.value, title[:80])
if entry.project_id:
await self._publish_async(entry)
return entry
@staticmethod
def _publish_sync(entry: ActivityLog) -> None:
try:
publisher = _get_sync_publisher()
publisher.publish(
channel=SSEChannel.project(str(entry.project_id)),
event_type=SSEEventType.ACTIVITY,
data=_entry_to_sse_payload(entry),
)
except Exception as e:
logger.warning("Failed to publish activity to SSE: %s", e)
@staticmethod
async def _publish_async(entry: ActivityLog) -> None:
try:
await sse_manager.publish(
channel=SSEChannel.project(str(entry.project_id)),
event_type=SSEEventType.ACTIVITY,
data=_entry_to_sse_payload(entry),
)
except Exception as e:
logger.warning("Failed to publish activity to SSE: %s", e)
@staticmethod
def _build_entry(
*,
event: ActivityEvent,
title: str,
description: str | None,
level: ActivityLevel,
project_id: uuid.UUID | str | None,
target_id: uuid.UUID | str | None,
user_id: uuid.UUID | str | None,
) -> ActivityLog:
return ActivityLog(
level=level,
event_type=event,
title=title[:200],
description=description[:2000] if description else None,
project_id=safe_uuid(project_id),
target_id=safe_uuid(target_id),
user_id=safe_uuid(user_id),
)