diff --git a/DEPLOYMENT_TROUBLESHOOTING.md b/DEPLOYMENT_TROUBLESHOOTING.md index cb815341..087d116f 100644 --- a/DEPLOYMENT_TROUBLESHOOTING.md +++ b/DEPLOYMENT_TROUBLESHOOTING.md @@ -47,14 +47,20 @@ See `COOLIFY_ENV_VARS.md` for complete list. Key variables for isolation: - ✅ **Port mismatch fixed**: Changed from 3000 to 80 - ✅ **Container accessibility**: Traefik can now route to port 80 - ✅ **Frontend running**: Next.js server operational on port 8000 -- ❌ **Backend failing**: PM2 bash execution error fixed -- ⚠️ **502 errors**: Should resolve once backend starts correctly +- ✅ **Backend running**: FastAPI server operational on port 9000 +- ❌ **Cross-deployment contamination**: LIVE calling DEV APIs and vice versa +- ⚠️ **Root cause**: Frontend build-time API URLs not properly isolated ### Identified Issues & Fixes: -**Problem**: Backend API service failing with bash execution error -**Root Cause**: Incorrect PM2 command syntax for starting uvicorn -**Solution**: Updated start script to use direct Python execution instead of bash interpreter +**Problem**: Cross-deployment data contamination (LIVE sees DEV data) +**Root Cause**: Next.js build embeds API URLs at build-time, both deployments may share same URLs +**Solution**: Added runtime API URL patching in Docker container startup + +**Current Fix Applied**: +1. ✅ Enhanced patched-start.sh to replace API URLs at runtime +2. ✅ Added debug endpoint `/api/v1/debug/deployment` for verification +3. ✅ Added deployment verification script `verify-isolation.sh` ### Next Debugging Steps: @@ -102,14 +108,25 @@ The 502 errors should resolve once: ### Post-Deploy Verification: -After redeploying, run the debug script again and verify: +After redeploying, verify isolation works: ```bash -docker exec -it /app/debug-services.sh +# Run the automated verification script +./verify-isolation.sh + +# Or manually test the debug endpoints +curl https://adr-lms.whitex.cloud/api/v1/debug/deployment +curl https://edu.adradviser.ro/api/v1/debug/deployment + +# Check for cross-deployment API calls in browser Network tab +# Should see only same-domain API calls: +# - DEV: Only calls to adr-lms.whitex.cloud +# - LIVE: Only calls to edu.adradviser.ro ``` Expected output should show: -- ✅ PM2 status: Both services "online" -- ✅ Port 9000: Backend responding with status 200 -- ✅ No bash execution errors in logs +- ✅ Different database hosts for DEV vs LIVE +- ✅ Different cookie domains: adr-lms.whitex.cloud vs edu.adradviser.ro +- ✅ No cross-domain API calls in browser Network tab +- ✅ Separate content/courses on each deployment The port configuration fix was the critical missing piece for Traefik routing. diff --git a/Dockerfile_coolify b/Dockerfile_coolify index 4e7c629f..868b157e 100644 --- a/Dockerfile_coolify +++ b/Dockerfile_coolify @@ -69,6 +69,12 @@ find /app/web/.next -type f -name "*.js" -exec sed -i "s/domain:[^,}]*,/domain: 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 "Patching API URLs for deployment isolation..."\n\ +if [ ! -z "$NEXT_PUBLIC_LEARNHOUSE_API_URL" ]; then\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\ +fi\n\ sh /app/start.sh' > /app/patched-start.sh && chmod +x /app/patched-start.sh # Use the patched start script diff --git a/apps/api/app.py b/apps/api/app.py index c929d758..d8481637 100644 --- a/apps/api/app.py +++ b/apps/api/app.py @@ -1,4 +1,5 @@ import uvicorn +import os # import logfire from fastapi import FastAPI, Request from config.config import LearnHouseConfig, get_learnhouse_config @@ -20,6 +21,15 @@ from fastapi.middleware.gzip import GZipMiddleware # Get LearnHouse Config learnhouse_config: LearnHouseConfig = get_learnhouse_config() +# Debug logging for deployment isolation +print(f"🔍 DEPLOYMENT DEBUG INFO:") +print(f" DEPLOYMENT_NAME: {os.environ.get('DEPLOYMENT_NAME', 'NOT_SET')}") +print(f" DATABASE: {learnhouse_config.database_config.sql_connection_string[:50]}...") +print(f" REDIS: {learnhouse_config.redis_config.redis_connection_string[:50]}...") +print(f" COOKIE_DOMAIN: {learnhouse_config.hosting_config.cookie_config.domain}") +print(f" API_DOMAIN: {learnhouse_config.hosting_config.domain}") +print(f"🔍 END DEBUG INFO") + # Global Config app = FastAPI( title=learnhouse_config.site_name, @@ -83,3 +93,15 @@ if __name__ == "__main__": @app.get("/") async def root(): return {"Message": "Welcome to LearnHouse ✨"} + +# Debug endpoint for deployment verification +@app.get("/debug/deployment") +async def debug_deployment(): + import os + 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" + } diff --git a/verify-isolation.sh b/verify-isolation.sh new file mode 100755 index 00000000..857d8275 --- /dev/null +++ b/verify-isolation.sh @@ -0,0 +1,88 @@ +#!/bin/bash + +echo "🔍 LearnHouse Deployment Isolation Verification" +echo "===============================================" + +# Check DEV deployment +echo "" +echo "📋 DEV Deployment (adr-lms.whitex.cloud):" +echo "----------------------------------------" +echo "Testing API connection..." +DEV_RESPONSE=$(curl -s https://adr-lms.whitex.cloud/api/v1/debug/deployment 2>/dev/null) +if [ $? -eq 0 ]; then + echo "✅ DEV API accessible" + echo " Response: $DEV_RESPONSE" +else + echo "❌ DEV API not accessible" +fi + +echo "" +echo "Testing frontend..." +DEV_FRONTEND=$(curl -s -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)" +else + echo "❌ DEV Frontend issue (HTTP $DEV_FRONTEND)" +fi + +# Check LIVE deployment +echo "" +echo "📋 LIVE Deployment (edu.adradviser.ro):" +echo "---------------------------------------" +echo "Testing API connection..." +LIVE_RESPONSE=$(curl -s https://edu.adradviser.ro/api/v1/debug/deployment 2>/dev/null) +if [ $? -eq 0 ]; then + echo "✅ LIVE API accessible" + echo " Response: $LIVE_RESPONSE" +else + echo "❌ LIVE API not accessible" +fi + +echo "" +echo "Testing frontend..." +LIVE_FRONTEND=$(curl -s -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)" +else + echo "❌ LIVE Frontend issue (HTTP $LIVE_FRONTEND)" +fi + +# Analysis +echo "" +echo "🔍 Cross-Deployment Isolation Analysis:" +echo "========================================" + +# 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 [ "$DEV_DB" != "unknown" ] && [ "$LIVE_DB" != "unknown" ]; then + if [ "$DEV_DB" = "$LIVE_DB" ]; then + echo "⚠️ WARNING: Both deployments using same database host: $DEV_DB" + else + echo "✅ Database isolation: DEV($DEV_DB) ≠ LIVE($LIVE_DB)" + 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) + + if [ "$DEV_COOKIE" != "unknown" ] && [ "$LIVE_COOKIE" != "unknown" ]; then + if [ "$DEV_COOKIE" = "$LIVE_COOKIE" ]; then + echo "⚠️ WARNING: Both deployments using same cookie domain: $DEV_COOKIE" + else + echo "✅ Cookie isolation: DEV($DEV_COOKIE) ≠ LIVE($LIVE_COOKIE)" + fi + fi +else + echo "ℹ️ Install 'jq' for detailed analysis" +fi + +echo "" +echo "🚀 Next Steps:" +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 "4. Check container logs: docker logs "