Fix migrations

This commit is contained in:
2026-01-07 14:03:02 +03:00
Unverified
parent 75ef3fc604
commit ef66025dba
3 changed files with 52 additions and 39 deletions
+8 -6
View File
@@ -19,6 +19,9 @@ def run_migrations():
This function will upgrade the database to the latest migration. This function will upgrade the database to the latest migration.
Fully automated - handles all scenarios automatically. Fully automated - handles all scenarios automatically.
""" """
# Get the directory where this script is located
current_dir = os.path.dirname(os.path.abspath(__file__))
try: try:
# FIRST: Check if database has any application tables (excluding alembic_version) # FIRST: Check if database has any application tables (excluding alembic_version)
engine = create_engine(DATABASE_URL) engine = create_engine(DATABASE_URL)
@@ -35,9 +38,6 @@ def run_migrations():
Base.metadata.create_all(bind=engine) Base.metadata.create_all(bind=engine)
logger.info("All tables created successfully from models.") 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 # Note: Problematic migrations are now cleaned up during Docker build
# Create Alembic configuration # Create Alembic configuration
@@ -487,7 +487,9 @@ def _create_database_directly():
from backend.shared.models import Base from backend.shared.models import Base
from backend.shared.db import get_engine from backend.shared.db import get_engine
from sqlalchemy import text, inspect from sqlalchemy import text, inspect
engine = get_engine()
# Check existing tables and update schema # Check existing tables and update schema
with engine.connect() as connection: with engine.connect() as connection:
inspector = inspect(connection) inspector = inspect(connection)
@@ -569,9 +571,9 @@ def _create_database_directly():
revision_match = re.search(r"revision: str = '([^']+)'", content) revision_match = re.search(r"revision: str = '([^']+)'", content)
if revision_match: if revision_match:
revision_id = revision_match.group(1) 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: 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: else:
connection.execute(text("INSERT OR IGNORE INTO alembic_version (version_num) VALUES ('direct_creation')")) connection.execute(text("INSERT OR IGNORE INTO alembic_version (version_num) VALUES ('direct_creation')"))
+13 -13
View File
@@ -155,7 +155,7 @@ class MessageFile(Base):
__tablename__ = "message_file" __tablename__ = "message_file"
id = Column(Integer, primary_key=True, index=True) 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) path = Column(Text, nullable=False)
name = Column(Text, nullable=False) name = Column(Text, nullable=False)
@@ -166,7 +166,7 @@ class CryptoPublicKey(Base):
__tablename__ = "crypto_public_key" __tablename__ = "crypto_public_key"
id = Column(Integer, primary_key=True, index=True) 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) public_key_b64 = Column(Text, nullable=False)
@@ -174,7 +174,7 @@ class CryptoBackup(Base):
__tablename__ = "crypto_backup" __tablename__ = "crypto_backup"
id = Column(Integer, primary_key=True, index=True) 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) blob_json = Column(Text, nullable=False)
@@ -182,8 +182,8 @@ class DMEnvelope(Base):
__tablename__ = "dm_envelope" __tablename__ = "dm_envelope"
id = Column(Integer, primary_key=True, index=True) id = Column(Integer, primary_key=True, index=True)
sender_id = Column(Integer, ForeignKey("user.id"), nullable=False) sender_id = Column(BigInteger, ForeignKey("users.id"), nullable=False)
recipient_id = Column(Integer, ForeignKey("user.id"), nullable=False) recipient_id = Column(BigInteger, ForeignKey("users.id"), nullable=False)
iv_b64 = Column(Text, nullable=False) iv_b64 = Column(Text, nullable=False)
ciphertext_b64 = Column(Text, nullable=False) ciphertext_b64 = Column(Text, nullable=False)
salt_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) id = Column(Integer, primary_key=True, index=True)
message_id = Column(Integer, ForeignKey("dm_envelope.id"), nullable=False, index=True) message_id = Column(Integer, ForeignKey("dm_envelope.id"), nullable=False, index=True)
sender_id = Column(Integer, ForeignKey("user.id"), nullable=False) sender_id = Column(BigInteger, ForeignKey("users.id"), nullable=False)
recipient_id = Column(Integer, ForeignKey("user.id"), nullable=False) recipient_id = Column(BigInteger, ForeignKey("users.id"), nullable=False)
name = Column(Text, nullable=False) name = Column(Text, nullable=False)
path = Column(Text, nullable=False) path = Column(Text, nullable=False)
@@ -212,7 +212,7 @@ class FcmToken(Base):
__tablename__ = "fcm_token" __tablename__ = "fcm_token"
id = Column(Integer, primary_key=True, index=True) 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) token = Column(Text, nullable=False, unique=True)
created_at = Column(DateTime, default=datetime.now) created_at = Column(DateTime, default=datetime.now)
updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now) updated_at = Column(DateTime, default=datetime.now, onupdate=datetime.now)
@@ -222,8 +222,8 @@ class Reaction(Base):
__tablename__ = "reaction" __tablename__ = "reaction"
id = Column(Integer, primary_key=True, index=True) 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)
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 emoji = Column(String(10), nullable=False) # Store emoji as string
timestamp = Column(DateTime, default=datetime.now) timestamp = Column(DateTime, default=datetime.now)
@@ -239,7 +239,7 @@ class DMReaction(Base):
id = Column(Integer, primary_key=True, index=True) id = Column(Integer, primary_key=True, index=True)
dm_envelope_id = Column(Integer, ForeignKey("dm_envelope.id"), nullable=False, 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 emoji = Column(String(10), nullable=False) # Store emoji as string
timestamp = Column(DateTime, default=datetime.now) timestamp = Column(DateTime, default=datetime.now)
@@ -256,7 +256,7 @@ class DeviceSession(Base):
__tablename__ = "device_session" __tablename__ = "device_session"
id = Column(Integer, primary_key=True, index=True) 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 for reference/debugging
raw_user_agent = Column(Text, nullable=True) raw_user_agent = Column(Text, nullable=True)
@@ -394,7 +394,7 @@ class UpdateLog(Base):
__tablename__ = "update_log" __tablename__ = "update_log"
id = Column(Integer, primary_key=True, index=True) 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) sequence = Column(Integer, nullable=False, index=True)
updates = Column(Text, nullable=False) # JSON array of updates updates = Column(Text, nullable=False) # JSON array of updates
timestamp = Column(DateTime, default=datetime.now, index=True) timestamp = Column(DateTime, default=datetime.now, index=True)
+31 -20
View File
@@ -1,20 +1,20 @@
services: services:
# Database service # Database service
db: database:
image: postgres:15 image: postgres:15
environment: environment:
POSTGRES_DB: fromchat POSTGRES_DB: fromchat
POSTGRES_USER: fromchat_admin POSTGRES_USER: fromchat_admin
POSTGRES_PASSWORD: ${DB_PASSWORD:-changeme} POSTGRES_PASSWORD: ${DB_PASSWORD:-changeme}
volumes: volumes:
- postgres_data:/var/lib/postgresql/data - database:/var/lib/postgresql/data
- ./db-init:/docker-entrypoint-initdb.d - ./db-init:/docker-entrypoint-initdb.d
networks: networks:
- fromchat_internal - fromchat_internal
restart: unless-stopped restart: unless-stopped
healthcheck: healthcheck:
test: ["CMD-SHELL", "pg_isready -U fromchat_admin -d fromchat"] test: ["CMD-SHELL", "pg_isready -U fromchat_admin -d fromchat"]
interval: 10s interval: 1s
timeout: 5s timeout: 5s
retries: 5 retries: 5
@@ -25,10 +25,10 @@ services:
dockerfile: docker/Dockerfile.multi dockerfile: docker/Dockerfile.multi
target: migration_runner target: migration_runner
environment: 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} JWT_SECRET: ${JWT_SECRET:-changeme}
depends_on: depends_on:
db: database:
condition: service_healthy condition: service_healthy
networks: networks:
- fromchat_internal - fromchat_internal
@@ -57,7 +57,7 @@ services:
dockerfile: docker/Dockerfile.multi dockerfile: docker/Dockerfile.multi
target: gateway target: gateway
environment: 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: depends_on:
migration_runner: migration_runner:
condition: service_completed_successfully condition: service_completed_successfully
@@ -93,7 +93,7 @@ services:
dockerfile: docker/Dockerfile.multi dockerfile: docker/Dockerfile.multi
target: account_service target: account_service
environment: 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} JWT_SECRET: ${JWT_SECRET:-changeme}
depends_on: depends_on:
migration_runner: migration_runner:
@@ -120,7 +120,7 @@ services:
dockerfile: docker/Dockerfile.multi dockerfile: docker/Dockerfile.multi
target: profile_service target: profile_service
environment: 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:-} FIREBASE_CERT: ${FIREBASE_CERT:-}
VAPID_PRIVATE_KEY: ${VAPID_PRIVATE_KEY:-} VAPID_PRIVATE_KEY: ${VAPID_PRIVATE_KEY:-}
VAPID_PUBLIC_KEY: ${VAPID_PUBLIC_KEY:-} VAPID_PUBLIC_KEY: ${VAPID_PUBLIC_KEY:-}
@@ -150,7 +150,7 @@ services:
dockerfile: docker/Dockerfile.multi dockerfile: docker/Dockerfile.multi
target: device_service target: device_service
environment: 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: depends_on:
migration_runner: migration_runner:
condition: service_completed_successfully condition: service_completed_successfully
@@ -176,7 +176,7 @@ services:
dockerfile: docker/Dockerfile.multi dockerfile: docker/Dockerfile.multi
target: messaging_service target: messaging_service
environment: 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:-} FIREBASE_CERT: ${FIREBASE_CERT:-}
VAPID_PRIVATE_KEY: ${VAPID_PRIVATE_KEY:-} VAPID_PRIVATE_KEY: ${VAPID_PRIVATE_KEY:-}
VAPID_PUBLIC_KEY: ${VAPID_PUBLIC_KEY:-} VAPID_PUBLIC_KEY: ${VAPID_PUBLIC_KEY:-}
@@ -209,7 +209,7 @@ services:
dockerfile: docker/Dockerfile.multi dockerfile: docker/Dockerfile.multi
target: push_service target: push_service
environment: 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:-} FIREBASE_CERT: ${FIREBASE_CERT:-}
VAPID_PRIVATE_KEY: ${VAPID_PRIVATE_KEY:-} VAPID_PRIVATE_KEY: ${VAPID_PRIVATE_KEY:-}
VAPID_PUBLIC_KEY: ${VAPID_PUBLIC_KEY:-} VAPID_PUBLIC_KEY: ${VAPID_PUBLIC_KEY:-}
@@ -242,7 +242,7 @@ services:
dockerfile: docker/Dockerfile.multi dockerfile: docker/Dockerfile.multi
target: webrtc_service target: webrtc_service
environment: 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: depends_on:
migration_runner: migration_runner:
condition: service_completed_successfully condition: service_completed_successfully
@@ -268,7 +268,7 @@ services:
dockerfile: docker/Dockerfile.multi dockerfile: docker/Dockerfile.multi
target: moderation_service target: moderation_service
environment: 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: depends_on:
migration_runner: migration_runner:
condition: service_completed_successfully condition: service_completed_successfully
@@ -295,24 +295,35 @@ services:
# Caddy reverse proxy # Caddy reverse proxy
caddy: caddy:
image: caddy:2 build:
context: ./caddy
dockerfile: Dockerfile
ports: ports:
- "80:80" - "80:80"
- "443:443" - "443:443"
volumes: volumes:
- ./caddy/Caddyfile:/etc/caddy/Caddyfile:ro - certs:/root/site/certs
- caddy_data:/data
- caddy_config:/config
networks: networks:
- fromchat_external - 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 restart: unless-stopped
depends_on: depends_on:
- gateway - gateway
profiles: ["prod"]
volumes: volumes:
postgres_data: database:
caddy_data: name: fromchat-database
caddy_config: certs:
name: fromchat-certs
logs:
name: fromchat-logs
data:
name: fromchat-data
networks: networks:
fromchat_internal: fromchat_internal: