From 69c0fd8b69925a4d555532ccb8643cc2619f172b Mon Sep 17 00:00:00 2001 From: "IGNY8 VPS (Salman)" Date: Wed, 17 Dec 2025 00:27:53 +0000 Subject: [PATCH] reorg --- CONTAINER-RESTART-DEBUGGING.md | 174 ------------------ .../AUTHENTICATION-HOLISTIC-REVAMP.md | 0 .../LOGOUT-CAUSES-COMPLETE-REFERENCE.md | 0 .../LOGOUT-TRACKING-IMPLEMENTATION.md | 0 .../LOGOUT-TRACKING-QUICK-REFERENCE.md | 0 .../LOGOUT-TRACKING-TESTING-CHECKLIST.md | 0 test_frontend_content_generation.py | 103 +++++++++++ test_real_ai_function.py | 159 ++++++++++++++++ 8 files changed, 262 insertions(+), 174 deletions(-) delete mode 100644 CONTAINER-RESTART-DEBUGGING.md rename AUTHENTICATION-HOLISTIC-REVAMP.md => docs/logout-issues/AUTHENTICATION-HOLISTIC-REVAMP.md (100%) rename LOGOUT-CAUSES-COMPLETE-REFERENCE.md => docs/logout-issues/LOGOUT-CAUSES-COMPLETE-REFERENCE.md (100%) rename LOGOUT-TRACKING-IMPLEMENTATION.md => docs/logout-issues/LOGOUT-TRACKING-IMPLEMENTATION.md (100%) rename LOGOUT-TRACKING-QUICK-REFERENCE.md => docs/logout-issues/LOGOUT-TRACKING-QUICK-REFERENCE.md (100%) rename LOGOUT-TRACKING-TESTING-CHECKLIST.md => docs/logout-issues/LOGOUT-TRACKING-TESTING-CHECKLIST.md (100%) create mode 100644 test_frontend_content_generation.py create mode 100644 test_real_ai_function.py diff --git a/CONTAINER-RESTART-DEBUGGING.md b/CONTAINER-RESTART-DEBUGGING.md deleted file mode 100644 index 7c0e3662..00000000 --- a/CONTAINER-RESTART-DEBUGGING.md +++ /dev/null @@ -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 diff --git a/AUTHENTICATION-HOLISTIC-REVAMP.md b/docs/logout-issues/AUTHENTICATION-HOLISTIC-REVAMP.md similarity index 100% rename from AUTHENTICATION-HOLISTIC-REVAMP.md rename to docs/logout-issues/AUTHENTICATION-HOLISTIC-REVAMP.md diff --git a/LOGOUT-CAUSES-COMPLETE-REFERENCE.md b/docs/logout-issues/LOGOUT-CAUSES-COMPLETE-REFERENCE.md similarity index 100% rename from LOGOUT-CAUSES-COMPLETE-REFERENCE.md rename to docs/logout-issues/LOGOUT-CAUSES-COMPLETE-REFERENCE.md diff --git a/LOGOUT-TRACKING-IMPLEMENTATION.md b/docs/logout-issues/LOGOUT-TRACKING-IMPLEMENTATION.md similarity index 100% rename from LOGOUT-TRACKING-IMPLEMENTATION.md rename to docs/logout-issues/LOGOUT-TRACKING-IMPLEMENTATION.md diff --git a/LOGOUT-TRACKING-QUICK-REFERENCE.md b/docs/logout-issues/LOGOUT-TRACKING-QUICK-REFERENCE.md similarity index 100% rename from LOGOUT-TRACKING-QUICK-REFERENCE.md rename to docs/logout-issues/LOGOUT-TRACKING-QUICK-REFERENCE.md diff --git a/LOGOUT-TRACKING-TESTING-CHECKLIST.md b/docs/logout-issues/LOGOUT-TRACKING-TESTING-CHECKLIST.md similarity index 100% rename from LOGOUT-TRACKING-TESTING-CHECKLIST.md rename to docs/logout-issues/LOGOUT-TRACKING-TESTING-CHECKLIST.md diff --git a/test_frontend_content_generation.py b/test_frontend_content_generation.py new file mode 100644 index 00000000..79a7c7e9 --- /dev/null +++ b/test_frontend_content_generation.py @@ -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) diff --git a/test_real_ai_function.py b/test_real_ai_function.py new file mode 100644 index 00000000..c7297bbe --- /dev/null +++ b/test_real_ai_function.py @@ -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)