SecureChat / src /models.py
ausername-12345
reuse register endpoint for google setup
73d7d26
from sqlalchemy import Column, Integer, String, Text, DateTime, Boolean, ForeignKey, Enum
from sqlalchemy.orm import relationship
from sqlalchemy.sql import func
from database import Base
import enum
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True, index=True)
username = Column(String(50), unique=True, index=True, nullable=False)
display_name = Column(String(100), nullable=False)
email = Column(String(255), unique=True, index=True, nullable=True)
hashed_password = Column(String, nullable=True)
google_id = Column(String, unique=True, nullable=True)
avatar_color = Column(String(7), default="#6366f1")
public_key = Column(Text, nullable=True) # RSA public key for E2EE
created_at = Column(DateTime(timezone=True), server_default=func.now())
is_online = Column(Boolean, default=False)
last_seen = Column(DateTime(timezone=True), nullable=True)
sent_messages = relationship("Message", foreign_keys="Message.sender_id", back_populates="sender")
conversations = relationship("ConversationMember", back_populates="user")
group_memberships = relationship("GroupMember", back_populates="user")
class Conversation(Base):
__tablename__ = "conversations"
id = Column(Integer, primary_key=True, index=True)
created_at = Column(DateTime(timezone=True), server_default=func.now())
members = relationship("ConversationMember", back_populates="conversation")
messages = relationship("Message", back_populates="conversation", order_by="Message.created_at")
class ConversationMember(Base):
__tablename__ = "conversation_members"
id = Column(Integer, primary_key=True)
conversation_id = Column(Integer, ForeignKey("conversations.id"))
user_id = Column(Integer, ForeignKey("users.id"))
conversation = relationship("Conversation", back_populates="members")
user = relationship("User", back_populates="conversations")
class Message(Base):
__tablename__ = "messages"
id = Column(Integer, primary_key=True, index=True)
conversation_id = Column(Integer, ForeignKey("conversations.id"))
sender_id = Column(Integer, ForeignKey("users.id"))
# Encrypted ciphertext (base64 encoded, encrypted with recipient's public key)
ciphertext_for_recipient = Column(Text, nullable=False)
# Also store for sender so they can read their own messages
ciphertext_for_sender = Column(Text, nullable=True)
created_at = Column(DateTime(timezone=True), server_default=func.now())
is_read = Column(Boolean, default=False)
sender = relationship("User", foreign_keys=[sender_id], back_populates="sent_messages")
conversation = relationship("Conversation", back_populates="messages")
class Group(Base):
__tablename__ = "groups"
id = Column(Integer, primary_key=True, index=True)
name = Column(String(100), nullable=False)
description = Column(String(255), nullable=True)
created_by = Column(Integer, ForeignKey("users.id"))
avatar_color = Column(String(7), default="#8b5cf6")
created_at = Column(DateTime(timezone=True), server_default=func.now())
members = relationship("GroupMember", back_populates="group")
messages = relationship("GroupMessage", back_populates="group", order_by="GroupMessage.created_at")
creator = relationship("User", foreign_keys=[created_by])
class GroupMember(Base):
__tablename__ = "group_members"
id = Column(Integer, primary_key=True)
group_id = Column(Integer, ForeignKey("groups.id"))
user_id = Column(Integer, ForeignKey("users.id"))
role = Column(String(20), default="member") # admin, member
joined_at = Column(DateTime(timezone=True), server_default=func.now())
group = relationship("Group", back_populates="members")
user = relationship("User", back_populates="group_memberships")
class GroupMessage(Base):
__tablename__ = "group_messages"
id = Column(Integer, primary_key=True, index=True)
group_id = Column(Integer, ForeignKey("groups.id"))
sender_id = Column(Integer, ForeignKey("users.id"))
# For group chats: AES key encrypted per-member (stored as JSON), message encrypted with AES
encrypted_aes_keys = Column(Text, nullable=False) # JSON: {user_id: encrypted_aes_key}
ciphertext = Column(Text, nullable=False) # AES-GCM encrypted message
iv = Column(String(100), nullable=False) # AES-GCM IV
created_at = Column(DateTime(timezone=True), server_default=func.now())
sender = relationship("User", foreign_keys=[sender_id])
group = relationship("Group", back_populates="messages")