Files
egg 44822a561a feat: Improve file display, timezone handling, and LOT management
Changes:
- Fix datetime serialization with UTC 'Z' suffix for correct timezone display
- Add PDF upload support with extension fallback for MIME detection
- Fix LOT add/remove by creating new list for SQLAlchemy JSON change detection
- Add file message components (FileMessage, ImageLightbox, UploadPreview)
- Add multi-file upload support with progress tracking
- Link uploaded files to chat messages via message_id
- Include file attachments in AI report generation
- Update specs for file-storage, realtime-messaging, and ai-report-generation

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-08 12:39:15 +08:00

50 lines
1.9 KiB
Python

"""Database models for file storage"""
from sqlalchemy import Column, String, BigInteger, DateTime, Index, ForeignKey
from sqlalchemy.orm import relationship
from datetime import datetime
from app.core.database import Base
class RoomFile(Base):
"""File uploaded to an incident room"""
__tablename__ = "tr_room_files"
# Primary key
file_id = Column(String(36), primary_key=True)
# Foreign key to incident room (CASCADE delete when room is permanently deleted)
room_id = Column(String(36), ForeignKey("tr_incident_rooms.room_id", ondelete="CASCADE"), nullable=False)
# Foreign key to associated message (nullable for legacy files)
message_id = Column(String(36), ForeignKey("tr_messages.message_id", ondelete="SET NULL"), nullable=True)
# File metadata
uploader_id = Column(String(255), nullable=False)
filename = Column(String(255), nullable=False)
file_type = Column(String(20), nullable=False) # 'image', 'document', 'log'
mime_type = Column(String(100), nullable=False)
file_size = Column(BigInteger, nullable=False) # bytes
# MinIO storage information
minio_bucket = Column(String(100), nullable=False)
minio_object_path = Column(String(500), nullable=False)
# Timestamps
uploaded_at = Column(DateTime, default=datetime.utcnow, nullable=False)
deleted_at = Column(DateTime, nullable=True) # soft delete
# Relationships
room = relationship("IncidentRoom", back_populates="files")
message = relationship("Message", backref="file_attachment", uselist=False)
# Indexes
__table_args__ = (
Index("ix_tr_room_files_room_uploaded", "room_id", "uploaded_at"),
Index("ix_tr_room_files_uploader", "uploader_id"),
Index("ix_tr_room_files_message", "message_id"),
)
def __repr__(self):
return f"<RoomFile(file_id={self.file_id}, filename={self.filename}, room_id={self.room_id})>"