diff --git a/backend/migration.py b/backend/migration.py index e48a669..1e00f0b 100644 --- a/backend/migration.py +++ b/backend/migration.py @@ -19,6 +19,9 @@ def run_migrations(): This function will upgrade the database to the latest migration. Fully automated - handles all scenarios automatically. """ + # Get the directory where this script is located + current_dir = os.path.dirname(os.path.abspath(__file__)) + try: # FIRST: Check if database has any application tables (excluding alembic_version) engine = create_engine(DATABASE_URL) @@ -35,9 +38,6 @@ def run_migrations(): Base.metadata.create_all(bind=engine) logger.info("All tables created successfully from models.") - # Get the directory where this script is located - current_dir = os.path.dirname(os.path.abspath(__file__)) - # Note: Problematic migrations are now cleaned up during Docker build # Create Alembic configuration @@ -487,7 +487,9 @@ def _create_database_directly(): from backend.shared.models import Base from backend.shared.db import get_engine from sqlalchemy import text, inspect - + + engine = get_engine() + # Check existing tables and update schema with engine.connect() as connection: inspector = inspect(connection) @@ -569,9 +571,9 @@ def _create_database_directly(): revision_match = re.search(r"revision: str = '([^']+)'", content) if revision_match: revision_id = revision_match.group(1) - connection.execute(text(f"INSERT OR IGNORE INTO alembic_version (version_num) VALUES ('{revision_id}')")) + connection.execute(text(f"INSERT INTO alembic_version (version_num) VALUES ('{revision_id}') ON CONFLICT DO NOTHING")) else: - connection.execute(text("INSERT OR IGNORE INTO alembic_version (version_num) VALUES ('direct_creation')")) + connection.execute(text("INSERT INTO alembic_version (version_num) VALUES ('direct_creation') ON CONFLICT DO NOTHING")) else: connection.execute(text("INSERT OR IGNORE INTO alembic_version (version_num) VALUES ('direct_creation')")) diff --git a/backend/shared/models.py b/backend/shared/models.py index ca409f3..7e28e34 100644 --- a/backend/shared/models.py +++ b/backend/shared/models.py @@ -155,7 +155,7 @@ class MessageFile(Base): __tablename__ = "message_file" id = Column(Integer, primary_key=True, index=True) - message_id = Column(Integer, ForeignKey("message.id"), nullable=False, index=True) + message_id = Column(BigInteger, ForeignKey("messages.id"), nullable=False, index=True) path = Column(Text, nullable=False) name = Column(Text, nullable=False) @@ -166,7 +166,7 @@ class CryptoPublicKey(Base): __tablename__ = "crypto_public_key" id = Column(Integer, primary_key=True, index=True) - user_id = Column(Integer, ForeignKey("user.id"), nullable=False, unique=True) + user_id = Column(BigInteger, ForeignKey("users.id"), nullable=False, unique=True) public_key_b64 = Column(Text, nullable=False) @@ -174,7 +174,7 @@ class CryptoBackup(Base): __tablename__ = "crypto_backup" id = Column(Integer, primary_key=True, index=True) - user_id = Column(Integer, ForeignKey("user.id"), nullable=False, unique=True) + user_id = Column(BigInteger, ForeignKey("users.id"), nullable=False, unique=True) blob_json = Column(Text, nullable=False) @@ -182,8 +182,8 @@ class DMEnvelope(Base): __tablename__ = "dm_envelope" id = Column(Integer, primary_key=True, index=True) - sender_id = Column(Integer, ForeignKey("user.id"), nullable=False) - recipient_id = Column(Integer, ForeignKey("user.id"), nullable=False) + sender_id = Column(BigInteger, ForeignKey("users.id"), nullable=False) + recipient_id = Column(BigInteger, ForeignKey("users.id"), nullable=False) iv_b64 = Column(Text, nullable=False) ciphertext_b64 = Column(Text, nullable=False) salt_b64 = Column(Text, nullable=False) @@ -200,8 +200,8 @@ class DMFile(Base): id = Column(Integer, primary_key=True, index=True) message_id = Column(Integer, ForeignKey("dm_envelope.id"), nullable=False, index=True) - sender_id = Column(Integer, ForeignKey("user.id"), nullable=False) - recipient_id = Column(Integer, ForeignKey("user.id"), nullable=False) + sender_id = Column(BigInteger, ForeignKey("users.id"), nullable=False) + recipient_id = Column(BigInteger, ForeignKey("users.id"), nullable=False) name = Column(Text, nullable=False) path = Column(Text, nullable=False) @@ -212,7 +212,7 @@ class FcmToken(Base): __tablename__ = "fcm_token" id = Column(Integer, primary_key=True, index=True) - user_id = Column(Integer, ForeignKey("user.id"), nullable=False, index=True) + user_id = Column(BigInteger, ForeignKey("users.id"), nullable=False, index=True) token = Column(Text, nullable=False, unique=True) created_at = Column(DateTime, default=datetime.now) updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now) @@ -222,8 +222,8 @@ class Reaction(Base): __tablename__ = "reaction" id = Column(Integer, primary_key=True, index=True) - message_id = Column(Integer, ForeignKey("message.id"), nullable=False, index=True) - user_id = Column(Integer, ForeignKey("user.id"), nullable=False) + message_id = Column(BigInteger, ForeignKey("messages.id"), nullable=False, index=True) + user_id = Column(BigInteger, ForeignKey("users.id"), nullable=False) emoji = Column(String(10), nullable=False) # Store emoji as string timestamp = Column(DateTime, default=datetime.now) @@ -239,7 +239,7 @@ class DMReaction(Base): id = Column(Integer, primary_key=True, index=True) dm_envelope_id = Column(Integer, ForeignKey("dm_envelope.id"), nullable=False, index=True) - user_id = Column(Integer, ForeignKey("user.id"), nullable=False) + user_id = Column(BigInteger, ForeignKey("users.id"), nullable=False) emoji = Column(String(10), nullable=False) # Store emoji as string timestamp = Column(DateTime, default=datetime.now) @@ -256,7 +256,7 @@ class DeviceSession(Base): __tablename__ = "device_session" id = Column(Integer, primary_key=True, index=True) - user_id = Column(Integer, ForeignKey("user.id"), nullable=False, index=True) + user_id = Column(BigInteger, ForeignKey("users.id"), nullable=False, index=True) # Raw User-Agent for reference/debugging raw_user_agent = Column(Text, nullable=True) @@ -394,7 +394,7 @@ class UpdateLog(Base): __tablename__ = "update_log" id = Column(Integer, primary_key=True, index=True) - user_id = Column(Integer, ForeignKey("user.id"), nullable=False, index=True) + user_id = Column(BigInteger, ForeignKey("users.id"), nullable=False, index=True) sequence = Column(Integer, nullable=False, index=True) updates = Column(Text, nullable=False) # JSON array of updates timestamp = Column(DateTime, default=datetime.now, index=True) diff --git a/deployment/docker-compose.yml b/deployment/docker-compose.yml index 406a600..c894486 100644 --- a/deployment/docker-compose.yml +++ b/deployment/docker-compose.yml @@ -1,20 +1,20 @@ services: # Database service - db: + database: image: postgres:15 environment: POSTGRES_DB: fromchat POSTGRES_USER: fromchat_admin POSTGRES_PASSWORD: ${DB_PASSWORD:-changeme} volumes: - - postgres_data:/var/lib/postgresql/data + - database:/var/lib/postgresql/data - ./db-init:/docker-entrypoint-initdb.d networks: - fromchat_internal restart: unless-stopped healthcheck: test: ["CMD-SHELL", "pg_isready -U fromchat_admin -d fromchat"] - interval: 10s + interval: 1s timeout: 5s retries: 5 @@ -25,10 +25,10 @@ services: dockerfile: docker/Dockerfile.multi target: migration_runner environment: - DATABASE_URL: postgresql://fromchat_admin:${DB_PASSWORD:-changeme}@db:5432/fromchat + DATABASE_URL: postgresql://fromchat_admin:${DB_PASSWORD:-changeme}@database:5432/fromchat JWT_SECRET: ${JWT_SECRET:-changeme} depends_on: - db: + database: condition: service_healthy networks: - fromchat_internal @@ -57,7 +57,7 @@ services: dockerfile: docker/Dockerfile.multi target: gateway environment: - DATABASE_URL: postgresql://gateway_user:${DB_PASSWORD:-changeme}@db:5432/fromchat + DATABASE_URL: postgresql://gateway_user:${DB_PASSWORD:-changeme}@database:5432/fromchat depends_on: migration_runner: condition: service_completed_successfully @@ -93,7 +93,7 @@ services: dockerfile: docker/Dockerfile.multi target: account_service environment: - DATABASE_URL: postgresql://account_service_user:${DB_PASSWORD:-changeme}@db:5432/fromchat + DATABASE_URL: postgresql://account_service_user:${DB_PASSWORD:-changeme}@database:5432/fromchat JWT_SECRET: ${JWT_SECRET:-changeme} depends_on: migration_runner: @@ -120,7 +120,7 @@ services: dockerfile: docker/Dockerfile.multi target: profile_service environment: - DATABASE_URL: postgresql://profile_service_user:${DB_PASSWORD:-changeme}@db:5432/fromchat + DATABASE_URL: postgresql://profile_service_user:${DB_PASSWORD:-changeme}@database:5432/fromchat FIREBASE_CERT: ${FIREBASE_CERT:-} VAPID_PRIVATE_KEY: ${VAPID_PRIVATE_KEY:-} VAPID_PUBLIC_KEY: ${VAPID_PUBLIC_KEY:-} @@ -150,7 +150,7 @@ services: dockerfile: docker/Dockerfile.multi target: device_service environment: - DATABASE_URL: postgresql://device_service_user:${DB_PASSWORD:-changeme}@db:5432/fromchat + DATABASE_URL: postgresql://device_service_user:${DB_PASSWORD:-changeme}@database:5432/fromchat depends_on: migration_runner: condition: service_completed_successfully @@ -176,7 +176,7 @@ services: dockerfile: docker/Dockerfile.multi target: messaging_service environment: - DATABASE_URL: postgresql://messaging_service_user:${DB_PASSWORD:-changeme}@db:5432/fromchat + DATABASE_URL: postgresql://messaging_service_user:${DB_PASSWORD:-changeme}@database:5432/fromchat FIREBASE_CERT: ${FIREBASE_CERT:-} VAPID_PRIVATE_KEY: ${VAPID_PRIVATE_KEY:-} VAPID_PUBLIC_KEY: ${VAPID_PUBLIC_KEY:-} @@ -209,7 +209,7 @@ services: dockerfile: docker/Dockerfile.multi target: push_service environment: - DATABASE_URL: postgresql://push_service_user:${DB_PASSWORD:-changeme}@db:5432/fromchat + DATABASE_URL: postgresql://push_service_user:${DB_PASSWORD:-changeme}@database:5432/fromchat FIREBASE_CERT: ${FIREBASE_CERT:-} VAPID_PRIVATE_KEY: ${VAPID_PRIVATE_KEY:-} VAPID_PUBLIC_KEY: ${VAPID_PUBLIC_KEY:-} @@ -242,7 +242,7 @@ services: dockerfile: docker/Dockerfile.multi target: webrtc_service environment: - DATABASE_URL: postgresql://webrtc_service_user:${DB_PASSWORD:-changeme}@db:5432/fromchat + DATABASE_URL: postgresql://webrtc_service_user:${DB_PASSWORD:-changeme}@database:5432/fromchat depends_on: migration_runner: condition: service_completed_successfully @@ -268,7 +268,7 @@ services: dockerfile: docker/Dockerfile.multi target: moderation_service environment: - DATABASE_URL: postgresql://moderation_service_user:${DB_PASSWORD:-changeme}@db:5432/fromchat + DATABASE_URL: postgresql://moderation_service_user:${DB_PASSWORD:-changeme}@database:5432/fromchat depends_on: migration_runner: condition: service_completed_successfully @@ -295,24 +295,35 @@ services: # Caddy reverse proxy caddy: - image: caddy:2 + build: + context: ./caddy + dockerfile: Dockerfile ports: - "80:80" - "443:443" volumes: - - ./caddy/Caddyfile:/etc/caddy/Caddyfile:ro - - caddy_data:/data - - caddy_config:/config + - certs:/root/site/certs networks: - fromchat_external + extra_hosts: + - "host.docker.internal:host-gateway" + environment: + - XDG_DATA_HOME=/root/site/certs + - XDG_CONFIG_HOME=/root/site/certs restart: unless-stopped depends_on: - gateway + profiles: ["prod"] volumes: - postgres_data: - caddy_data: - caddy_config: + database: + name: fromchat-database + certs: + name: fromchat-certs + logs: + name: fromchat-logs + data: + name: fromchat-data networks: fromchat_internal: