v1
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
|
||||
from sqlalchemy import DateTime, ForeignKey, Index, String, Text, JSON, func
|
||||
from sqlalchemy import Uuid
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
from app.models.base import Base, UUIDMixin
|
||||
|
||||
|
||||
class AuditLog(Base, UUIDMixin):
|
||||
"""Immutable record of member activity and application errors."""
|
||||
|
||||
__tablename__ = "audit_logs"
|
||||
__table_args__ = (
|
||||
Index("ix_audit_logs_timestamp", "timestamp"),
|
||||
Index("ix_audit_logs_member_id", "member_id"),
|
||||
Index("ix_audit_logs_action_type", "action_type"),
|
||||
Index("ix_audit_logs_status", "status"),
|
||||
)
|
||||
|
||||
timestamp: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), server_default=func.now(), nullable=False
|
||||
)
|
||||
# Nullable FK — SET NULL if member is deleted so the log is preserved.
|
||||
member_id: Mapped[Optional[uuid.UUID]] = mapped_column(
|
||||
Uuid(as_uuid=True),
|
||||
ForeignKey("members.id", ondelete="SET NULL"),
|
||||
nullable=True,
|
||||
)
|
||||
# Denormalised for readability after member deletion.
|
||||
member_email: Mapped[Optional[str]] = mapped_column(String(255), nullable=True)
|
||||
|
||||
# One of: login, logout, page_visit, booking_created, booking_cancelled,
|
||||
# profile_updated, onboarding_updated, contract_signed,
|
||||
# account_claimed, message_read, error
|
||||
action_type: Mapped[str] = mapped_column(String(64), nullable=False)
|
||||
|
||||
# Identifies the page / feature area, e.g. "members/dashboard"
|
||||
area: Mapped[str] = mapped_column(String(255), nullable=False)
|
||||
|
||||
# Human-readable one-liner
|
||||
description: Mapped[str] = mapped_column(String(500), nullable=False)
|
||||
|
||||
# success | warning | error
|
||||
status: Mapped[str] = mapped_column(String(16), nullable=False, default="success")
|
||||
|
||||
# Optional related booking — SET NULL if booking is deleted.
|
||||
booking_id: Mapped[Optional[uuid.UUID]] = mapped_column(
|
||||
Uuid(as_uuid=True),
|
||||
ForeignKey("bookings.id", ondelete="SET NULL"),
|
||||
nullable=True,
|
||||
)
|
||||
|
||||
# Error detail — populated for action_type='error' records.
|
||||
error_message: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
|
||||
error_detail: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
|
||||
|
||||
# Request metadata
|
||||
ip_address: Mapped[Optional[str]] = mapped_column(String(64), nullable=True)
|
||||
user_agent: Mapped[Optional[str]] = mapped_column(String(512), nullable=True)
|
||||
|
||||
# Catch-all JSON for any extra context (e.g. booking service_type)
|
||||
extra: Mapped[Optional[dict]] = mapped_column(JSON, nullable=True)
|
||||
|
||||
member: Mapped[Optional["Member"]] = relationship( # type: ignore[name-defined]
|
||||
"Member", foreign_keys=[member_id]
|
||||
)
|
||||
Reference in New Issue
Block a user