"""Folder and File models for the DMS plugin.""" from __future__ import annotations import uuid from datetime import datetime from sqlalchemy import DateTime, ForeignKey, Index, Integer, String, UniqueConstraint from sqlalchemy.dialects.postgresql import UUID as PGUUID from sqlalchemy.orm import Mapped, mapped_column from app.core.db import Base, TenantMixin class Folder(Base, TenantMixin): """Folder entity — hierarchical, tenant-scoped, soft-deletable.""" __tablename__ = "folders" __table_args__ = ( UniqueConstraint( "tenant_id", "name", "parent_id", name="uq_folders_tenant_name_parent", # Partial uniqueness only for non-deleted folders is handled at app level ), Index("ix_folders_parent", "parent_id"), Index("ix_folders_tenant", "tenant_id"), ) id: Mapped[uuid.UUID] = mapped_column( PGUUID(as_uuid=True), primary_key=True, default=uuid.uuid4 ) name: Mapped[str] = mapped_column(String(255), nullable=False) parent_id: Mapped[uuid.UUID | None] = mapped_column( PGUUID(as_uuid=True), ForeignKey("folders.id", ondelete="CASCADE"), nullable=True, ) created_by: Mapped[uuid.UUID] = mapped_column(PGUUID(as_uuid=True), nullable=False) deleted_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True) class File(Base, TenantMixin): """File entity — stored on disk, tenant-scoped, soft-deletable.""" __tablename__ = "files" __table_args__ = ( Index("ix_files_folder", "folder_id"), Index("ix_files_tenant", "tenant_id"), Index("ix_files_name", "name"), ) id: Mapped[uuid.UUID] = mapped_column( PGUUID(as_uuid=True), primary_key=True, default=uuid.uuid4 ) name: Mapped[str] = mapped_column(String(255), nullable=False) folder_id: Mapped[uuid.UUID | None] = mapped_column( PGUUID(as_uuid=True), ForeignKey("folders.id", ondelete="SET NULL"), nullable=True, ) uploaded_by: Mapped[uuid.UUID] = mapped_column(PGUUID(as_uuid=True), nullable=False) mime_type: Mapped[str] = mapped_column(String(255), nullable=False) size_bytes: Mapped[int] = mapped_column(Integer, nullable=False) storage_path: Mapped[str] = mapped_column(String(1024), nullable=False) deleted_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)