mirror of
https://github.com/rzmk/learnhouse.git
synced 2025-12-19 04:19:25 +00:00
feat: implement deployment isolation verification and enhance API URL patching
This commit is contained in:
parent
e10e8ee5ba
commit
482dd930ce
4 changed files with 143 additions and 10 deletions
|
|
@ -47,14 +47,20 @@ See `COOLIFY_ENV_VARS.md` for complete list. Key variables for isolation:
|
||||||
- ✅ **Port mismatch fixed**: Changed from 3000 to 80
|
- ✅ **Port mismatch fixed**: Changed from 3000 to 80
|
||||||
- ✅ **Container accessibility**: Traefik can now route to port 80
|
- ✅ **Container accessibility**: Traefik can now route to port 80
|
||||||
- ✅ **Frontend running**: Next.js server operational on port 8000
|
- ✅ **Frontend running**: Next.js server operational on port 8000
|
||||||
- ❌ **Backend failing**: PM2 bash execution error fixed
|
- ✅ **Backend running**: FastAPI server operational on port 9000
|
||||||
- ⚠️ **502 errors**: Should resolve once backend starts correctly
|
- ❌ **Cross-deployment contamination**: LIVE calling DEV APIs and vice versa
|
||||||
|
- ⚠️ **Root cause**: Frontend build-time API URLs not properly isolated
|
||||||
|
|
||||||
### Identified Issues & Fixes:
|
### Identified Issues & Fixes:
|
||||||
|
|
||||||
**Problem**: Backend API service failing with bash execution error
|
**Problem**: Cross-deployment data contamination (LIVE sees DEV data)
|
||||||
**Root Cause**: Incorrect PM2 command syntax for starting uvicorn
|
**Root Cause**: Next.js build embeds API URLs at build-time, both deployments may share same URLs
|
||||||
**Solution**: Updated start script to use direct Python execution instead of bash interpreter
|
**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:
|
### Next Debugging Steps:
|
||||||
|
|
||||||
|
|
@ -102,14 +108,25 @@ The 502 errors should resolve once:
|
||||||
|
|
||||||
### Post-Deploy Verification:
|
### Post-Deploy Verification:
|
||||||
|
|
||||||
After redeploying, run the debug script again and verify:
|
After redeploying, verify isolation works:
|
||||||
```bash
|
```bash
|
||||||
docker exec -it <container_name> /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:
|
Expected output should show:
|
||||||
- ✅ PM2 status: Both services "online"
|
- ✅ Different database hosts for DEV vs LIVE
|
||||||
- ✅ Port 9000: Backend responding with status 200
|
- ✅ Different cookie domains: adr-lms.whitex.cloud vs edu.adradviser.ro
|
||||||
- ✅ No bash execution errors in logs
|
- ✅ 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.
|
The port configuration fix was the critical missing piece for Traefik routing.
|
||||||
|
|
|
||||||
|
|
@ -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: *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\
|
find /app/web/.next -type f -name "*.js" -exec sed -i "s/\.domain\s*=\s*[^;]*;/\.domain = undefined;/g" {} \\;\n\
|
||||||
echo "Patch complete."\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
|
sh /app/start.sh' > /app/patched-start.sh && chmod +x /app/patched-start.sh
|
||||||
|
|
||||||
# Use the patched start script
|
# Use the patched start script
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import uvicorn
|
import uvicorn
|
||||||
|
import os
|
||||||
# import logfire
|
# import logfire
|
||||||
from fastapi import FastAPI, Request
|
from fastapi import FastAPI, Request
|
||||||
from config.config import LearnHouseConfig, get_learnhouse_config
|
from config.config import LearnHouseConfig, get_learnhouse_config
|
||||||
|
|
@ -20,6 +21,15 @@ from fastapi.middleware.gzip import GZipMiddleware
|
||||||
# Get LearnHouse Config
|
# Get LearnHouse Config
|
||||||
learnhouse_config: LearnHouseConfig = 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
|
# Global Config
|
||||||
app = FastAPI(
|
app = FastAPI(
|
||||||
title=learnhouse_config.site_name,
|
title=learnhouse_config.site_name,
|
||||||
|
|
@ -83,3 +93,15 @@ if __name__ == "__main__":
|
||||||
@app.get("/")
|
@app.get("/")
|
||||||
async def root():
|
async def root():
|
||||||
return {"Message": "Welcome to LearnHouse ✨"}
|
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"
|
||||||
|
}
|
||||||
|
|
|
||||||
88
verify-isolation.sh
Executable file
88
verify-isolation.sh
Executable file
|
|
@ -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 <container_name>"
|
||||||
Loading…
Add table
Add a link
Reference in a new issue