This commit is contained in:
Danilo Reyes
2025-11-02 15:05:18 -06:00
parent 948fe591dc
commit c68a6a7d01
14 changed files with 1599 additions and 74 deletions

View File

@@ -13,6 +13,7 @@ from app.database.base import Base
if TYPE_CHECKING:
from app.database.models.board_image import BoardImage
from app.database.models.comment import Comment
from app.database.models.group import Group
from app.database.models.share_link import ShareLink
from app.database.models.user import User
@@ -56,6 +57,7 @@ class Board(Base):
share_links: Mapped[list["ShareLink"]] = relationship(
"ShareLink", back_populates="board", cascade="all, delete-orphan"
)
comments: Mapped[list["Comment"]] = relationship("Comment", back_populates="board", cascade="all, delete-orphan")
def __repr__(self) -> str:
"""String representation of Board."""

View File

@@ -1,4 +1,4 @@
"""Comment model for board comments."""
"""Comment model for board annotations."""
import uuid
from datetime import datetime
@@ -11,18 +11,16 @@ from app.database.base import Base
class Comment(Base):
"""Comment model for viewer comments on shared boards."""
"""Comment model representing viewer comments on shared boards."""
__tablename__ = "comments"
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
board_id = Column(UUID(as_uuid=True), ForeignKey("boards.id", ondelete="CASCADE"), nullable=False, index=True)
share_link_id = Column(
UUID(as_uuid=True), ForeignKey("share_links.id", ondelete="SET NULL"), nullable=True, index=True
)
board_id = Column(UUID(as_uuid=True), ForeignKey("boards.id", ondelete="CASCADE"), nullable=False)
share_link_id = Column(UUID(as_uuid=True), ForeignKey("share_links.id", ondelete="SET NULL"), nullable=True)
author_name = Column(String(100), nullable=False)
content = Column(Text, nullable=False)
position = Column(JSONB, nullable=True) # Optional canvas position
position = Column(JSONB, nullable=True) # Optional canvas position reference
created_at = Column(DateTime, nullable=False, default=datetime.utcnow)
is_deleted = Column(Boolean, nullable=False, default=False)
@@ -31,4 +29,4 @@ class Comment(Base):
share_link = relationship("ShareLink", back_populates="comments")
def __repr__(self) -> str:
return f"<Comment(id={self.id}, author={self.author_name})>"
return f"<Comment(id={self.id}, board_id={self.board_id}, author={self.author_name})>"

View File

@@ -1,45 +1,33 @@
"""ShareLink database model."""
"""ShareLink model for board sharing functionality."""
import uuid
from datetime import datetime
from typing import TYPE_CHECKING
from uuid import UUID, uuid4
from sqlalchemy import Boolean, DateTime, ForeignKey, Integer, String
from sqlalchemy.dialects.postgresql import UUID as PGUUID
from sqlalchemy.orm import Mapped, mapped_column, relationship
from sqlalchemy import Boolean, Column, DateTime, ForeignKey, Integer, String
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.orm import relationship
from app.database.base import Base
if TYPE_CHECKING:
from app.database.models.board import Board
class ShareLink(Base):
"""
ShareLink model for sharing boards with configurable permissions.
Share links allow users to share boards with others without requiring
authentication, with permission levels controlling what actions are allowed.
"""
"""ShareLink model representing shareable board links with permissions."""
__tablename__ = "share_links"
id: Mapped[UUID] = mapped_column(PGUUID(as_uuid=True), primary_key=True, default=uuid4)
board_id: Mapped[UUID] = mapped_column(
PGUUID(as_uuid=True), ForeignKey("boards.id", ondelete="CASCADE"), nullable=False
)
token: Mapped[str] = mapped_column(String(64), unique=True, nullable=False, index=True)
permission_level: Mapped[str] = mapped_column(String(20), nullable=False) # 'view-only' or 'view-comment'
created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False, default=datetime.utcnow)
expires_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True)
last_accessed_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True)
access_count: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
is_revoked: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False)
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
board_id = Column(UUID(as_uuid=True), ForeignKey("boards.id", ondelete="CASCADE"), nullable=False)
token = Column(String(64), unique=True, nullable=False, index=True)
permission_level = Column(String(20), nullable=False) # 'view-only' or 'view-comment'
created_at = Column(DateTime, nullable=False, default=datetime.utcnow)
expires_at = Column(DateTime, nullable=True)
last_accessed_at = Column(DateTime, nullable=True)
access_count = Column(Integer, nullable=False, default=0)
is_revoked = Column(Boolean, nullable=False, default=False)
# Relationships
board: Mapped["Board"] = relationship("Board", back_populates="share_links")
board = relationship("Board", back_populates="share_links")
comments = relationship("Comment", back_populates="share_link", cascade="all, delete-orphan")
def __repr__(self) -> str:
"""String representation of ShareLink."""
return f"<ShareLink(id={self.id}, token={self.token[:8]}..., board_id={self.board_id})>"
return f"<ShareLink(id={self.id}, board_id={self.board_id}, permission={self.permission_level})>"