from quart import Quart from quart_cors import cors # No longer using Flask-JWT-Extended - replaced with Quart-compatible JWT from dotenv import load_dotenv import os import tempfile import asyncio load_dotenv() def setup_temp_directories(): """Set up temporary directories for Flask/Werkzeug file handling.""" # Try to create a temp directory in the backend folder backend_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) temp_dir = os.path.join(backend_dir, 'temp') upload_dir = os.path.join(backend_dir, 'uploads') # Create directories with proper permissions for directory in [temp_dir, upload_dir]: try: os.makedirs(directory, exist_ok=True) os.chmod(directory, 0o755) # Test write permissions test_file = os.path.join(directory, 'test_write') with open(test_file, 'w') as f: f.write('test') os.remove(test_file) print(f"✓ Directory {directory} is writable") except (OSError, PermissionError) as e: print(f"Warning: Cannot write to {directory}: {e}") continue # Set environment variables for Python's tempfile module if os.path.isdir(temp_dir) and os.access(temp_dir, os.W_OK): os.environ['TMPDIR'] = temp_dir os.environ['TEMP'] = temp_dir os.environ['TMP'] = temp_dir tempfile.tempdir = temp_dir print(f"✓ Set temp directory to: {temp_dir}") return temp_dir, upload_dir def create_app(): # Set up temp directories BEFORE creating Quart app temp_dir, upload_dir = setup_temp_directories() app = Quart(__name__) # Setup custom logging configuration try: from logging_config import setup_logging, DEFAULT_LOG_LEVEL setup_logging(os.environ.get('LOG_LEVEL', DEFAULT_LOG_LEVEL)) except ImportError: pass # Fallback to default logging if logging_config is not available # Configuration app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY', 'dev-secret-key') app.config['JWT_SECRET_KEY'] = os.environ.get('JWT_SECRET_KEY', 'jwt-secret-key') # Fix strict slashes - this prevents 308 redirects for trailing slashes app.url_map.strict_slashes = False app.config['JWT_ACCESS_TOKEN_EXPIRES'] = 86400 # 24 hours # Set longer timeouts for AI operations app.config['TIMEOUT'] = 300 # 5 minutes app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024 # 16 MB max upload # Configure Quart/Werkzeug file upload settings app.config['UPLOAD_FOLDER'] = upload_dir app.config['UPLOAD_EXTENSIONS'] = ['.jpg', '.jpeg', '.png'] # Configure temp directory for Quart/Werkzeug if temp_dir and os.path.isdir(temp_dir): app.config['TEMP_FOLDER'] = temp_dir print(f"✓ Quart configured with temp directory: {temp_dir}") # Additional Werkzeug configuration for multipart form handling app.config['MAX_CONTENT_PATH'] = None # Don't limit content path # Configure Werkzeug to handle uploads without temp files for small files app.config['MAX_FORM_MEMORY_SIZE'] = 16 * 1024 * 1024 # Keep small uploads in memory # Initialize extensions app = cors(app, allow_origin="*", allow_methods=["GET", "POST", "PUT", "DELETE", "OPTIONS"]) # JWT is now handled by custom Quart-compatible auth system # No longer using JWTManager(app) due to Flask/Quart incompatibility # Initialize AsyncServer WebSocket functionality from .extensions import socketio_server # Store socketio reference on app for backward compatibility app.socketio = socketio_server # Initialize async WebSocket manager from app.websocket_manager_async import init_async_websocket_manager websocket_manager = init_async_websocket_manager() # Debug tap removed - using simpler GPT-5 diagnostic logging instead # Debug: Track main process ID for cross-process debugging import threading main_process_id = os.getpid() main_thread_id = threading.get_ident() print(f"🔌 PROCESS DEBUG - Quart app initialized with WebSocket manager") print(f"🔌 PROCESS DEBUG - Main Quart PID: {main_process_id}, Thread: {main_thread_id}") # Initialize AI Runner service for autonomous conversations from app.services.ai_runner_service import init_ai_runner init_ai_runner() # Register blueprints from app.routes.auth import auth_bp from app.routes.personas import personas_bp from app.routes.focus_groups import focus_groups_bp from app.routes.ai_personas import ai_personas_bp from app.routes.focus_group_ai import focus_group_ai_bp from app.routes.folders import folders_bp from app.routes.tasks import tasks_bp app.register_blueprint(auth_bp, url_prefix='/api/auth') app.register_blueprint(personas_bp, url_prefix='/api/personas') app.register_blueprint(focus_groups_bp, url_prefix='/api/focus-groups') app.register_blueprint(ai_personas_bp, url_prefix='/api/ai-personas') app.register_blueprint(focus_group_ai_bp, url_prefix='/api/focus-group-ai') app.register_blueprint(folders_bp, url_prefix='/api/folders') app.register_blueprint(tasks_bp, url_prefix='/api/tasks') # Health check endpoint @app.route('/api/health', methods=['GET']) def health_check(): return {'status': 'ok', 'message': 'Backend is running'}, 200 # Create ASGI app with SocketIO integration import socketio as socketio_pkg asgi_app = socketio_pkg.ASGIApp(socketio_server, app) # Store reference to the original Quart app for access in routes asgi_app.quart_app = app return asgi_app