Spaces:
Sleeping
Sleeping
| # 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) | |
| 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) | |