From e94fcded2ac6f0a827ed367e44d7eafe995d59c2 Mon Sep 17 00:00:00 2001 From: WhiteX Date: Thu, 12 Jun 2025 21:42:04 +0300 Subject: [PATCH] Implement comprehensive deployment isolation fixes and verification tools --- DATABASE_ISOLATION_FIX.md | 155 +++++++++++++++++++++++ Dockerfile_coolify | 37 +++++- ISOLATION_IMPLEMENTATION_CHECKLIST.md | 0 apps/api/src/routers/debug.py | 64 +++++++++- deploy-isolation-fix.sh | 0 verify-db-isolation.sh | 171 ++++++++++++++++++++++++++ verify-isolation.sh | 155 +++++++++++++++++------ 7 files changed, 534 insertions(+), 48 deletions(-) create mode 100644 DATABASE_ISOLATION_FIX.md create mode 100644 ISOLATION_IMPLEMENTATION_CHECKLIST.md create mode 100755 deploy-isolation-fix.sh create mode 100755 verify-db-isolation.sh diff --git a/DATABASE_ISOLATION_FIX.md b/DATABASE_ISOLATION_FIX.md new file mode 100644 index 00000000..5796c166 --- /dev/null +++ b/DATABASE_ISOLATION_FIX.md @@ -0,0 +1,155 @@ +# Database Isolation Fix for LearnHouse Deployments + +## Issues Identified: Multiple Sources of Cross-Deployment Contamination + +We have identified several issues causing cross-deployment contamination between DEV (adr-lms.whitex.cloud) and LIVE (edu.adradviser.ro) environments: + +### 1. Shared Database Connection + +Both deployments are connecting to the same database server, causing data sharing: + +- DEV: `postgresql://learnhouse_dev:YOUR_DEV_DB_PASSWORD@db:5432/learnhouse_dev` +- LIVE: `postgresql://learnhouse:YOUR_LIVE_DB_PASSWORD@db:5432/learnhouse` + +The container networking is not isolating these properly, causing both deployments to resolve `db` to the same physical database server. + +### 2. Hardcoded URLs in Frontend Bundle + +The DEV deployment contains hardcoded URLs pointing to the LIVE site: +- `http://edu.adradviser.ro/collections/new` +- `http://edu.adradviser.ro/courses?new=true` + +These URLs are embedded in the JavaScript bundles at build time and aren't being properly updated at runtime. + +### 3. Container API Access Inconsistencies + +The API is accessed differently from inside containers versus from outside: +- Inside container: via `http://localhost:9000` +- External access: via domain's `/api/v1` path + +This inconsistency complicates URL patching and can cause references to the wrong domain. + +## How to Fix Deployment Isolation + +### 1. Database Connection Isolation + +Each deployment must use its own fully-qualified database hostname: + +1. For DEV deployment: +``` +LEARNHOUSE_SQL_CONNECTION_STRING=postgresql://learnhouse_dev:YOUR_DEV_DB_PASSWORD@db-dev.${DEPLOYMENT_NAME}-network:5432/learnhouse_dev +``` + +2. For LIVE deployment: +``` +LEARNHOUSE_SQL_CONNECTION_STRING=postgresql://learnhouse:YOUR_LIVE_DB_PASSWORD@db-live.${DEPLOYMENT_NAME}-network:5432/learnhouse +``` + +This ensures: +- Each deployment uses a uniquely named database server +- The server hostname includes the deployment name for clarity +- The network name is deployment-specific + +### 2. Enhanced URL Patching + +We've improved the URL patching in Dockerfile_coolify to handle multiple URL formats: + +```bash +# Replace all occurrences of edu.adradviser.ro with the current domain +find /app/web/.next -type f -name "*.js" -exec sed -i "s|http://edu.adradviser.ro|$NEXT_PUBLIC_LEARNHOUSE_DOMAIN|g" {} \; +find /app/web/.next -type f -name "*.js" -exec sed -i "s|https://edu.adradviser.ro|$NEXT_PUBLIC_LEARNHOUSE_DOMAIN|g" {} \; +find /app/web/.next -type f -name "*.js" -exec sed -i "s|//edu.adradviser.ro|//$DOMAIN_ONLY|g" {} \; +find /app/web/.next -type f -name "*.js" -exec sed -i "s|\"href\":\"http://edu.adradviser.ro|\"href\":\"$NEXT_PUBLIC_LEARNHOUSE_DOMAIN|g" {} \; +``` + +### 3. Cookie Domain Isolation + +Ensure each deployment uses its own cookie domain: + +``` +LEARNHOUSE_COOKIE_DOMAIN=adr-lms.whitex.cloud # For DEV +LEARNHOUSE_COOKIE_DOMAIN=edu.adradviser.ro # For LIVE +``` + +### 4. API URL Inside vs Outside Container + +Address the inconsistency in how the API is accessed: + +1. Inside container: `http://localhost:9000` +2. External access: via domain's `/api/v1` path + +Solution: +- Added the debug endpoint at `/api/v1/debug` to ensure it's accessible externally +- Enhanced the URL patching to handle both internal and external URL formats + +### 5. API Debug Endpoints + +Added comprehensive debug endpoints: + +1. `/api/v1/debug/deployment` - Shows deployment configuration details +2. `/api/v1/debug/urls` - Scans for any remaining hardcoded URLs in the frontend bundle + +## Verification Process + +After implementing these fixes, use this verification process: + +1. **Deploy the API changes** to add the debug endpoints: + ```bash + git add apps/api/src/routers/debug.py apps/api/src/router.py apps/api/app.py + git commit -m "Add isolation debug endpoints for deployment verification" + git push + # Deploy through your CI/CD process + ``` + +2. **Verify database isolation** by accessing the debug endpoints: + ```bash + # Check DEV deployment + curl -k https://adr-lms.whitex.cloud/api/v1/debug/deployment + + # Check LIVE deployment + curl -k https://edu.adradviser.ro/api/v1/debug/deployment + ``` + +3. **Check for hardcoded URLs** in the frontend bundle: + ```bash + # Check DEV deployment + curl -k https://adr-lms.whitex.cloud/api/v1/debug/urls + + # Check LIVE deployment + curl -k https://edu.adradviser.ro/api/v1/debug/urls + ``` + +4. **Test with incognito browsers** to ensure sessions don't cross-contaminate + +5. **Run the verification script** to perform all checks automatically: + ```bash + ./verify-isolation.sh + ``` + +Solution: +- Added the debug endpoint at `/api/v1/debug` to ensure it's accessible externally +- Enhanced the URL patching to handle both internal and external URL formats + +### 5. API Debug Endpoints + +Added comprehensive debug endpoints: + +1. `/api/v1/debug/deployment` - Shows deployment configuration details +2. `/api/v1/debug/urls` - Scans for any remaining hardcoded URLs in the frontend bundle + +4. **Network Isolation**: Updated docker-compose-coolify.yml to use deployment-specific networks. + +## Verification Steps + +After implementing these changes: + +1. Run `./verify-isolation.sh` to check deployment isolation +2. Verify each deployment has its own database connection using `/api/v1/debug/deployment` endpoint +3. Check that no cross-domain references appear in the frontend + +## Implementation Plan + +1. Update database connection strings in Coolify environment variables for both deployments +2. Rebuild and redeploy both environments to apply the changes +3. Run the verification script to confirm isolation +4. Clear browser cookies and caches to test with clean state diff --git a/Dockerfile_coolify b/Dockerfile_coolify index 868b157e..08eb7db2 100644 --- a/Dockerfile_coolify +++ b/Dockerfile_coolify @@ -62,19 +62,46 @@ COPY ./extra/start.sh /app/start.sh COPY ./debug-services.sh /app/debug-services.sh RUN chmod +x /app/start.sh /app/debug-services.sh -# Add near the end of your Dockerfile_coolify +# Add enhanced URL and domain patching RUN echo '#!/bin/bash\n\ -echo "Enhanced patching of NextAuth cookies..."\n\ +echo "Enhanced patching of NextAuth cookies and domains..."\n\ find /app/web/.next -type f -name "*.js" -exec sed -i "s/domain:[^,}]*,/domain: undefined,/g" {} \\;\n\ find /app/web/.next -type f -name "*.js" -exec sed -i "s/domain: *process.env.LEARNHOUSE_COOKIE_DOMAIN/domain: undefined/g" {} \\;\n\ find /app/web/.next -type f -name "*.js" -exec sed -i "s/\.domain\s*=\s*[^;]*;/\.domain = undefined;/g" {} \\;\n\ -echo "Patch complete."\n\ +echo "Cookie domain patches complete."\n\ +\n\ echo "Patching API URLs for deployment isolation..."\n\ -if [ ! -z "$NEXT_PUBLIC_LEARNHOUSE_API_URL" ]; then\n\ +if [ ! -z "$NEXT_PUBLIC_LEARNHOUSE_DOMAIN" ]; then\n\ + echo "Domain value found: $NEXT_PUBLIC_LEARNHOUSE_DOMAIN"\n\ + # Extract the domain without protocol\n\ + DOMAIN_ONLY=$(echo "$NEXT_PUBLIC_LEARNHOUSE_DOMAIN" | sed -E "s/^https?:\\/\\///g")\n\ + echo "Extracted domain: $DOMAIN_ONLY"\n\ + \n\ + # Replace all occurrences of edu.adradviser.ro with the current domain\n\ + find /app/web/.next -type f -name "*.js" -exec sed -i "s|http://edu.adradviser.ro|$NEXT_PUBLIC_LEARNHOUSE_DOMAIN|g" {} \\;\n\ + find /app/web/.next -type f -name "*.js" -exec sed -i "s|https://edu.adradviser.ro|$NEXT_PUBLIC_LEARNHOUSE_DOMAIN|g" {} \\;\n\ + find /app/web/.next -type f -name "*.js" -exec sed -i "s|//edu.adradviser.ro|//$DOMAIN_ONLY|g" {} \\;\n\ + find /app/web/.next -type f -name "*.js" -exec sed -i "s|\"href\":\"http://edu.adradviser.ro|\"href\":\"$NEXT_PUBLIC_LEARNHOUSE_DOMAIN|g" {} \\;\n\ + find /app/web/.next -type f -name "*.js" -exec sed -i "s|https://edu.adradviser.ro|$NEXT_PUBLIC_LEARNHOUSE_DOMAIN|g" {} \\;\n\ + find /app/web/.next -type f -name "*.html" -exec sed -i "s|http://edu.adradviser.ro|$NEXT_PUBLIC_LEARNHOUSE_DOMAIN|g" {} \\;\n\ + find /app/web/.next -type f -name "*.html" -exec sed -i "s|https://edu.adradviser.ro|$NEXT_PUBLIC_LEARNHOUSE_DOMAIN|g" {} \\;\n\ + \n\ + # Replace absolute URL paths\n\ find /app/web/.next -type f -name "*.js" -exec sed -i "s|https://[^/\"]*\\(/api/v1/\\)|${NEXT_PUBLIC_LEARNHOUSE_API_URL}|g" {} \\;\n\ find /app/web/.next -type f -name "*.js" -exec sed -i "s|https://[^/\"]*\\(/api/auth\\)|${NEXT_PUBLIC_LEARNHOUSE_BACKEND_URL}api/auth|g" {} \\;\n\ - echo "API URLs patched to: $NEXT_PUBLIC_LEARNHOUSE_API_URL"\n\ + \n\ + # Replace hardcoded href values\n\ + find /app/web/.next -type f -name "*.js" -exec sed -i "s|href:\\\"http://edu.adradviser.ro|href:\\\"$NEXT_PUBLIC_LEARNHOUSE_DOMAIN|g" {} \\;\n\ + find /app/web/.next -type f -name "*.js" -exec sed -i "s|href:\\\"https://edu.adradviser.ro|href:\\\"$NEXT_PUBLIC_LEARNHOUSE_DOMAIN|g" {} \\;\n\ + \n\ + echo "URL and domain patching completed using: $NEXT_PUBLIC_LEARNHOUSE_DOMAIN"\n\ + \n\ + # Verify the changes\n\ + echo "Verification of patched files:"\n\ + grep -r "edu.adradviser.ro" /app/web/.next --include="*.js" | head -5 || echo "No references to edu.adradviser.ro found (good)"\n\ fi\n\ +\n\ +echo "Starting application..."\n\ sh /app/start.sh' > /app/patched-start.sh && chmod +x /app/patched-start.sh # Use the patched start script diff --git a/ISOLATION_IMPLEMENTATION_CHECKLIST.md b/ISOLATION_IMPLEMENTATION_CHECKLIST.md new file mode 100644 index 00000000..e69de29b diff --git a/apps/api/src/routers/debug.py b/apps/api/src/routers/debug.py index 120b9e59..81377879 100644 --- a/apps/api/src/routers/debug.py +++ b/apps/api/src/routers/debug.py @@ -1,5 +1,6 @@ import os -from fastapi import APIRouter +import subprocess +from fastapi import APIRouter, Request from config.config import get_learnhouse_config router = APIRouter() @@ -9,10 +10,67 @@ async def debug_deployment(): """Debug endpoint for deployment verification and isolation testing""" learnhouse_config = get_learnhouse_config() + # Parse database host safely + db_host = "unknown" + if '@' in learnhouse_config.database_config.sql_connection_string: + try: + parts = learnhouse_config.database_config.sql_connection_string.split('@') + if len(parts) > 1: + host_parts = parts[1].split('/') + if len(host_parts) > 0: + db_host = host_parts[0] + except Exception: + pass + + # Parse redis host safely + redis_host = "unknown" + if '@' in learnhouse_config.redis_config.redis_connection_string: + try: + parts = learnhouse_config.redis_config.redis_connection_string.split('@') + if len(parts) > 1: + host_parts = parts[1].split(':') + if len(host_parts) > 0: + redis_host = host_parts[0] + except Exception: + pass + return { "deployment_name": os.environ.get('DEPLOYMENT_NAME', 'NOT_SET'), "cookie_domain": learnhouse_config.hosting_config.cookie_config.domain, "api_domain": learnhouse_config.hosting_config.domain, - "database_host": learnhouse_config.database_config.sql_connection_string.split('@')[1].split('/')[0] if '@' in learnhouse_config.database_config.sql_connection_string else "unknown", - "redis_host": learnhouse_config.redis_config.redis_connection_string.split('@')[1].split(':')[0] if '@' in learnhouse_config.redis_config.redis_connection_string else "unknown" + "database_host": db_host, + "redis_host": redis_host, + "database_name": learnhouse_config.database_config.sql_connection_string.split('/')[-1] if '/' in learnhouse_config.database_config.sql_connection_string else "unknown", + "env_vars": { + "NEXT_PUBLIC_LEARNHOUSE_DOMAIN": os.environ.get('NEXT_PUBLIC_LEARNHOUSE_DOMAIN', 'NOT_SET'), + "NEXT_PUBLIC_LEARNHOUSE_API_URL": os.environ.get('NEXT_PUBLIC_LEARNHOUSE_API_URL', 'NOT_SET') + } } + +@router.get("/urls") +async def debug_urls(request: Request): + """Debug endpoint to detect hardcoded URLs in NextJS bundle""" + try: + # This only works if Next.js files are accessible from the API container + result = subprocess.run( + ["find", "/app/web/.next", "-type", "f", "-name", "*.js", "-exec", "grep", "-o", "http://edu.adradviser.ro[^\"']*", "{}", ";"], + capture_output=True, text=True, timeout=5 + ) + hardcoded_urls = list(set(result.stdout.strip().split('\n'))) if result.stdout.strip() else [] + + # Get the current domain for comparison + current_domain = os.environ.get('NEXT_PUBLIC_LEARNHOUSE_DOMAIN', 'unknown') + + return { + "detected_hardcoded_urls": hardcoded_urls, + "current_domain": current_domain, + "client_host": request.client.host, + "headers": dict(request.headers), + "deployment_name": os.environ.get('DEPLOYMENT_NAME', 'NOT_SET') + } + except Exception as e: + return { + "error": str(e), + "message": "Could not scan for hardcoded URLs", + "deployment_name": os.environ.get('DEPLOYMENT_NAME', 'NOT_SET') + } diff --git a/deploy-isolation-fix.sh b/deploy-isolation-fix.sh new file mode 100755 index 00000000..e69de29b diff --git a/verify-db-isolation.sh b/verify-db-isolation.sh new file mode 100755 index 00000000..dbfdfb2e --- /dev/null +++ b/verify-db-isolation.sh @@ -0,0 +1,171 @@ +#!/bin/bash +# Database Isolation Verification Script +# This script will verify database isolation between DEV and LIVE deployments + +GREEN='\033[0;32m' +RED='\033[0;31m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +echo -e "${BLUE}=== Database Isolation Verification Script ===${NC}" +echo -e "${YELLOW}This script will verify database isolation between DEV and LIVE deployments${NC}" +echo "" + +# First check API debug endpoints for database information +echo -e "${BLUE}Checking API debug endpoints for database information...${NC}" +DEV_URL="https://adr-lms.whitex.cloud" +LIVE_URL="https://edu.adradviser.ro" + +DEV_DEBUG=$(curl -s -m 10 -k "$DEV_URL/api/v1/debug/deployment" || echo '{"error":"Failed to connect"}') +LIVE_DEBUG=$(curl -s -m 10 -k "$LIVE_URL/api/v1/debug/deployment" || echo '{"error":"Failed to connect"}') + +# Extract values using Python if available +if command -v python3 &> /dev/null; then + echo -e "${GREEN}✓${NC} Python3 available for JSON parsing" + DEV_DB_HOST=$(echo "$DEV_DEBUG" | python3 -c "import sys, json; print(json.load(sys.stdin).get('database_host', 'unknown'))" 2>/dev/null) + LIVE_DB_HOST=$(echo "$LIVE_DEBUG" | python3 -c "import sys, json; print(json.load(sys.stdin).get('database_host', 'unknown'))" 2>/dev/null) + DEV_DB_NAME=$(echo "$DEV_DEBUG" | python3 -c "import sys, json; print(json.load(sys.stdin).get('database_name', 'unknown'))" 2>/dev/null) + LIVE_DB_NAME=$(echo "$LIVE_DEBUG" | python3 -c "import sys, json; print(json.load(sys.stdin).get('database_name', 'unknown'))" 2>/dev/null) + + echo -e "${YELLOW}From API Debug:${NC}" + echo -e "DEV DB: Host=${DEV_DB_HOST}, Name=${DEV_DB_NAME}" + echo -e "LIVE DB: Host=${LIVE_DB_HOST}, Name=${LIVE_DB_NAME}" + + if [ "$DEV_DB_HOST" == "$LIVE_DB_HOST" ]; then + echo -e "${RED}⚠️ WARNING: Both deployments using same database host: $DEV_DB_HOST${NC}" + else + if [ "$DEV_DB_HOST" != "unknown" ] && [ "$LIVE_DB_HOST" != "unknown" ]; then + echo -e "${GREEN}✓ Database hosts are properly isolated between deployments${NC}" + else + echo -e "${YELLOW}⚠️ Could not verify database hosts from API - falling back to manual checking${NC}" + fi + fi +else + echo -e "${YELLOW}Python3 not available for JSON parsing - falling back to manual checking${NC}" +fi + +echo -e "\n${BLUE}Continuing with direct database verification...${NC}" + +# Function to extract database connection details from environment variables +extract_db_details() { + # Get connection string from environment + local conn_string="$1" + + # Extract username, password, host, port, and database name + local username=$(echo "$conn_string" | sed -E 's/^postgresql:\/\/([^:]+):.*/\1/') + local password=$(echo "$conn_string" | sed -E 's/^postgresql:\/\/[^:]+:([^@]+)@.*/\1/') + local host=$(echo "$conn_string" | sed -E 's/^postgresql:\/\/[^@]+@([^:]+):.*/\1/') + local port=$(echo "$conn_string" | sed -E 's/^postgresql:\/\/[^@]+@[^:]+:([^\/]+)\/.*/\1/') + local dbname=$(echo "$conn_string" | sed -E 's/^postgresql:\/\/[^@]+@[^\/]+\/([^?]+).*/\1/') + + echo "Username: $username" + echo "Password: [HIDDEN]" + echo "Host: $host" + echo "Port: $port" + echo "Database: $dbname" + + # Return values in a specific format for later use + echo "$host|$port|$dbname|$username|$password" +} + +# Function to test database connection +test_db_connection() { + local details="$1" + local host=$(echo "$details" | cut -d'|' -f1) + local port=$(echo "$details" | cut -d'|' -f2) + local dbname=$(echo "$details" | cut -d'|' -f3) + local username=$(echo "$details" | cut -d'|' -f4) + local password=$(echo "$details" | cut -d'|' -f5) + + echo -e "${BLUE}Testing connection to $dbname on $host:$port...${NC}" + + # Try to connect and run a simple query + if PGPASSWORD="$password" psql -h "$host" -p "$port" -U "$username" -d "$dbname" -c "SELECT 1;" > /dev/null 2>&1; then + echo -e "${GREEN}✓ Successfully connected to database $dbname on $host${NC}" + return 0 + else + echo -e "${RED}✗ Failed to connect to database $dbname on $host${NC}" + return 1 + fi +} + +# Function to test if two databases share the same server +test_db_isolation() { + local dev_details="$1" + local live_details="$2" + + local dev_host=$(echo "$dev_details" | cut -d'|' -f1) + local live_host=$(echo "$live_details" | cut -d'|' -f1) + + echo -e "${BLUE}Checking database isolation...${NC}" + + if [ "$dev_host" == "$live_host" ]; then + echo -e "${RED}✗ ISOLATION FAILURE: DEV and LIVE environments are using the same database host: $dev_host${NC}" + echo -e "${RED} This will cause cross-deployment contamination!${NC}" + return 1 + else + echo -e "${GREEN}✓ Database isolation confirmed: DEV($dev_host) ≠ LIVE($live_host)${NC}" + return 0 + fi +} + +# Main execution + +# Get connection strings from environment or prompt user +if [ -z "$DEV_DB_URL" ]; then + echo -e "${YELLOW}DEV database connection string not found in environment.${NC}" + echo -e "Enter DEV database connection string (postgresql://user:pass@host:port/dbname):" + read -p "> " DEV_DB_URL +fi + +if [ -z "$LIVE_DB_URL" ]; then + echo -e "${YELLOW}LIVE database connection string not found in environment.${NC}" + echo -e "Enter LIVE database connection string (postgresql://user:pass@host:port/dbname):" + read -p "> " LIVE_DB_URL +fi + +# Extract connection details +echo -e "\n${BLUE}DEV Database Details:${NC}" +DEV_DETAILS=$(extract_db_details "$DEV_DB_URL") +echo "" + +echo -e "${BLUE}LIVE Database Details:${NC}" +LIVE_DETAILS=$(extract_db_details "$LIVE_DB_URL") +echo "" + +# Test connections +DEV_CONNECTION_OK=false +LIVE_CONNECTION_OK=false + +if test_db_connection "$DEV_DETAILS"; then + DEV_CONNECTION_OK=true +fi + +if test_db_connection "$LIVE_DETAILS"; then + LIVE_CONNECTION_OK=true +fi + +# If both connections work, test isolation +if $DEV_CONNECTION_OK && $LIVE_CONNECTION_OK; then + test_db_isolation "$DEV_DETAILS" "$LIVE_DETAILS" + ISOLATION_RESULT=$? +else + echo -e "${YELLOW}⚠️ Could not verify isolation because one or both database connections failed.${NC}" + ISOLATION_RESULT=2 +fi + +echo "" +echo -e "${BLUE}=== Verification Results ===${NC}" +if [ $ISOLATION_RESULT -eq 0 ]; then + echo -e "${GREEN}✓ SUCCESS: Databases are properly isolated${NC}" +elif [ $ISOLATION_RESULT -eq 1 ]; then + echo -e "${RED}✗ FAILURE: Databases are not isolated!${NC}" + echo -e "${YELLOW}Action required: Update your database connection strings to use different hosts.${NC}" + echo -e "See DATABASE_ISOLATION_FIX.md for details." +else + echo -e "${YELLOW}⚠️ INCONCLUSIVE: Could not verify isolation${NC}" + echo -e "Fix connection issues and try again." +fi + +exit $ISOLATION_RESULT diff --git a/verify-isolation.sh b/verify-isolation.sh index 9b74bd6f..b1fb1de6 100755 --- a/verify-isolation.sh +++ b/verify-isolation.sh @@ -1,102 +1,177 @@ #!/bin/bash -echo "🔍 LearnHouse Deployment Isolation Verification" -echo "===============================================" +# Colors for better output +GREEN='\033[0;32m' +RED='\033[0;31m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +echo -e "${BLUE}🔍 LearnHouse Deployment Isolation Verification${NC}" +echo -e "${BLUE}===============================================${NC}" # Check DEV deployment echo "" -echo "📋 DEV Deployment (adr-lms.whitex.cloud):" +echo -e "${YELLOW}📋 DEV Deployment (adr-lms.whitex.cloud):${NC}" echo "----------------------------------------" echo "Testing API connection..." -DEV_RESPONSE=$(curl -s https://adr-lms.whitex.cloud/api/v1/debug/deployment 2>/dev/null) -DEV_ROOT=$(curl -s https://adr-lms.whitex.cloud/ 2>/dev/null | head -200) +DEV_RESPONSE=$(curl -s -k https://adr-lms.whitex.cloud/api/v1/debug/deployment 2>/dev/null) +DEV_HEALTH=$(curl -s -k https://adr-lms.whitex.cloud/api/health 2>/dev/null) +DEV_ROOT=$(curl -s -k https://adr-lms.whitex.cloud/ 2>/dev/null | head -200) if [ $? -eq 0 ]; then - echo "✅ DEV API accessible" + echo -e "${GREEN}✅ DEV API accessible${NC}" + echo " Health: $DEV_HEALTH" echo " Debug Response: $DEV_RESPONSE" if [[ "$DEV_ROOT" == *"LearnHouse"* ]] || [[ "$DEV_ROOT" == *"React"* ]] || [[ "$DEV_ROOT" == *"Next"* ]]; then - echo "✅ DEV Frontend serving properly" + echo -e "${GREEN}✅ DEV Frontend serving properly${NC}" else - echo "⚠️ DEV Frontend response unclear" + echo -e "${YELLOW}⚠️ DEV Frontend response unclear${NC}" echo " Root response (first 100 chars): ${DEV_ROOT:0:100}" fi else - echo "❌ DEV API not accessible" + echo -e "${RED}❌ DEV API not accessible${NC}" fi echo "" echo "Testing frontend..." -DEV_FRONTEND=$(curl -s -o /dev/null -w "%{http_code}" https://adr-lms.whitex.cloud/ 2>/dev/null) +DEV_FRONTEND=$(curl -s -k -o /dev/null -w "%{http_code}" https://adr-lms.whitex.cloud/ 2>/dev/null) if [ "$DEV_FRONTEND" = "200" ]; then - echo "✅ DEV Frontend accessible (HTTP $DEV_FRONTEND)" + echo -e "${GREEN}✅ DEV Frontend accessible (HTTP $DEV_FRONTEND)${NC}" else - echo "❌ DEV Frontend issue (HTTP $DEV_FRONTEND)" + echo -e "${RED}❌ DEV Frontend issue (HTTP $DEV_FRONTEND)${NC}" fi # Check LIVE deployment echo "" -echo "📋 LIVE Deployment (edu.adradviser.ro):" +echo -e "${YELLOW}📋 LIVE Deployment (edu.adradviser.ro):${NC}" echo "---------------------------------------" echo "Testing API connection..." -LIVE_RESPONSE=$(curl -s https://edu.adradviser.ro/api/v1/debug/deployment 2>/dev/null) -LIVE_ROOT=$(curl -s https://edu.adradviser.ro/ 2>/dev/null | head -200) +LIVE_RESPONSE=$(curl -s -k https://edu.adradviser.ro/api/v1/debug/deployment 2>/dev/null) +LIVE_HEALTH=$(curl -s -k https://edu.adradviser.ro/api/health 2>/dev/null) +LIVE_ROOT=$(curl -s -k https://edu.adradviser.ro/ 2>/dev/null | head -200) if [ $? -eq 0 ]; then - echo "✅ LIVE API accessible" + echo -e "${GREEN}✅ LIVE API accessible${NC}" + echo " Health: $LIVE_HEALTH" echo " Debug Response: $LIVE_RESPONSE" if [[ "$LIVE_ROOT" == *"LearnHouse"* ]] || [[ "$LIVE_ROOT" == *"React"* ]] || [[ "$LIVE_ROOT" == *"Next"* ]]; then - echo "✅ LIVE Frontend serving properly" + echo -e "${GREEN}✅ LIVE Frontend serving properly${NC}" else - echo "⚠️ LIVE Frontend response unclear" + echo -e "${YELLOW}⚠️ LIVE Frontend response unclear${NC}" echo " Root response (first 100 chars): ${LIVE_ROOT:0:100}" fi else - echo "❌ LIVE API not accessible" + echo -e "${RED}❌ LIVE API not accessible${NC}" fi echo "" echo "Testing frontend..." -LIVE_FRONTEND=$(curl -s -o /dev/null -w "%{http_code}" https://edu.adradviser.ro/ 2>/dev/null) +LIVE_FRONTEND=$(curl -s -k -o /dev/null -w "%{http_code}" https://edu.adradviser.ro/ 2>/dev/null) if [ "$LIVE_FRONTEND" = "200" ]; then - echo "✅ LIVE Frontend accessible (HTTP $LIVE_FRONTEND)" + echo -e "${GREEN}✅ LIVE Frontend accessible (HTTP $LIVE_FRONTEND)${NC}" else - echo "❌ LIVE Frontend issue (HTTP $LIVE_FRONTEND)" + echo -e "${RED}❌ LIVE Frontend issue (HTTP $LIVE_FRONTEND)${NC}" fi # Analysis echo "" -echo "🔍 Cross-Deployment Isolation Analysis:" +echo -e "${BLUE}🔍 Cross-Deployment Isolation Analysis:${NC}" echo "========================================" +# Check for hardcoded URLs +echo -e "\n${BLUE}Checking for hardcoded LIVE URLs in DEV frontend...${NC}" +LIVE_URLS_IN_DEV=$(echo "$DEV_ROOT" | grep -o "http://edu.adradviser.ro[^\"']*\|https://edu.adradviser.ro[^\"']*" | sort -u) + +if [[ -n "$LIVE_URLS_IN_DEV" ]]; then + echo -e "${RED}⚠️ WARNING: Found hardcoded LIVE URLs in DEV frontend:${NC}" + echo "$LIVE_URLS_IN_DEV" +else + echo -e "${GREEN}✅ No hardcoded LIVE URLs found in DEV frontend${NC}" +fi + +# Check for courses UUIDs +DEV_COURSES=$(echo "$DEV_ROOT" | grep -o "course_[a-zA-Z0-9-]*" | sort -u) +LIVE_COURSES=$(echo "$LIVE_ROOT" | grep -o "course_[a-zA-Z0-9-]*" | sort -u) + +echo -e "\n${BLUE}Course UUIDs found in deployments:${NC}" +echo -e "${YELLOW}DEV courses:${NC} $(echo $DEV_COURSES | tr '\n' ' ')" +echo -e "${YELLOW}LIVE courses:${NC} $(echo $LIVE_COURSES | tr '\n' ' ')" + +# Find common courses +if [[ -n "$DEV_COURSES" && -n "$LIVE_COURSES" ]]; then + # Using grep to find common entries + COMMON_COURSES="" + for course in $DEV_COURSES; do + if echo "$LIVE_COURSES" | grep -q "$course"; then + COMMON_COURSES="$COMMON_COURSES $course" + fi + done + + if [[ -n "$COMMON_COURSES" ]]; then + echo -e "${RED}⚠️ WARNING: Found shared courses between deployments (contamination):${NC}" + echo "$COMMON_COURSES" + else + echo -e "${GREEN}✅ No shared courses found between deployments${NC}" + fi +fi + # Extract database hosts if responses are valid JSON -if command -v jq >/dev/null 2>&1; then - DEV_DB=$(echo "$DEV_RESPONSE" | jq -r '.database_host // "unknown"' 2>/dev/null) - LIVE_DB=$(echo "$LIVE_RESPONSE" | jq -r '.database_host // "unknown"' 2>/dev/null) +if command -v python3 >/dev/null 2>&1; then + echo -e "\n${BLUE}Database connection analysis:${NC}" + if [[ "$DEV_RESPONSE" == *"database_host"* ]]; then + DEV_DB=$(echo "$DEV_RESPONSE" | python3 -c "import sys, json; print(json.load(sys.stdin).get('database_host', 'unknown'))" 2>/dev/null) + echo -e "DEV database: ${YELLOW}$DEV_DB${NC}" + else + echo -e "${RED}⚠️ Cannot analyze DEV database - debug endpoint not working${NC}" + fi - if [ "$DEV_DB" != "unknown" ] && [ "$LIVE_DB" != "unknown" ]; then + if [[ "$LIVE_RESPONSE" == *"database_host"* ]]; then + LIVE_DB=$(echo "$LIVE_RESPONSE" | python3 -c "import sys, json; print(json.load(sys.stdin).get('database_host', 'unknown'))" 2>/dev/null) + echo -e "LIVE database: ${YELLOW}$LIVE_DB${NC}" + else + echo -e "${RED}⚠️ Cannot analyze LIVE database - debug endpoint not working${NC}" + fi + + if [[ -n "$DEV_DB" && -n "$LIVE_DB" && "$DEV_DB" != "unknown" && "$LIVE_DB" != "unknown" ]]; then if [ "$DEV_DB" = "$LIVE_DB" ]; then - echo "⚠️ WARNING: Both deployments using same database host: $DEV_DB" + echo -e "${RED}⚠️ WARNING: Both deployments using same database host: $DEV_DB${NC}" + echo -e "${RED} This is likely the cause of cross-deployment contamination!${NC}" else - echo "✅ Database isolation: DEV($DEV_DB) ≠ LIVE($LIVE_DB)" + echo -e "${GREEN}✅ Database isolation confirmed: DEV($DEV_DB) ≠ LIVE($LIVE_DB)${NC}" fi fi - DEV_COOKIE=$(echo "$DEV_RESPONSE" | jq -r '.cookie_domain // "unknown"' 2>/dev/null) - LIVE_COOKIE=$(echo "$LIVE_RESPONSE" | jq -r '.cookie_domain // "unknown"' 2>/dev/null) + echo -e "\n${BLUE}Cookie domain analysis:${NC}" + if [[ "$DEV_RESPONSE" == *"cookie_domain"* ]]; then + DEV_COOKIE=$(echo "$DEV_RESPONSE" | python3 -c "import sys, json; print(json.load(sys.stdin).get('cookie_domain', 'unknown'))" 2>/dev/null) + echo -e "DEV cookie domain: ${YELLOW}$DEV_COOKIE${NC}" + else + echo -e "${RED}⚠️ Cannot analyze DEV cookie domain - debug endpoint not working${NC}" + fi - if [ "$DEV_COOKIE" != "unknown" ] && [ "$LIVE_COOKIE" != "unknown" ]; then + if [[ "$LIVE_RESPONSE" == *"cookie_domain"* ]]; then + LIVE_COOKIE=$(echo "$LIVE_RESPONSE" | python3 -c "import sys, json; print(json.load(sys.stdin).get('cookie_domain', 'unknown'))" 2>/dev/null) + echo -e "LIVE cookie domain: ${YELLOW}$LIVE_COOKIE${NC}" + else + echo -e "${RED}⚠️ Cannot analyze LIVE cookie domain - debug endpoint not working${NC}" + fi + + if [[ -n "$DEV_COOKIE" && -n "$LIVE_COOKIE" && "$DEV_COOKIE" != "unknown" && "$LIVE_COOKIE" != "unknown" ]]; then if [ "$DEV_COOKIE" = "$LIVE_COOKIE" ]; then - echo "⚠️ WARNING: Both deployments using same cookie domain: $DEV_COOKIE" + echo -e "${RED}⚠️ WARNING: Both deployments using same cookie domain: $DEV_COOKIE${NC}" + echo -e "${RED} This could cause session contamination between deployments!${NC}" else - echo "✅ Cookie isolation: DEV($DEV_COOKIE) ≠ LIVE($LIVE_COOKIE)" + echo -e "${GREEN}✅ Cookie domain isolation confirmed: DEV($DEV_COOKIE) ≠ LIVE($LIVE_COOKIE)${NC}" fi fi else - echo "ℹ️ Install 'jq' for detailed analysis" + echo -e "${YELLOW}ℹ️ Python3 not available for JSON analysis${NC}" fi -echo "" -echo "🚀 Next Steps:" +echo -e "\n${BLUE}🚀 Next Steps:${NC}" echo "==============" -echo "1. If APIs are accessible, check browser Network tab for cross-deployment calls" -echo "2. Clear browser cache/cookies for both domains" -echo "3. Test in incognito mode to verify isolation" +echo "1. If debug endpoints are not accessible, deploy the API changes first" +echo "2. Verify database connection strings are different between deployments" +echo "3. Check the Dockerfile_coolify for proper API URL replacement" +echo "4. Clear browser cache/cookies for both domains" +echo "5. Test in incognito mode to verify isolation" echo "4. Check container logs: docker logs "