from sqlalchemy import Column, Integer, String, Sequence, create_engine from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker from sqlalchemy import Column, Integer, Text, ForeignKey, DateTime from sqlalchemy.sql import func from sqlalchemy import text SQLALCHEMY_DATABASE_URL = "sqlite:///./chepuhagram.db" engine = create_engine(SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False}) SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) Base = declarative_base() Base.metadata.create_all(bind=engine) class User(Base): __tablename__ = "users" id = Column(Integer, Sequence('user_id_seq', start=100), primary_key=True, index=True) first_name = Column(String(50), nullable=False, server_default="User") last_name = Column(String(50), nullable=True) username = Column(String, unique=True, index=True) about = Column(String, nullable=True) phone = Column(String(20), unique=True, nullable=True) email = Column(String(255), unique=True, nullable=True) totp_secret = Column(String(32), nullable=True) totp_temp_secret = Column(String(32), nullable=True) # Temporary secret until verified hashed_password = Column(String) public_key = Column(String, nullable=True) encrypted_private_key = Column(String, nullable=True) fcm_token = Column(String, nullable=True) avatar_file_id = Column(String, nullable=True) # Privacy settings show_email = Column(Integer, nullable=False, server_default="1") # 1 = true, 0 = false show_phone = Column(Integer, nullable=False, server_default="1") show_avatar = Column(Integer, nullable=False, server_default="1") show_about = Column(Integer, nullable=False, server_default="1") show_username = Column(Integer, nullable=False, server_default="1") show_last_online = Column(Integer, nullable=False, server_default="1") last_online = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now()) class Message(Base): __tablename__ = "messages" id = Column(Integer, primary_key=True, index=True) sender_id = Column(Integer, ForeignKey("users.id")) receiver_id = Column(Integer, ForeignKey("users.id")) content = Column(Text) timestamp = Column(DateTime(timezone=True), server_default=func.now()) delivered_at = Column(DateTime(timezone=True), nullable=True) read_at = Column(DateTime(timezone=True), nullable=True) reply_to_id = Column(Integer, ForeignKey("messages.id"), nullable=True) reply_to_text = Column(Text, nullable=True) edited_at = Column(DateTime(timezone=True), nullable=True) Base.metadata.create_all(bind=engine) def _ensure_sqlite_message_columns(): # Простая авто-миграция для SQLite без Alembic. # Добавляет колонки, если приложение обновилось на уже существующей БД. with engine.connect() as conn: cols = conn.execute(text("PRAGMA table_info(messages)")).fetchall() existing = {row[1] for row in cols} # row[1] = name if "delivered_at" not in existing: conn.execute(text("ALTER TABLE messages ADD COLUMN delivered_at DATETIME")) if "read_at" not in existing: conn.execute(text("ALTER TABLE messages ADD COLUMN read_at DATETIME")) if "reply_to_id" not in existing: conn.execute(text("ALTER TABLE messages ADD COLUMN reply_to_id INTEGER REFERENCES messages(id)")) if "reply_to_text" not in existing: conn.execute(text("ALTER TABLE messages ADD COLUMN reply_to_text TEXT")) if "edited_at" not in existing: conn.execute(text("ALTER TABLE messages ADD COLUMN edited_at DATETIME")) conn.commit() _ensure_sqlite_message_columns() def _ensure_sqlite_user_columns(): with engine.connect() as conn: cols = conn.execute(text("PRAGMA table_info(users)")).fetchall() existing = {row[1] for row in cols} if "about" not in existing: conn.execute(text("ALTER TABLE users ADD COLUMN about TEXT")) if "phone" not in existing: conn.execute(text("ALTER TABLE users ADD COLUMN phone VARCHAR(20)")) if "email" not in existing: conn.execute(text("ALTER TABLE users ADD COLUMN email VARCHAR(255)")) if "show_email" not in existing: conn.execute(text("ALTER TABLE users ADD COLUMN show_email INTEGER DEFAULT 1")) if "show_phone" not in existing: conn.execute(text("ALTER TABLE users ADD COLUMN show_phone INTEGER DEFAULT 1")) if "show_avatar" not in existing: conn.execute(text("ALTER TABLE users ADD COLUMN show_avatar INTEGER DEFAULT 1")) if "show_about" not in existing: conn.execute(text("ALTER TABLE users ADD COLUMN show_about INTEGER DEFAULT 1")) if "show_username" not in existing: conn.execute(text("ALTER TABLE users ADD COLUMN show_username INTEGER DEFAULT 1")) if "show_last_online" not in existing: conn.execute(text("ALTER TABLE users ADD COLUMN show_last_online INTEGER DEFAULT 1")) if "last_online" not in existing: conn.execute(text("ALTER TABLE users ADD COLUMN last_online DATETIME")) conn.execute(text("UPDATE users SET last_online = datetime('now')")) if "avatar_file_id" not in existing: conn.execute(text("ALTER TABLE users ADD COLUMN avatar_file_id VARCHAR(255)")) if "totp_temp_secret" not in existing: conn.execute(text("ALTER TABLE users ADD COLUMN totp_temp_secret VARCHAR(32)")) conn.commit() _ensure_sqlite_user_columns()