47 lines
2.2 KiB
Python
47 lines
2.2 KiB
Python
from datetime import datetime, timezone
|
|
from sqlalchemy import BigInteger, Column, DateTime, Index, String, Text
|
|
from sqlalchemy.dialects.postgresql import JSONB
|
|
from database.postgres import Base
|
|
|
|
|
|
def _now():
|
|
return datetime.now(timezone.utc)
|
|
|
|
|
|
class MigrationRun(Base):
|
|
"""Tracks every migration script execution — what ran, when, row counts, success/failure."""
|
|
__tablename__ = "_migration_runs"
|
|
|
|
id = Column(BigInteger, primary_key=True, autoincrement=True)
|
|
script_name = Column(String(256), nullable=False)
|
|
ran_at = Column(DateTime(timezone=True), nullable=False, default=_now)
|
|
source_rows = Column(BigInteger, nullable=False, default=0)
|
|
dest_rows = Column(BigInteger, nullable=False, default=0)
|
|
success = Column(String(8), nullable=False, default="ok") # 'ok' | 'error'
|
|
notes = Column(Text)
|
|
|
|
|
|
class AuditLog(Base):
|
|
"""Staff action audit trail — all create/update/delete/command events."""
|
|
__tablename__ = "audit_log"
|
|
__table_args__ = (
|
|
Index("idx_audit_actor", "actor_id", "occurred_at"),
|
|
Index("idx_audit_entity", "entity_type", "entity_id", "occurred_at"),
|
|
Index("idx_audit_action", "action", "occurred_at"),
|
|
Index("idx_audit_occurred", "occurred_at"),
|
|
)
|
|
|
|
id = Column(BigInteger, primary_key=True, autoincrement=True)
|
|
occurred_at = Column(DateTime(timezone=True), nullable=False, default=_now)
|
|
actor_id = Column(String(128), nullable=False)
|
|
actor_name = Column(String(255), nullable=False)
|
|
action = Column(String(64), nullable=False)
|
|
# CREATE | UPDATE | DELETE | COMMAND | PUBLISH | UNPUBLISH |
|
|
# LOGIN | LOGOUT | PERMISSION_CHANGE | STATUS_CHANGE
|
|
entity_type = Column(String(64), nullable=False)
|
|
# customer | order | device | melody | product | staff | ticket | note | quotation | ...
|
|
entity_id = Column(String(128), nullable=False)
|
|
entity_label = Column(String(500)) # denormalised human name
|
|
changes = Column(JSONB) # {"field": {"old": x, "new": y}} — null for CREATE/DELETE
|
|
meta = Column(JSONB) # extra context: ip_address, command_name, etc.
|