from datetime import datetime from typing import Optional from sqlalchemy import BigInteger, DateTime, Enum, ForeignKey, Integer, JSON, SmallInteger, String, Text, func from sqlalchemy.orm import Mapped, mapped_column, relationship from .database import Base class Admin(Base): __tablename__ = "tpc_admins" id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) username: Mapped[str] = mapped_column(String(64), unique=True, nullable=False) password_hash: Mapped[str] = mapped_column(String(128), nullable=False) is_active: Mapped[bool] = mapped_column(default=True, nullable=False) created_at: Mapped[datetime] = mapped_column(DateTime, server_default=func.now(), nullable=False) last_login_at: Mapped[Optional[datetime]] = mapped_column(DateTime) class Developer(Base): __tablename__ = "tpc_developers" id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True) name: Mapped[str] = mapped_column(String(128), nullable=False) email: Mapped[Optional[str]] = mapped_column(String(255)) website: Mapped[Optional[str]] = mapped_column(String(512)) avatar_url: Mapped[Optional[str]] = mapped_column(String(512)) verified: Mapped[bool] = mapped_column(default=False, nullable=False) created_at: Mapped[datetime] = mapped_column(DateTime, server_default=func.now(), nullable=False) updated_at: Mapped[datetime] = mapped_column( DateTime, server_default=func.now(), onupdate=func.now(), nullable=False ) apps: Mapped[list["App"]] = relationship(back_populates="developer") class Category(Base): __tablename__ = "tpc_categories" id: Mapped[int] = mapped_column(SmallInteger, primary_key=True, autoincrement=True) slug: Mapped[str] = mapped_column(String(64), unique=True, nullable=False) name: Mapped[str] = mapped_column(String(128), nullable=False) description: Mapped[Optional[str]] = mapped_column(String(255)) icon: Mapped[Optional[str]] = mapped_column(String(64)) sort_order: Mapped[int] = mapped_column(SmallInteger, default=0, nullable=False) created_at: Mapped[datetime] = mapped_column(DateTime, server_default=func.now(), nullable=False) apps: Mapped[list["App"]] = relationship(secondary="tpc_app_category_map", back_populates="categories") class AppCategoryMap(Base): __tablename__ = "tpc_app_category_map" app_id: Mapped[int] = mapped_column( BigInteger, ForeignKey("tpc_apps.id", ondelete="CASCADE"), primary_key=True ) category_id: Mapped[int] = mapped_column( SmallInteger, ForeignKey("tpc_categories.id", ondelete="CASCADE"), primary_key=True ) class AppVersion(Base): __tablename__ = "tpc_app_versions" id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True) app_id: Mapped[int] = mapped_column(BigInteger, ForeignKey("tpc_apps.id", ondelete="CASCADE"), nullable=False) version: Mapped[str] = mapped_column(String(64), nullable=False) storage_url: Mapped[Optional[str]] = mapped_column(String(512)) checksum_sha256: Mapped[Optional[str]] = mapped_column(String(64)) size_bytes: Mapped[Optional[int]] = mapped_column(BigInteger) manifest: Mapped[Optional[dict]] = mapped_column(JSON) release_notes: Mapped[Optional[str]] = mapped_column(Text) min_client_version: Mapped[Optional[str]] = mapped_column(String(32)) status: Mapped[str] = mapped_column( Enum("draft", "published", "yanked", name="version_status"), default="draft", nullable=False, ) published_at: Mapped[Optional[datetime]] = mapped_column(DateTime) created_at: Mapped[datetime] = mapped_column(DateTime, server_default=func.now(), nullable=False) app: Mapped["App"] = relationship(back_populates="versions", foreign_keys=[app_id]) class App(Base): __tablename__ = "tpc_apps" id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True) app_uid: Mapped[str] = mapped_column(String(64), unique=True, nullable=False, index=True) name: Mapped[str] = mapped_column(String(255), nullable=False) type: Mapped[str] = mapped_column( Enum("system", "community", "external", name="app_type"), nullable=False, index=True ) developer_id: Mapped[Optional[int]] = mapped_column(BigInteger, ForeignKey("tpc_developers.id", ondelete="SET NULL")) icon_url: Mapped[Optional[str]] = mapped_column(String(512)) short_description: Mapped[Optional[str]] = mapped_column(String(255)) description: Mapped[Optional[str]] = mapped_column(Text) homepage_url: Mapped[Optional[str]] = mapped_column(String(512)) current_version_id: Mapped[Optional[int]] = mapped_column(BigInteger, ForeignKey("tpc_app_versions.id")) status: Mapped[str] = mapped_column( Enum("draft", "published", "deprecated", "suspended", name="app_status"), default="draft", nullable=False, index=True, ) is_featured: Mapped[bool] = mapped_column(default=False, nullable=False, index=True) download_count: Mapped[int] = mapped_column(BigInteger, default=0, nullable=False) publish_time: Mapped[Optional[datetime]] = mapped_column(DateTime, index=True) created_at: Mapped[datetime] = mapped_column(DateTime, server_default=func.now(), nullable=False) updated_at: Mapped[datetime] = mapped_column( DateTime, server_default=func.now(), onupdate=func.now(), nullable=False ) developer: Mapped[Optional["Developer"]] = relationship(back_populates="apps") categories: Mapped[list["Category"]] = relationship(secondary="tpc_app_category_map", back_populates="apps") versions: Mapped[list["AppVersion"]] = relationship( back_populates="app", foreign_keys="AppVersion.app_id", order_by="AppVersion.published_at.desc()", ) current_version: Mapped[Optional["AppVersion"]] = relationship( foreign_keys=[current_version_id], lazy="joined" )