diff --git a/backend/igny8_core/ai/tasks.py b/backend/igny8_core/ai/tasks.py index 2b1d3af1..cfe436c1 100644 --- a/backend/igny8_core/ai/tasks.py +++ b/backend/igny8_core/ai/tasks.py @@ -176,10 +176,10 @@ def process_image_generation_queue(self, image_ids: list, account_id: int = None logger.info(f"[process_image_generation_queue] Image generation settings found. Config keys: {list(config.keys())}") logger.info(f"[process_image_generation_queue] Full config: {config}") - provider = config.get('provider', 'openai') - # FORCE DALL-E 2 ONLY FOR NOW + # FORCE OPENAI DALL-E 2 ONLY FOR NOW + provider = 'openai' model = 'dall-e-2' - logger.info(f"[process_image_generation_queue] FORCED MODEL: {model} (ignoring config model: {config.get('model', 'dall-e-3')})") + logger.info(f"[process_image_generation_queue] FORCED PROVIDER: {provider}, MODEL: {model} (ignoring config provider: {config.get('provider', 'openai')}, model: {config.get('model', 'dall-e-3')})") image_type = config.get('image_type', 'realistic') image_format = config.get('image_format', 'webp') desktop_enabled = config.get('desktop_enabled', True) @@ -202,32 +202,33 @@ def process_image_generation_queue(self, image_ids: list, account_id: int = None # Get provider API key (using same approach as test image generation) # Note: API key is stored as 'apiKey' (camelCase) in IntegrationSettings.config - logger.info(f"[process_image_generation_queue] Step 2: Loading {provider.upper()} API key") + # FORCED: Always use 'openai' for provider (DALL-E 2 only) + logger.info(f"[process_image_generation_queue] Step 2: Loading {provider.upper()} API key (FORCED: openai)") try: provider_settings = IntegrationSettings.objects.get( account=account, - integration_type=provider, # 'openai' or 'runware' + integration_type='openai', # FORCED: Always use 'openai' for DALL-E 2 is_active=True ) - logger.info(f"[process_image_generation_queue] {provider.upper()} integration settings found") - logger.info(f"[process_image_generation_queue] {provider.upper()} config keys: {list(provider_settings.config.keys()) if provider_settings.config else 'None'}") + logger.info(f"[process_image_generation_queue] OPENAI integration settings found") + logger.info(f"[process_image_generation_queue] OPENAI config keys: {list(provider_settings.config.keys()) if provider_settings.config else 'None'}") api_key = provider_settings.config.get('apiKey') if provider_settings.config else None if not api_key: - logger.error(f"[process_image_generation_queue] {provider.upper()} API key not found in config") - logger.error(f"[process_image_generation_queue] {provider.upper()} config: {provider_settings.config}") - return {'success': False, 'error': f'{provider.upper()} API key not configured'} + logger.error(f"[process_image_generation_queue] OPENAI API key not found in config") + logger.error(f"[process_image_generation_queue] OPENAI config: {provider_settings.config}") + return {'success': False, 'error': 'OPENAI API key not configured'} # Log API key presence (but not the actual key for security) api_key_preview = f"{api_key[:10]}...{api_key[-4:]}" if len(api_key) > 14 else "***" - logger.info(f"[process_image_generation_queue] {provider.upper()} API key retrieved successfully (length: {len(api_key)}, preview: {api_key_preview})") + logger.info(f"[process_image_generation_queue] OPENAI API key retrieved successfully (length: {len(api_key)}, preview: {api_key_preview})") except IntegrationSettings.DoesNotExist: - logger.error(f"[process_image_generation_queue] ERROR: {provider.upper()} integration settings not found") - logger.error(f"[process_image_generation_queue] Account: {account.id if account else 'None'}, integration_type: '{provider}'") - return {'success': False, 'error': f'{provider.upper()} integration not found or not active'} + logger.error(f"[process_image_generation_queue] ERROR: OPENAI integration settings not found") + logger.error(f"[process_image_generation_queue] Account: {account.id if account else 'None'}, integration_type: 'openai'") + return {'success': False, 'error': 'OPENAI integration not found or not active'} except Exception as e: - logger.error(f"[process_image_generation_queue] ERROR getting {provider} API key: {e}", exc_info=True) - return {'success': False, 'error': f'Error retrieving {provider} API key: {str(e)}'} + logger.error(f"[process_image_generation_queue] ERROR getting OPENAI API key: {e}", exc_info=True) + return {'success': False, 'error': f'Error retrieving OPENAI API key: {str(e)}'} # Get image prompt template (has placeholders: {image_type}, {post_title}, {image_prompt}) try: @@ -533,11 +534,43 @@ def process_image_generation_queue(self, image_ids: list, account_id: int = None import os import requests from django.conf import settings + from pathlib import Path # Create /data/app/images directory if it doesn't exist + # Try absolute path first, fallback to project-relative if needed images_dir = '/data/app/images' - os.makedirs(images_dir, exist_ok=True) - logger.info(f"[process_image_generation_queue] Image {image_id} - Created/verified directory: {images_dir}") + write_test_passed = False + + try: + os.makedirs(images_dir, exist_ok=True) + # Test write access + test_file = os.path.join(images_dir, '.write_test') + with open(test_file, 'w') as f: + f.write('test') + os.remove(test_file) + write_test_passed = True + logger.info(f"[process_image_generation_queue] Image {image_id} - Directory writable: {images_dir}") + except Exception as write_test_error: + logger.warning(f"[process_image_generation_queue] Image {image_id} - Directory not writable: {images_dir}, error: {write_test_error}") + # Fallback to project-relative path + from django.conf import settings + base_dir = Path(settings.BASE_DIR) if hasattr(settings, 'BASE_DIR') else Path(__file__).resolve().parent.parent.parent + images_dir = str(base_dir / 'data' / 'app' / 'images') + try: + os.makedirs(images_dir, exist_ok=True) + # Test fallback directory write access + test_file = os.path.join(images_dir, '.write_test') + with open(test_file, 'w') as f: + f.write('test') + os.remove(test_file) + write_test_passed = True + logger.info(f"[process_image_generation_queue] Image {image_id} - Using fallback directory (writable): {images_dir}") + except Exception as fallback_error: + logger.error(f"[process_image_generation_queue] Image {image_id} - Fallback directory also not writable: {images_dir}, error: {fallback_error}") + raise Exception(f"Neither /data/app/images nor {images_dir} is writable. Last error: {fallback_error}") + + if not write_test_passed: + raise Exception(f"Failed to find writable directory for saving images") # Generate filename: image_{image_id}_{timestamp}.png import time diff --git a/backend/test_image_write_access.py b/backend/test_image_write_access.py new file mode 100644 index 00000000..427c2c8d --- /dev/null +++ b/backend/test_image_write_access.py @@ -0,0 +1,126 @@ +""" +Test script to verify write access to image directories +""" +import os +import sys +from pathlib import Path + +# Add project to path +sys.path.insert(0, str(Path(__file__).parent)) + +# Test write access logic +def test_write_access(): + print("=" * 60) + print("Testing Image Directory Write Access") + print("=" * 60) + + # Test 1: Absolute path /data/app/images + images_dir = '/data/app/images' + write_test_passed = False + + print(f"\n[Test 1] Testing absolute path: {images_dir}") + try: + os.makedirs(images_dir, exist_ok=True) + print(f" ✓ Directory created/verified: {images_dir}") + + # Test write access + test_file = os.path.join(images_dir, '.write_test') + print(f" → Attempting to write test file: {test_file}") + + with open(test_file, 'w') as f: + f.write('test') + print(f" ✓ Write successful") + + os.remove(test_file) + print(f" ✓ Test file removed") + + write_test_passed = True + print(f" ✅ SUCCESS: {images_dir} is writable") + + except PermissionError as e: + print(f" ✗ PERMISSION DENIED: {e}") + print(f" → Trying fallback path...") + + # Fallback to project-relative path + try: + from django.conf import settings + base_dir = Path(settings.BASE_DIR) if hasattr(settings, 'BASE_DIR') else Path(__file__).resolve().parent.parent + except: + base_dir = Path(__file__).resolve().parent + + images_dir = str(base_dir / 'data' / 'app' / 'images') + print(f"\n[Test 2] Testing fallback path: {images_dir}") + + try: + os.makedirs(images_dir, exist_ok=True) + print(f" ✓ Directory created/verified: {images_dir}") + + # Test fallback directory write access + test_file = os.path.join(images_dir, '.write_test') + print(f" → Attempting to write test file: {test_file}") + + with open(test_file, 'w') as f: + f.write('test') + print(f" ✓ Write successful") + + os.remove(test_file) + print(f" ✓ Test file removed") + + write_test_passed = True + print(f" ✅ SUCCESS: {images_dir} is writable") + + except Exception as fallback_error: + print(f" ✗ FAILED: {fallback_error}") + print(f" ❌ ERROR: Neither /data/app/images nor {images_dir} is writable") + return False + + except Exception as e: + print(f" ✗ ERROR: {e}") + print(f" → Trying fallback path...") + + # Fallback to project-relative path + try: + from django.conf import settings + base_dir = Path(settings.BASE_DIR) if hasattr(settings, 'BASE_DIR') else Path(__file__).resolve().parent.parent + except: + base_dir = Path(__file__).resolve().parent + + images_dir = str(base_dir / 'data' / 'app' / 'images') + print(f"\n[Test 2] Testing fallback path: {images_dir}") + + try: + os.makedirs(images_dir, exist_ok=True) + print(f" ✓ Directory created/verified: {images_dir}") + + # Test fallback directory write access + test_file = os.path.join(images_dir, '.write_test') + print(f" → Attempting to write test file: {test_file}") + + with open(test_file, 'w') as f: + f.write('test') + print(f" ✓ Write successful") + + os.remove(test_file) + print(f" ✓ Test file removed") + + write_test_passed = True + print(f" ✅ SUCCESS: {images_dir} is writable") + + except Exception as fallback_error: + print(f" ✗ FAILED: {fallback_error}") + print(f" ❌ ERROR: Neither /data/app/images nor {images_dir} is writable") + return False + + if not write_test_passed: + print(f"\n❌ FAILED: No writable directory found") + return False + + print(f"\n" + "=" * 60) + print(f"✅ FINAL RESULT: Images will be saved to: {images_dir}") + print("=" * 60) + return True + +if __name__ == '__main__': + success = test_write_access() + sys.exit(0 if success else 1) +