reorg
This commit is contained in:
@@ -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
|
||||
103
test_frontend_content_generation.py
Normal file
103
test_frontend_content_generation.py
Normal 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
159
test_real_ai_function.py
Normal 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)
|
||||
Reference in New Issue
Block a user