from __future__ import annotations from datetime import datetime from typing import Any from sqlalchemy import DateTime, ForeignKey, Index, String, UniqueConstraint from sqlalchemy.orm import Mapped, mapped_column, relationship from werkzeug.security import check_password_hash, generate_password_hash from app.models.base import BaseModel, ID_TYPE, TimestampMixin class Account(BaseModel, TimestampMixin): __tablename__ = "accounts" __table_args__ = ( UniqueConstraint("username", name="uk_accounts_username"), Index("idx_accounts_role_status", "role", "status"), Index("idx_accounts_last_login_at", "last_login_at"), ) id: Mapped[int] = mapped_column(ID_TYPE, primary_key=True) username: Mapped[str] = mapped_column(String(64), nullable=False) password_hash: Mapped[str] = mapped_column(String(255), nullable=False) display_name: Mapped[str | None] = mapped_column(String(64), nullable=True) role: Mapped[str] = mapped_column(String(32), nullable=False, default="editor") status: Mapped[str] = mapped_column(String(16), nullable=False, default="active") last_login_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True) created_by: Mapped[int | None] = mapped_column( ID_TYPE, ForeignKey("accounts.id", ondelete="SET NULL"), nullable=True, ) updated_by: Mapped[int | None] = mapped_column( ID_TYPE, ForeignKey("accounts.id", ondelete="SET NULL"), nullable=True, ) creator: Mapped[Account | None] = relationship( "Account", remote_side="Account.id", foreign_keys=[created_by], post_update=True, ) updater: Mapped[Account | None] = relationship( "Account", remote_side="Account.id", foreign_keys=[updated_by], post_update=True, ) audit_logs: Mapped[list[Any]] = relationship("AuditLog", back_populates="actor") created_households: Mapped[list[Any]] = relationship( "Household", foreign_keys="Household.created_by", back_populates="created_by_account", ) updated_households: Mapped[list[Any]] = relationship( "Household", foreign_keys="Household.updated_by", back_populates="updated_by_account", ) created_members: Mapped[list[Any]] = relationship( "HouseholdMember", foreign_keys="HouseholdMember.created_by", back_populates="created_by_account", ) updated_members: Mapped[list[Any]] = relationship( "HouseholdMember", foreign_keys="HouseholdMember.updated_by", back_populates="updated_by_account", ) created_gift_records: Mapped[list[Any]] = relationship( "GiftRecord", foreign_keys="GiftRecord.created_by", back_populates="created_by_account", ) updated_gift_records: Mapped[list[Any]] = relationship( "GiftRecord", foreign_keys="GiftRecord.updated_by", back_populates="updated_by_account", ) created_option_items: Mapped[list[Any]] = relationship( "OptionItem", foreign_keys="OptionItem.created_by", back_populates="created_by_account", ) updated_option_items: Mapped[list[Any]] = relationship( "OptionItem", foreign_keys="OptionItem.updated_by", back_populates="updated_by_account", ) created_share_links: Mapped[list[Any]] = relationship( "ShareLink", foreign_keys="ShareLink.created_by", back_populates="creator", ) def set_password(self, password: str) -> None: self.password_hash = generate_password_hash(password) def check_password(self, password: str) -> bool: return check_password_hash(self.password_hash, password)