This commit is contained in:
IGNY8 VPS (Salman)
2025-12-17 00:27:53 +00:00
parent 8f97666522
commit 69c0fd8b69
8 changed files with 262 additions and 174 deletions

View File

@@ -1,174 +0,0 @@
# Container Restart and Auto-Logout Debugging Setup
## Overview
Added comprehensive logging to track container restarts and automatic user logouts.
## Changes Made
### 1. Container Lifecycle Logging
#### Backend Container (`backend/container_startup.sh`)
- Logs container startup time, hostname, and PID
- Detects and logs container restarts (by checking for previous PID file)
- Logs environment configuration (Python version, Django settings, DB host)
- Warns when restarts are detected and suggests checking Docker logs for SIGTERM signals
- Integrated into Dockerfile as ENTRYPOINT
#### Frontend Container (`frontend/container_startup.sh`)
- Logs container startup time, hostname, and PID
- Detects and logs container restarts
- Logs Node/NPM versions and Vite configuration
- Checks for git directory presence and warns about file watching
- Shows last git commit when detected
- Integrated into Dockerfile.dev as ENTRYPOINT
### 2. Vite File Watching Fix (`frontend/vite.config.ts`)
**ROOT CAUSE IDENTIFIED:** `usePolling: true` was watching ALL files including `.git` directory, causing container restarts on git commits.
**Fix Applied:**
```typescript
watch: {
usePolling: true,
ignored: [
'**/node_modules/**',
'**/.git/**', // CRITICAL: Ignore git directory
'**/dist/**',
'**/build/**',
'**/.vscode/**',
'**/.idea/**',
],
interval: 1000, // Poll every 1 second instead of default
}
```
### 3. Auto-Logout Logging (`backend/igny8_core/auth/middleware.py`)
Added detailed logging for all automatic logout scenarios:
#### Session Contamination - Account ID Mismatch
```python
logger.warning(
f"[AUTO-LOGOUT] Session contamination: account_id mismatch. "
f"Session={stored_account_id}, Current={request.account.id}, "
f"User={request.user.id}, Path={request.path}, IP={request.META.get('REMOTE_ADDR')}"
)
```
#### Session Contamination - User ID Mismatch
```python
logger.warning(
f"[AUTO-LOGOUT] Session contamination: user_id mismatch. "
f"Session={stored_user_id}, Current={request.user.id}, "
f"Account={request.account.id if request.account else None}, "
f"Path={request.path}, IP={request.META.get('REMOTE_ADDR')}"
)
```
#### Account/Plan Validation Failures
```python
logger.warning(
f"[AUTO-LOGOUT] Account/plan validation failed: {error}. "
f"User={request.user.id}, Account={getattr(request, 'account', None)}, "
f"Path={request.path}, IP={request.META.get('REMOTE_ADDR')}"
)
```
### 4. Logging Configuration (`backend/igny8_core/settings.py`)
Added two new loggers:
- `auth.middleware` - Captures all authentication and auto-logout events
- `container.lifecycle` - Captures container startup/restart events
Both log to console (captured by Docker logs).
## How to Use
### Viewing Container Restart Logs
```bash
# Check backend container logs for restart events
docker logs igny8_backend 2>&1 | grep "CONTAINER-STARTUP\|RESTART"
# Check frontend container logs for restart events
docker logs igny8_frontend 2>&1 | grep "CONTAINER-STARTUP\|RESTART"
# Monitor in real-time
docker logs -f igny8_backend
```
### Viewing Auto-Logout Logs
```bash
# Check for automatic logout events
docker logs igny8_backend 2>&1 | grep "AUTO-LOGOUT"
# Filter by specific logout reason
docker logs igny8_backend 2>&1 | grep "AUTO-LOGOUT.*contamination"
docker logs igny8_backend 2>&1 | grep "AUTO-LOGOUT.*validation failed"
# See recent logout events with context
docker logs --since 1h igny8_backend 2>&1 | grep -A2 -B2 "AUTO-LOGOUT"
```
### Correlating Events
```bash
# See both container restarts and logouts together
docker logs --since 2h igny8_backend 2>&1 | grep -E "CONTAINER-STARTUP|AUTO-LOGOUT|SIGTERM"
# Check if git commits correlate with restarts
git log --since="2 hours ago" --format="%ai %s" && \
docker logs --since 2h igny8_frontend 2>&1 | grep "CONTAINER-STARTUP"
```
## Next Steps to Deploy
1. **Rebuild Docker images:**
```bash
cd /data/app/igny8/backend
docker build -t igny8-backend:latest -f Dockerfile .
cd /data/app/igny8/frontend
docker build -t igny8-frontend-dev:latest -f Dockerfile.dev .
```
2. **Restart containers:**
```bash
cd /data/app/igny8
docker compose -f docker-compose.app.yml down
docker compose -f docker-compose.app.yml up -d
```
3. **Verify logging is working:**
```bash
docker logs igny8_backend 2>&1 | head -30
docker logs igny8_frontend 2>&1 | head -30
```
4. **Test git commit trigger (should NOT restart now):**
```bash
cd /data/app/igny8
echo "test" >> README.md
git add README.md
git commit -m "test commit"
# Wait 5 seconds and check - containers should NOT restart
sleep 5
docker ps --filter "name=igny8" --format "{{.Names}}: {{.Status}}"
```
## Expected Outcomes
1. **Git commits should NO LONGER trigger container restarts** because `.git` is now ignored by Vite's file watcher
2. **Every container restart will be logged** with timestamp and reason
3. **Every automatic logout will be logged** with user ID, account ID, reason, path, and IP address
4. **You can correlate restarts with git operations** to verify the fix is working
## Troubleshooting
If containers still restart after git commits:
1. Check if the new images were built and deployed
2. Verify the vite.config.ts changes are present in the running container
3. Check Docker logs to see what triggered the restart
4. Look for HMR messages in frontend logs mentioning `.git` files

View File

@@ -0,0 +1,103 @@
#!/usr/bin/env python
"""
Test ACTUAL content generation using existing task (same as frontend)
"""
import os, sys, django
sys.path.insert(0, '/app')
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'igny8_core.settings')
django.setup()
from igny8_core.ai.functions.generate_content import execute as generate_content
from igny8_core.auth.models import Account
from igny8_core.business.content.models import Tasks, Content
import re
from html import unescape
print("=" * 80)
print("TESTING ACTUAL CONTENT GENERATION (same function as frontend)")
print("=" * 80)
print()
# Get account and task
account = Account.objects.filter(slug='aws-admin').first()
task_id = 229 # "Essential Garden Tools for Every Gardener"
task = Tasks.objects.get(id=task_id)
print(f"✅ Using task: {task.title}")
print()
# Delete any existing content for this task to start fresh
existing = Content.objects.filter(task=task)
if existing.exists():
count = existing.count()
existing.delete()
print(f"🗑️ Deleted {count} existing content items")
print()
print("=" * 80)
print("CALLING generate_content() - THE ACTUAL FUNCTION")
print("=" * 80)
print()
# Call the ACTUAL function
try:
result = generate_content(
payload={'ids': [task_id]},
account=account,
user=None
)
print()
print("=" * 80)
print("GENERATION RESULT")
print("=" * 80)
print()
if result.get('success'):
print(f"✅ Success: {result.get('message')}")
print(f" Items: {result.get('count', 0)}")
print()
# Get the generated content
content = Content.objects.filter(task=task).order_by('-created_at').first()
if content:
# Count actual words
html_content = content.content_html or ''
text_only = re.sub(r'<[^>]+>', '', html_content)
text_only = unescape(text_only)
words = len(text_only.split())
print(f"📝 Content Details:")
print(f" Title: {content.title}")
print(f" Word Count (actual): {words}")
print(f" Word Count (reported): {content.word_count}")
print()
if words < 1200:
print(f"🚨 CRITICAL ISSUE: Only {words} words!")
print(f" Target: 1200+ words")
print(f" Shortfall: {1200 - words} words")
else:
print(f"✅ EXCELLENT: {words} words (exceeds 1200)")
print()
print("=" * 80)
print("FIRST 800 CHARACTERS OF CONTENT:")
print("=" * 80)
print(text_only[:800])
print()
print(f"[...{len(text_only) - 800} more characters...]" if len(text_only) > 800 else "")
else:
print(f"❌ Failed: {result.get('error')}")
except Exception as e:
print(f"❌ Exception: {e}")
import traceback
traceback.print_exc()
print()
print("=" * 80)
print("This is the EXACT same generate_content() function called by frontend")
print("=" * 80)

159
test_real_ai_function.py Normal file
View File

@@ -0,0 +1,159 @@
#!/usr/bin/env python
"""
Test the ACTUAL AI function execution path (same as frontend)
"""
import os, sys, django
sys.path.insert(0, '/app')
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'igny8_core.settings')
django.setup()
from igny8_core.ai.tasks import run_ai_task
from igny8_core.auth.models import Account, Site
from igny8_core.business.content.models import Tasks
import json
# Get account
account = Account.objects.filter(slug='aws-admin').first()
site = Site.objects.filter(account=account).first()
# Create a test task with the massager content idea
task_data = {
"title": "Top Shiatsu Back and Neck Massagers for Effective Pain Relief",
"description": json.dumps({
"introduction": {
"hook": "Explore the best shiatsu back and neck massagers to alleviate pain and improve relaxation.",
"paragraphs": [
{"content_type": "paragraph", "details": "Introduction to the concept of shiatsu massage and its effectiveness for pain relief."},
{"content_type": "paragraph", "details": "The shift towards home-based massage solutions in managing chronic pain."}
]
},
"H2": [
{
"heading": "Understanding Shiatsu Back and Neck Massagers",
"subsections": [
{"subheading": "The Science of Shiatsu Massage", "content_type": "paragraph", "details": "Explanation of shiatsu techniques and their therapeutic benefits."},
{"subheading": "Choosing the Right Shiatsu Massager", "content_type": "list", "details": "Evaluating features like heat, intensity settings, and portability."}
]
},
{
"heading": "Benefits of Shiatsu Massagers for Neck and Back",
"subsections": [
{"subheading": "Pain Relief and Muscle Relaxation", "content_type": "paragraph", "details": "How shiatsu massage helps release muscle tension."},
{"subheading": "Improved Sleep and Stress Reduction", "content_type": "list", "details": "Additional health benefits of regular shiatsu massages."}
]
},
{
"heading": "Top Shiatsu Back and Neck Massagers to Consider",
"subsections": [
{"subheading": "High-End Models", "content_type": "paragraph", "details": "Features of top-tier shiatsu massagers."},
{"subheading": "Best Budget Picks", "content_type": "list", "details": "Affordable options without compromising on quality."}
]
},
{
"heading": "User Guide: How to Use a Shiatsu Massager",
"subsections": [
{"subheading": "Step-by-Step Instructions", "content_type": "paragraph", "details": "Detailed guide on using your massager effectively."},
{"subheading": "Safety Tips", "content_type": "list", "details": "Precautions to ensure safe and beneficial use."}
]
},
{
"heading": "Comparing Shiatsu and Electric Massagers for Back Pain",
"subsections": [
{"subheading": "Pros and Cons", "content_type": "paragraph", "details": "Comparison of features and benefits."},
{"subheading": "Choosing Based on Personal Needs", "content_type": "list", "details": "Factors to consider when selecting a massager."}
]
},
{
"heading": "Caring for Your Shiatsu Massager",
"subsections": [
{"subheading": "Cleaning and Maintenance Tips", "content_type": "paragraph", "details": "Ensuring your massager lasts longer with proper care."},
{"subheading": "Troubleshooting and Repairs", "content_type": "list", "details": "Common issues and how to fix them."}
]
}
]
}),
"content_type": "post",
"content_structure": "comparison",
"keywords": "shiatsu back and neck massager, shiatsu neck and back massager, shiatsu back massager",
"status": "new",
"account": account,
"site": site,
}
# Create or get existing test task
task = Tasks.objects.filter(
title=task_data['title'],
account=account
).first()
if not task:
task = Tasks.objects.create(**task_data)
print(f"✅ Created test task: {task.id}")
else:
print(f"✅ Using existing task: {task.id}")
print()
print("=" * 70)
print("RUNNING ACTUAL AI FUNCTION (same path as frontend)")
print("=" * 70)
print()
# Run the ACTUAL function (synchronous, not Celery)
result = run_ai_task(
function_name='generate_content',
payload={'ids': [task.id]},
account_id=account.id,
self=type('obj', (object,), {'request': type('obj', (object,), {'id': 'test-task-id'})(), 'update_state': lambda *args, **kwargs: None})()
)
print()
print("=" * 70)
print("RESULT")
print("=" * 70)
print()
if result.get('success'):
print("✅ Content generated successfully")
print(f" Items created: {result.get('count', 0)}")
# Get the generated content
from igny8_core.business.content.models import Content
content = Content.objects.filter(task=task).order_by('-created_at').first()
if content:
# Count actual words
import re
from html import unescape
html_content = content.content_html or ''
# Strip HTML tags
text_only = re.sub(r'<[^>]+>', '', html_content)
# Unescape HTML entities
text_only = unescape(text_only)
# Count words
words = len(text_only.split())
print(f" Title: {content.title[:60]}...")
print(f" Word Count (actual): {words}")
print(f" Word Count (reported): {content.word_count}")
print()
if words < 1200:
print(f"🚨 PROBLEM: Only {words} words generated (target: 1200+)")
print(f" Shortfall: {1200 - words} words missing")
else:
print(f"✅ SUCCESS: {words} words generated (exceeds 1200 minimum)")
# Show first 500 chars
print()
print("First 500 characters of content:")
print("-" * 70)
print(text_only[:500])
else:
print(f"❌ Generation failed: {result.get('error')}")
print()
print("=" * 70)
print("This is the EXACT same code path used by the frontend")
print("=" * 70)