#!/bin/bash # Webapp entrypoint script - restores from S3 and runs database migrations before starting Gunicorn set -e echo "[ENTRYPOINT] Checking for S3 restore..." # Restore database from S3 if enabled (Feature 015) python3 <<'RESTORE_PYEOF' import os import sys # Add src directory to Python path sys.path.insert(0, '/app') try: from src.services.s3_restore import restore_on_startup result = restore_on_startup() print(f"[S3_RESTORE] Result: {result.value}") except Exception as e: print(f"[S3_RESTORE] Failed (non-fatal): {e}") # Continue startup even if restore fails RESTORE_PYEOF echo "[ENTRYPOINT] Running database migrations..." # Run Python migration script python3 <<'PYEOF' import sqlite3 import os DB_PATH = os.environ.get('DATABASE_PATH', '/app/data/contacts.db') MIGRATIONS_DIR = '/app/migrations' print(f"[MIGRATION] Connecting to database: {DB_PATH}") conn = sqlite3.connect(DB_PATH) cursor = conn.cursor() # Step 1: Check if contact_sessions table exists cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='contact_sessions'") table_exists = cursor.fetchone() is not None if not table_exists: print("[MIGRATION] Initial database - creating tables from 001_create_tables.sql") try: with open(f'{MIGRATIONS_DIR}/001_create_tables.sql', 'r') as f: schema_sql = f.read() cursor.executescript(schema_sql) conn.commit() print(" ✓ Created user_profiles table") print(" ✓ Created contact_sessions table") print(" ✓ Created indexes") except Exception as e: print(f" ⚠ Failed to create initial schema: {e}") conn.rollback() raise # Step 2: Check if normalized_name column exists (producer columns migration) cursor.execute("PRAGMA table_info(contact_sessions)") columns = [row[1] for row in cursor.fetchall()] # Step 2a: Add contact_id column if missing (v2 API alignment) if 'contact_id' not in columns: print("[MIGRATION] Applying migration: Add contact_id column") try: cursor.execute("ALTER TABLE contact_sessions ADD COLUMN contact_id VARCHAR(255) NOT NULL DEFAULT ''") print(" ✓ Added contact_id column") except Exception as e: print(f" ⚠ contact_id: {e}") try: cursor.execute("UPDATE contact_sessions SET contact_id = session_id WHERE contact_id = ''") print(f" ✓ Backfilled {cursor.rowcount} existing contact_id values") except Exception as e: print(f" ⚠ backfill contact_id: {e}") try: cursor.execute("CREATE INDEX IF NOT EXISTS idx_contact_sessions_contact_id ON contact_sessions(contact_id)") print(" ✓ Created index idx_contact_sessions_contact_id") except Exception as e: print(f" ⚠ index: {e}") conn.commit() print("[MIGRATION] contact_id migration complete") # Refresh columns list after contact_id migration cursor.execute("PRAGMA table_info(contact_sessions)") columns = [row[1] for row in cursor.fetchall()] if 'normalized_name' not in columns: print("[MIGRATION] Applying migration: Add producer-related columns") try: cursor.execute("ALTER TABLE contact_sessions ADD COLUMN normalized_name TEXT") print(" ✓ Added normalized_name column") except Exception as e: print(f" ⚠ normalized_name: {e}") try: cursor.execute("ALTER TABLE contact_sessions ADD COLUMN sequence_number INTEGER DEFAULT 1") print(" ✓ Added sequence_number column") except Exception as e: print(f" ⚠ sequence_number: {e}") try: cursor.execute("ALTER TABLE contact_sessions ADD COLUMN producer_id TEXT") print(" ✓ Added producer_id column") except Exception as e: print(f" ⚠ producer_id: {e}") try: cursor.execute("CREATE INDEX IF NOT EXISTS idx_contact_sessions_producer ON contact_sessions(user_id, normalized_name)") print(" ✓ Created index idx_contact_sessions_producer") except Exception as e: print(f" ⚠ index: {e}") try: cursor.execute(""" UPDATE contact_sessions SET normalized_name = LOWER(TRIM(contact_name)), sequence_number = 1, producer_id = user_id || '_' || LOWER(TRIM(contact_name)) || '_1' WHERE normalized_name IS NULL """) print(f" ✓ Backfilled {cursor.rowcount} existing rows") except Exception as e: print(f" ⚠ backfill: {e}") conn.commit() print("[MIGRATION] Migration complete") else: print("[MIGRATION] Schema already up-to-date (normalized_name column exists)") conn.close() PYEOF echo "[ENTRYPOINT] Starting Gunicorn..." # Start Gunicorn with the provided arguments exec gunicorn -w 1 -b 0.0.0.0:7860 --timeout 120 --graceful-timeout 120 src.app:app