from datetime import UTC, datetime from uuid import UUID from sqlalchemy import Boolean, DateTime, ForeignKey, Integer, String, Text from sqlalchemy.orm import Mapped, mapped_column from uuid_utils import uuid7 from .db import Base def new_id() -> UUID: return UUID(str(uuid7())) def utcnow() -> datetime: return datetime.now(UTC) class AppState(Base): __tablename__ = "app_state" key: Mapped[str] = mapped_column(String(64), primary_key=True) created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utcnow) class User(Base): __tablename__ = "users" id: Mapped[UUID] = mapped_column(primary_key=True, default=new_id) username: Mapped[str] = mapped_column(String(64), unique=True, index=True) password_hash: Mapped[str] = mapped_column(Text) timezone: Mapped[str] = mapped_column(String(64), default="Asia/Shanghai") created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utcnow) class Session(Base): __tablename__ = "sessions" id: Mapped[UUID] = mapped_column(primary_key=True, default=new_id) token_hash: Mapped[str] = mapped_column(String(64), unique=True, index=True) user_id: Mapped[UUID] = mapped_column(ForeignKey("users.id", ondelete="CASCADE"), index=True) expires_at: Mapped[datetime] = mapped_column(DateTime(timezone=True)) created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utcnow) class Folder(Base): __tablename__ = "folders" id: Mapped[UUID] = mapped_column(primary_key=True, default=new_id) user_id: Mapped[UUID] = mapped_column(ForeignKey("users.id", ondelete="CASCADE"), index=True) name: Mapped[str] = mapped_column(String(120)) position: Mapped[int] = mapped_column(Integer, default=0) created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utcnow) class TaskList(Base): __tablename__ = "task_lists" id: Mapped[UUID] = mapped_column(primary_key=True, default=new_id) user_id: Mapped[UUID] = mapped_column(ForeignKey("users.id", ondelete="CASCADE"), index=True) folder_id: Mapped[UUID | None] = mapped_column(ForeignKey("folders.id", ondelete="SET NULL"), nullable=True) name: Mapped[str] = mapped_column(String(120)) is_inbox: Mapped[bool] = mapped_column(Boolean, default=False) position: Mapped[int] = mapped_column(Integer, default=0) created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utcnow) class Task(Base): __tablename__ = "tasks" id: Mapped[UUID] = mapped_column(primary_key=True, default=new_id) user_id: Mapped[UUID] = mapped_column(ForeignKey("users.id", ondelete="CASCADE"), index=True) list_id: Mapped[UUID] = mapped_column(ForeignKey("task_lists.id", ondelete="CASCADE"), index=True) parent_id: Mapped[UUID | None] = mapped_column(ForeignKey("tasks.id", ondelete="CASCADE"), nullable=True) title: Mapped[str] = mapped_column(String(500)) description: Mapped[str] = mapped_column(Text, default="") priority: Mapped[int] = mapped_column(Integer, default=0) completed: Mapped[bool] = mapped_column(Boolean, default=False, index=True) due_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True) version: Mapped[int] = mapped_column(Integer, default=1) position: Mapped[int] = mapped_column(Integer, default=0) created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utcnow) updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utcnow, onupdate=utcnow) deleted_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)