""" Visual Workflow Builder - Backend Flask Application This is the main entry point for the Visual Workflow Builder backend API. It provides REST endpoints for workflow management and WebSocket support for real-time execution updates. """ from flask import Flask from flask_cors import CORS from flask_socketio import SocketIO from flask_sqlalchemy import SQLAlchemy from flask_caching import Cache import os from dotenv import load_dotenv # Load environment variables load_dotenv() # Initialize Flask app app = Flask(__name__) # Configuration app.config['SECRET_KEY'] = os.getenv('SECRET_KEY', 'dev-secret-key-change-in-production') app.config['SQLALCHEMY_DATABASE_URI'] = os.getenv('DATABASE_URL', 'sqlite:///workflows.db') app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False app.config['MAX_CONTENT_LENGTH'] = 10 * 1024 * 1024 # 10MB max upload app.config['CACHE_TYPE'] = 'redis' if os.getenv('REDIS_URL') else 'simple' app.config['CACHE_REDIS_URL'] = os.getenv('REDIS_URL', 'redis://localhost:6379/0') # Initialize extensions db = SQLAlchemy(app) cache = Cache(app) socketio = SocketIO( app, cors_allowed_origins="*", async_mode='threading', logger=True, engineio_logger=True ) # Enable CORS CORS(app, resources={ r"/api/*": { "origins": os.getenv('CORS_ORIGINS', 'http://localhost:3000').split(','), "methods": ["GET", "POST", "PUT", "DELETE", "OPTIONS"], "allow_headers": ["Content-Type", "Authorization"] } }) # Import and register blueprints (minimal set) from api.workflows import workflows_bp from api.screen_capture import screen_capture_bp from api.real_demo import real_demo_bp from api.errors import error_response app.register_blueprint(workflows_bp, url_prefix='/api/workflows') app.register_blueprint(screen_capture_bp, url_prefix='/api/screen-capture') app.register_blueprint(real_demo_bp) # Optional / Phase 2+ blueprints (loaded only if modules are available) try: from api.self_healing import self_healing_bp app.register_blueprint(self_healing_bp) except ImportError as e: print(f"⚠️ Blueprint self_healing désactivé: {e}") try: from api.visual_targets import visual_targets_bp, init_visual_target_manager app.register_blueprint(visual_targets_bp) VISUAL_TARGETS_BP_AVAILABLE = True except ImportError as e: print(f"⚠️ Blueprint visual_targets désactivé: {e}") VISUAL_TARGETS_BP_AVAILABLE = False init_visual_target_manager = None try: from api.element_detection import element_detection_bp, init_element_detection app.register_blueprint(element_detection_bp) ELEMENT_DETECTION_BP_AVAILABLE = True except ImportError as e: print(f"⚠️ Blueprint element_detection désactivé: {e}") ELEMENT_DETECTION_BP_AVAILABLE = False init_element_detection = None try: from api.analytics import analytics_bp app.register_blueprint(analytics_bp, url_prefix='/api/analytics') except ImportError: pass # Register other blueprints (optional - depends on Phase 2+ services) try: from api.templates import templates_bp app.register_blueprint(templates_bp, url_prefix='/api/templates') except ImportError as e: print(f"⚠️ Blueprint templates désactivé: {e}") from api.node_types import node_types_bp app.register_blueprint(node_types_bp, url_prefix='/api/node-types') try: from api.executions import executions_bp app.register_blueprint(executions_bp, url_prefix='/api/executions') except ImportError as e: print(f"⚠️ Blueprint executions désactivé: {e}") try: from api.import_export import import_export_bp app.register_blueprint(import_export_bp, url_prefix='/api') except ImportError as e: print(f"⚠️ Blueprint import_export désactivé: {e}") try: from api.correction_packs import correction_packs_bp app.register_blueprint(correction_packs_bp, url_prefix='/api') print("✅ Blueprint correction_packs enregistré") except ImportError as e: print(f"⚠️ Blueprint correction_packs désactivé: {e}") # Import WebSocket handlers (optional) try: from api import websocket_handlers # noqa: F401 except Exception as e: print(f"⚠️ WebSocket handlers désactivés: {e}") # Global error handlers @app.errorhandler(404) def not_found(error): """Handle 404 errors""" return error_response(404, "Resource not found") @app.errorhandler(405) def method_not_allowed(error): """Handle 405 errors""" return error_response(405, "Method not allowed") @app.errorhandler(500) def internal_error(error): """Handle 500 errors""" return error_response(500, "Internal server error") @app.errorhandler(Exception) def handle_exception(error): """Handle all unhandled exceptions""" import traceback traceback.print_exc() return error_response(500, f"Unexpected error: {str(error)}") # Health check endpoint @app.route('/health') def health_check(): """Health check endpoint for monitoring""" return {'status': 'healthy', 'version': '1.0.0'} # Create database tables with app.app_context(): db.create_all() # Initialize VisualTargetManager with RPA Vision V3 components (optional) try: from core.capture.screen_capturer import ScreenCapturer from core.detection.ui_detector import UIDetector from core.embedding.fusion_engine import FusionEngine # Only initialize if the related blueprints were actually loaded if VISUAL_TARGETS_BP_AVAILABLE and init_visual_target_manager: screen_capturer = ScreenCapturer() ui_detector = UIDetector() fusion_engine = FusionEngine() init_visual_target_manager(screen_capturer, ui_detector, fusion_engine) if ELEMENT_DETECTION_BP_AVAILABLE and init_element_detection: # Reuse the same instances when possible if 'ui_detector' not in locals(): ui_detector = UIDetector() if 'screen_capturer' not in locals(): screen_capturer = ScreenCapturer() init_element_detection(ui_detector, screen_capturer) if (VISUAL_TARGETS_BP_AVAILABLE and init_visual_target_manager) or (ELEMENT_DETECTION_BP_AVAILABLE and init_element_detection): print("✅ Services visuels initialisés (VisualTargets / ElementDetection)") except ImportError as e: print(f"⚠️ Core RPA non disponible pour l'initialisation visuelle: {e}") except Exception as e: print(f"❌ Erreur lors de l'initialisation des services visuels: {e}") if __name__ == '__main__': port = int(os.getenv('PORT', 5002)) debug = os.getenv('FLASK_ENV') == 'development' socketio.run( app, host='0.0.0.0', port=port, debug=debug, use_reloader=debug, allow_unsafe_werkzeug=True # For development only )