diff --git a/backend/Dockerfile b/backend/Dockerfile index 6c5e3a3..e0b5705 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -1,15 +1,6 @@ FROM python:3.12-slim -# Force IPv4 and use German mirror with aggressive retry/timeout settings -RUN echo "Acquire::ForceIPv4 \"true\";" > /etc/apt/apt.conf.d/99force-ipv4 && \ - echo "Acquire::Retries \"5\";" > /etc/apt/apt.conf.d/80-retries && \ - echo "Acquire::http::Timeout \"10\";" >> /etc/apt/apt.conf.d/80-retries && \ - echo "Acquire::ftp::Timeout \"10\";" >> /etc/apt/apt.conf.d/80-retries && \ - echo "deb http://ftp.de.debian.org/debian bookworm main" > /etc/apt/sources.list && \ - echo "deb http://ftp.de.debian.org/debian bookworm-updates main" >> /etc/apt/sources.list - -# Install PostgreSQL client for psql (needed for startup.sh) -RUN apt-get update && apt-get install -y postgresql-client && rm -rf /var/lib/apt/lists/* +# No system packages needed - we use Python (psycopg2-binary) for PostgreSQL checks WORKDIR /app diff --git a/backend/db_init.py b/backend/db_init.py new file mode 100644 index 0000000..86481cd --- /dev/null +++ b/backend/db_init.py @@ -0,0 +1,135 @@ +#!/usr/bin/env python3 +""" +Database initialization script for PostgreSQL. +Replaces psql commands in startup.sh with pure Python. +""" +import os +import sys +import time +import psycopg2 +from psycopg2 import OperationalError + +DB_HOST = os.getenv("DB_HOST", "localhost") +DB_PORT = os.getenv("DB_PORT", "5432") +DB_NAME = os.getenv("DB_NAME", "mitai_dev") +DB_USER = os.getenv("DB_USER", "mitai_dev") +DB_PASSWORD = os.getenv("DB_PASSWORD", "") + +def get_connection(): + """Get PostgreSQL connection.""" + return psycopg2.connect( + host=DB_HOST, + port=DB_PORT, + database=DB_NAME, + user=DB_USER, + password=DB_PASSWORD + ) + +def wait_for_postgres(max_retries=30): + """Wait for PostgreSQL to be ready.""" + print("\nChecking PostgreSQL connection...") + for i in range(1, max_retries + 1): + try: + conn = get_connection() + conn.close() + print("✓ PostgreSQL ready") + return True + except OperationalError: + print(f" Waiting for PostgreSQL... (attempt {i}/{max_retries})") + time.sleep(2) + + print(f"✗ PostgreSQL not ready after {max_retries} attempts") + return False + +def check_table_exists(table_name="profiles"): + """Check if a table exists.""" + try: + conn = get_connection() + cur = conn.cursor() + cur.execute(""" + SELECT COUNT(*) + FROM information_schema.tables + WHERE table_schema='public' AND table_name=%s + """, (table_name,)) + count = cur.fetchone()[0] + cur.close() + conn.close() + return count > 0 + except Exception as e: + print(f"Error checking table: {e}") + return False + +def load_schema(schema_file="/app/schema.sql"): + """Load schema from SQL file.""" + try: + with open(schema_file, 'r') as f: + schema_sql = f.read() + + conn = get_connection() + cur = conn.cursor() + cur.execute(schema_sql) + conn.commit() + cur.close() + conn.close() + print("✓ Schema loaded from schema.sql") + return True + except Exception as e: + print(f"✗ Error loading schema: {e}") + return False + +def get_profile_count(): + """Get number of profiles in database.""" + try: + conn = get_connection() + cur = conn.cursor() + cur.execute("SELECT COUNT(*) FROM profiles") + count = cur.fetchone()[0] + cur.close() + conn.close() + return count + except Exception as e: + print(f"Error getting profile count: {e}") + return -1 + +if __name__ == "__main__": + print("═══════════════════════════════════════════════════════════") + print("MITAI JINKENDO - Database Initialization (v9b)") + print("═══════════════════════════════════════════════════════════") + + # Wait for PostgreSQL + if not wait_for_postgres(): + sys.exit(1) + + # Check schema + print("\nChecking database schema...") + if not check_table_exists("profiles"): + print(" Schema not found, initializing...") + if not load_schema(): + sys.exit(1) + else: + print("✓ Schema already exists") + + # Check for migration + print("\nChecking for SQLite data migration...") + sqlite_db = "/app/data/bodytrack.db" + profile_count = get_profile_count() + + if os.path.exists(sqlite_db) and profile_count == 0: + print(" SQLite database found and PostgreSQL is empty") + print(" Starting automatic migration...") + # Import and run migration + try: + import migrate_to_postgres + print("✓ Migration completed") + except Exception as e: + print(f"✗ Migration failed: {e}") + sys.exit(1) + elif os.path.exists(sqlite_db) and profile_count > 0: + print(f"⚠ SQLite DB exists but PostgreSQL already has {profile_count} profiles") + print(" Skipping migration (already migrated)") + elif not os.path.exists(sqlite_db): + print("✓ No SQLite database found (fresh install or already migrated)") + else: + print("✓ No migration needed") + + print("\n✓ Database initialization complete") diff --git a/backend/startup.sh b/backend/startup.sh index 54bd626..5ee2bb4 100644 --- a/backend/startup.sh +++ b/backend/startup.sh @@ -1,66 +1,12 @@ #!/bin/bash set -e -echo "═══════════════════════════════════════════════════════════" -echo "MITAI JINKENDO - Backend Startup (v9b)" -echo "═══════════════════════════════════════════════════════════" +# Run database initialization with Python (no psql needed!) +python /app/db_init.py -# ── PostgreSQL Connection Check ─────────────────────────────── -echo "" -echo "Checking PostgreSQL connection..." - -MAX_RETRIES=30 -RETRY_COUNT=0 - -until PGPASSWORD=$DB_PASSWORD psql -h "$DB_HOST" -U "$DB_USER" -d "$DB_NAME" -c '\q' 2>/dev/null; do - RETRY_COUNT=$((RETRY_COUNT + 1)) - if [ $RETRY_COUNT -ge $MAX_RETRIES ]; then - echo "✗ PostgreSQL not ready after ${MAX_RETRIES} attempts" - echo " Exiting..." - exit 1 - fi - echo " Waiting for PostgreSQL... (attempt $RETRY_COUNT/$MAX_RETRIES)" - sleep 2 -done - -echo "✓ PostgreSQL ready" - -# ── Schema Initialization ────────────────────────────────────── -echo "" -echo "Checking database schema..." - -# Check if profiles table exists -TABLE_EXISTS=$(PGPASSWORD=$DB_PASSWORD psql -h "$DB_HOST" -U "$DB_USER" -d "$DB_NAME" -tAc \ - "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='public' AND table_name='profiles'") - -if [ "$TABLE_EXISTS" = "0" ]; then - echo " Schema not found, initializing..." - PGPASSWORD=$DB_PASSWORD psql -h "$DB_HOST" -U "$DB_USER" -d "$DB_NAME" -f /app/schema.sql - echo "✓ Schema loaded from schema.sql" -else - echo "✓ Schema already exists" -fi - -# ── Auto-Migration (SQLite → PostgreSQL) ─────────────────────── -echo "" -echo "Checking for SQLite data migration..." - -SQLITE_DB="/app/data/bodytrack.db" -PROFILE_COUNT=$(PGPASSWORD=$DB_PASSWORD psql -h "$DB_HOST" -U "$DB_USER" -d "$DB_NAME" -tAc \ - "SELECT COUNT(*) FROM profiles") - -if [ -f "$SQLITE_DB" ] && [ "$PROFILE_COUNT" = "0" ]; then - echo " SQLite database found and PostgreSQL is empty" - echo " Starting automatic migration..." - python /app/migrate_to_postgres.py - echo "✓ Migration completed" -elif [ -f "$SQLITE_DB" ] && [ "$PROFILE_COUNT" != "0" ]; then - echo "⚠ SQLite DB exists but PostgreSQL already has $PROFILE_COUNT profiles" - echo " Skipping migration (already migrated)" -elif [ ! -f "$SQLITE_DB" ]; then - echo "✓ No SQLite database found (fresh install or already migrated)" -else - echo "✓ No migration needed" +if [ $? -ne 0 ]; then + echo "✗ Database initialization failed" + exit 1 fi # ── Start Application ──────────────────────────────────────────