143 lines
No EOL
5.7 KiB
Python
Executable file
143 lines
No EOL
5.7 KiB
Python
Executable file
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 |