""" models.py – SQLAlchemy ORM models for SexyChat. Tables ------ users – Registered accounts messages – Encrypted PM history (user↔user and user↔AI) """ from datetime import datetime from database import db class User(db.Model): __tablename__ = "users" id = db.Column(db.Integer, primary_key=True) username = db.Column(db.String(20), unique=True, nullable=False, index=True) password_hash = db.Column(db.String(128), nullable=False) email = db.Column(db.String(255), unique=True, nullable=True) has_ai_access = db.Column(db.Boolean, default=False, nullable=False) ai_messages_used = db.Column(db.Integer, default=0, nullable=False) is_verified = db.Column(db.Boolean, default=False, nullable=False) created_at = db.Column(db.DateTime, default=datetime.utcnow, nullable=False) sent_messages = db.relationship( "Message", foreign_keys="Message.sender_id", backref="sender", lazy="dynamic", ) received_messages = db.relationship( "Message", foreign_keys="Message.recipient_id", backref="recipient", lazy="dynamic", ) # Persistent "Ignore" list ignoring = db.relationship( "User", secondary="user_ignores", primaryjoin="User.id == UserIgnore.ignorer_id", secondaryjoin="User.id == UserIgnore.ignored_id", backref=db.backref("ignored_by", lazy="dynamic"), lazy="dynamic" ) def __repr__(self): return f"" class UserIgnore(db.Model): __tablename__ = "user_ignores" id = db.Column(db.Integer, primary_key=True) ignorer_id = db.Column(db.Integer, db.ForeignKey("users.id"), nullable=False) ignored_id = db.Column(db.Integer, db.ForeignKey("users.id"), nullable=False) created_at = db.Column(db.DateTime, default=datetime.utcnow, nullable=False) __table_args__ = ( db.Index("ix_ignore_pair", "ignorer_id", "ignored_id", unique=True), ) class Message(db.Model): __tablename__ = "messages" id = db.Column(db.Integer, primary_key=True) sender_id = db.Column(db.Integer, db.ForeignKey("users.id"), nullable=False) recipient_id = db.Column(db.Integer, db.ForeignKey("users.id"), nullable=False) # AES-GCM ciphertext – base64 encoded; server never stores plaintext encrypted_content = db.Column(db.Text, nullable=False) # AES-GCM nonce / IV – base64 encoded (12 bytes → 16 chars) nonce = db.Column(db.String(64), nullable=False) timestamp = db.Column(db.DateTime, default=datetime.utcnow, nullable=False) __table_args__ = ( # Composite indices for the two common query patterns db.Index("ix_msg_recipient_ts", "recipient_id", "timestamp"), db.Index("ix_msg_sender_ts", "sender_id", "timestamp"), ) def __repr__(self): return f""