# MorphGuard Production Application # app.py - Flask application for production deployment from flask import Flask, session import os import sys from threading import Thread from src.utils.logger import logger # When running on HuggingFace Spaces, PostgreSQL / Ethereum / drone services # are not available. Set HF_SPACE=1 to skip those startup tasks gracefully. IS_HF_SPACE = os.environ.get('HF_SPACE', '0') == '1' # Add project root to path sys.path.append(os.getcwd()) # Import Utilities from flask_sock import Sock if not IS_HF_SPACE: from src.utils.telemetry import start_metrics_collection_thread from utils.auth import init_db as init_auth_db from scripts.setup_timescaledb import initialize_schema else: # Lightweight stubs so startup tasks can be skipped cleanly def start_metrics_collection_thread(): pass def init_auth_db(): pass def initialize_schema(): pass # Import API Main Class from morphguard_api import MorphGuardAPI def create_app(config_object=None): app = Flask(__name__) sock = Sock(app) # Configuration app.secret_key = os.environ.get('MORPHGUARD_SECRET_KEY') if not app.secret_key: print("WARNING: MORPHGUARD_SECRET_KEY not set. Using insecure default for dev.") app.secret_key = 'dev-default-key' app.config['UPLOAD_FOLDER'] = 'static/uploads' app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024 if IS_HF_SPACE: app.config['SESSION_COOKIE_SAMESITE'] = 'None' app.config['SESSION_COOKIE_SECURE'] = True # WebSocket Configuration - Disable server pings (Pi client handles keepalive) app.config['SOCK_SERVER_OPTIONS'] = {'ping_interval': None} os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True) os.makedirs('logs', exist_ok=True) # Initialize Core API try: api = MorphGuardAPI( detector_path='models/morph_detector.pth', demorph_path='models/demorpher.pth', gan_config_path=os.environ.get('GAN_CONFIG_PATH'), gan_checkpoint_path=os.environ.get('GAN_CHECKPOINT_PATH') ) app.mg_api = api # Attach to app for blueprints app.deepfake_detector = None # Initialize placeholder logger.info("MorphGuard Core API initialized") except Exception as e: logger.error(f"Failed to initialize MorphGuard Core API: {e}") app.mg_api = None # Deepfake Detector try: from src.deepfake.deepfake_detector import DeepfakeDetectorAPI app.deepfake_detector = DeepfakeDetectorAPI() logger.info("Deepfake detector loaded") except Exception as e: logger.warning(f"Deepfake detector not available: {e}") # Register Blueprints from src.routes.auth import auth_bp from src.routes.detection import detection_bp from src.routes.training import training_bp, start_feedback_watcher from src.routes.main import main_bp from src.routes.setup import setup_bp from src.routes.demorph import demorph_bp from src.routes.drone import drone_bp, register_drone_routes from src.routes.liveness import liveness_bp from src.api.identity_verification_api import identity_bp app.register_blueprint(auth_bp) # /login, /register app.register_blueprint(detection_bp) # /api/detect app.register_blueprint(demorph_bp) # /api/demorph app.register_blueprint(training_bp) # /api/train app.register_blueprint(main_bp) # /, /demo, /setup app.register_blueprint(setup_bp) # /api/setup/users, etc. app.register_blueprint(liveness_bp) app.register_blueprint(identity_bp) # Initialize Drone Routes with WebSocket register_drone_routes(app, sock) # Optional Plugins/Extensions _register_extensions(app) # Startup Tasks try: if not IS_HF_SPACE: # DB Init (Auth + Timescale) — skipped on HF Spaces (no PostgreSQL) try: init_auth_db() initialize_schema() except Exception as e: logger.error(f"DB Schema init failed: {e}") start_metrics_collection_thread() start_feedback_watcher() except Exception as e: logger.error(f"Startup task error: {e}") return app def _register_extensions(app): # Plugin API try: from plugin_api import init_app as init_plugin_api init_plugin_api(app) except ImportError: pass # Notification API try: from notification_api import init_app as init_notification_api init_notification_api(app) except ImportError: pass # Advanced Face API try: from src.api.advanced_face_api import advanced_face_bp app.register_blueprint(advanced_face_bp) except ImportError: pass # AXI Integration try: from axi_integration import auto_integrate_with_app if app.mg_api: auto_integrate_with_app(app, app.mg_api, app.config['UPLOAD_FOLDER']) except ImportError: pass # Law Enforcement Portal try: from law_enforcement_portal.le_routes import register_le_routes register_le_routes(app) @app.route('/le/') @app.route('/law-enforcement/') def le_portal_redirect(): if not session.get('logged_in'): from flask import redirect, url_for return redirect(url_for('main.index')) from flask import redirect return redirect('/le/dashboard') # Assuming generic route structure # Better: url_for('le_dashboard') if endpoint known, but let's stick to path if uncertain except ImportError: pass # Initialize SocketIO (not used on HF Spaces — sync gunicorn workers only) _socketio_available = False socketio = None if not IS_HF_SPACE: try: from flask_socketio import SocketIO socketio = SocketIO(cors_allowed_origins="*") _socketio_available = True except ImportError: pass # Create globally accessible app instance for gunicorn app = create_app() if _socketio_available and socketio is not None: socketio.init_app(app) try: from notification_api import init_socketio init_socketio(socketio) except Exception: pass # ROS2 Bridge (Optional) def start_ros2_if_supported(): try: import rclpy from rclpy.node import Node from std_msgs.msg import String class MGROS2Node(Node): def __init__(self): super().__init__('morphguard_bridge') self.detect_pub = self.create_publisher(String, 'morphguard/detect', 10) # ... publishers ... def start_ros2_bridge(): rclpy.init() node = MGROS2Node() # Attach node to app or global if needed, primarily for detection.py to publish # Ideally detection.py should import this singleton or publishing logic # For this refactor, allowing partial breakage of ROS2 is acceptable if we plan to move it to a clean service # or we can attach to current_app app.ros2_node = node Thread(target=lambda: rclpy.spin(node), daemon=True).start() start_ros2_bridge() logger.info("ROS2 bridge started") except ImportError: pass except Exception as e: logger.error(f"ROS2 init failed: {e}") if __name__ == '__main__': start_ros2_if_supported() is_production = os.environ.get('FLASK_ENV') == 'production' debug_mode = not is_production host = '0.0.0.0' port = 5000 logger.info(f"Starting MorphGuard (Probable Mode: {'Production' if is_production else 'Debug'})") if _socketio_available: socketio.run(app, debug=debug_mode, host=host, port=port, use_reloader=False, allow_unsafe_werkzeug=True) else: app.run(debug=debug_mode, host=host, port=port, use_reloader=False, threaded=True)