Spaces:
Sleeping
Sleeping
| import os | |
| from flask import Flask, jsonify, request | |
| from flask_cors import CORS | |
| from dotenv import load_dotenv | |
| import logging | |
| from logging.handlers import RotatingFileHandler | |
| import pymongo | |
| import datetime | |
| import secrets | |
| # Import Database early for logging connection | |
| from db import Database | |
| # Import routes | |
| from routes.department_routes import department_bp | |
| from routes.auth_routes import auth_bp | |
| from routes.workflow_routes import workflow_bp | |
| from routes.log_routes import log_bp | |
| from routes.incident_routes import incident_bp | |
| from routes.user_routes import user_bp | |
| # Load environment variables | |
| load_dotenv() | |
| # Create Flask app | |
| app = Flask(__name__) | |
| # Setup basic logging configuration first | |
| log_dir = '/tmp/logs' | |
| log_file = f"{log_dir}/app.log" | |
| try: | |
| if not os.path.exists(log_dir): | |
| os.makedirs(log_dir) | |
| handler = RotatingFileHandler(log_file, maxBytes=100000, backupCount=5) | |
| log_level = logging.INFO | |
| handler.setLevel(log_level) | |
| formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') | |
| handler.setFormatter(formatter) | |
| app.logger.addHandler(handler) | |
| # Also add console logging | |
| console_handler = logging.StreamHandler() | |
| console_handler.setLevel(log_level) | |
| console_handler.setFormatter(formatter) | |
| app.logger.addHandler(console_handler) | |
| app.logger.setLevel(log_level) | |
| app.logger.info('File logging configured successfully.') | |
| except Exception as e: | |
| # If file logging fails, ensure console logging is set up | |
| if not any(isinstance(h, logging.StreamHandler) for h in app.logger.handlers): | |
| console_handler = logging.StreamHandler() | |
| console_handler.setLevel(logging.INFO) | |
| formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') | |
| console_handler.setFormatter(formatter) | |
| app.logger.addHandler(console_handler) | |
| app.logger.setLevel(logging.INFO) | |
| app.logger.warning(f"Could not set up file logging to {log_file}: {str(e)}. Logging to console.") | |
| app.logger.info('Flask app created.') | |
| # Configure secrets with fallbacks for development | |
| app.config['SECRET_KEY'] = os.environ.get('JWT_SECRET') or secrets.token_hex(32) | |
| app.config['MONGO_URI'] = os.environ.get('MONGO_URI') | |
| app.logger.info('App secrets configured.') | |
| # Verify essential environment variables | |
| missing_vars = [] | |
| required_env_vars = ['MONGO_URI', 'OPENAI_API_KEY'] | |
| for var in required_env_vars: | |
| if not os.environ.get(var): | |
| missing_vars.append(var) | |
| if missing_vars: | |
| app.logger.warning(f"Missing essential environment variables: {', '.join(missing_vars)}") | |
| app.logger.warning("Please create a .env file or set Space secrets.") | |
| else: | |
| app.logger.info("All essential environment variables are present.") | |
| # Database Connection Test | |
| try: | |
| db_instance = Database.get_instance() | |
| db = db_instance.get_db() | |
| db.command('ping') | |
| app.logger.info("Successfully connected to MongoDB.") | |
| except Exception as e: | |
| app.logger.error(f"Failed to connect to MongoDB: {str(e)}") | |
| # Depending on severity, you might want to exit or prevent app run | |
| # Configure OpenAI API key | |
| if 'OPENAI_API_KEY' in os.environ: | |
| try: | |
| # We don't need to initialize the client globally | |
| # It will be initialized in each function that needs it | |
| app.logger.info("OpenAI API key available.") | |
| except Exception as e: | |
| app.logger.error(f"Failed to import OpenAI: {str(e)}") | |
| else: | |
| app.logger.warning("OPENAI_API_KEY not found in environment variables.") | |
| # Enable CORS with specific origins | |
| origins = [ | |
| "http://localhost:3000", # Development frontend | |
| "https://www.tryenflow.com", # Production frontend | |
| "https://droov-enflow-api.hf.space" # Add the Hugging Face Space URL | |
| ] | |
| try: | |
| CORS(app, resources={r"/api/*": {"origins": origins}}, supports_credentials=True) | |
| app.logger.info(f"CORS configured for origins: {origins}") | |
| except Exception as e: | |
| app.logger.error(f"Failed to configure CORS: {str(e)}") | |
| # Register blueprints | |
| try: | |
| app.register_blueprint(auth_bp, url_prefix='/api/auth') | |
| app.register_blueprint(department_bp, url_prefix='/api/departments') | |
| app.register_blueprint(workflow_bp, url_prefix='/api/workflows') | |
| app.register_blueprint(log_bp, url_prefix='/api/logs') | |
| app.register_blueprint(incident_bp, url_prefix='/api/incidents') | |
| app.register_blueprint(user_bp, url_prefix='/api/users') | |
| app.logger.info("All API blueprints registered successfully.") | |
| except Exception as e: | |
| app.logger.error(f"Failed to register blueprints: {str(e)}") | |
| # Set up uploads directory in /tmp (which should be writable) | |
| app.config['UPLOAD_FOLDER'] = '/tmp/uploads' | |
| if not os.path.exists(app.config['UPLOAD_FOLDER']): | |
| try: | |
| os.makedirs(app.config['UPLOAD_FOLDER']) | |
| app.logger.info(f"Uploads directory created at {app.config['UPLOAD_FOLDER']}") | |
| except PermissionError: | |
| app.logger.warning(f"Permission denied creating {app.config['UPLOAD_FOLDER']}. Using /tmp fallback.") | |
| app.config['UPLOAD_FOLDER'] = '/tmp' | |
| except Exception as e: | |
| app.logger.error(f"Error creating uploads directory {app.config['UPLOAD_FOLDER']}: {str(e)}") | |
| # Error handler | |
| def handle_exception(e): | |
| # Log the actual exception trace | |
| app.logger.exception(f"Unhandled exception: {str(e)}") | |
| return jsonify({"error": "An unexpected internal error occurred"}), 500 | |
| # Root route | |
| def index(): | |
| app.logger.debug("Root route / accessed") | |
| return jsonify({ | |
| "message": "Enflow API is running", | |
| "version": "1.0.0" | |
| }) | |
| # Test route that doesn't require authentication | |
| def test_route(): | |
| app.logger.info("/api/test route accessed") | |
| return jsonify({ | |
| "message": "API test successful", | |
| "timestamp": datetime.datetime.now().isoformat(), | |
| "environment": os.environ.get('FLASK_DEBUG', '0') # Use FLASK_DEBUG now | |
| }) | |
| # Health check route | |
| def health_check(): | |
| app.logger.info("Health check requested") | |
| db_status = "unknown" | |
| env_status = "ok" | |
| error_details = "" | |
| status_code = 200 | |
| try: | |
| # Check MongoDB connection | |
| db = Database.get_instance().get_db() | |
| db.command('ping') | |
| db_status = "connected" | |
| app.logger.info("Health Check: MongoDB connection successful.") | |
| except Exception as e: | |
| db_status = "disconnected" | |
| error_details += f"MongoDB Error: {str(e)}; " | |
| app.logger.error(f"Health Check: MongoDB connection failed: {str(e)}") | |
| status_code = 503 # Service Unavailable | |
| # Check environment variables | |
| if missing_vars: | |
| env_status = f"Missing: {', '.join(missing_vars)}" | |
| if status_code == 200: # Don't downgrade status if DB already failed | |
| status_code = 500 # Internal Server Error for config issue | |
| error_details += f"Missing environment variables: {', '.join(missing_vars)}; " | |
| app.logger.warning(f"Health Check: Missing environment variables: {', '.join(missing_vars)}") | |
| response = { | |
| "status": "healthy" if status_code == 200 else "unhealthy", | |
| "mongo": db_status, | |
| "env_vars": env_status, | |
| "upload_dir_exists": os.path.exists(app.config.get('UPLOAD_FOLDER', '/tmp')), | |
| "log_dir_exists": os.path.exists(log_dir) | |
| } | |
| if error_details: | |
| response["errors"] = error_details.strip() | |
| return jsonify(response), status_code | |
| # Logging setup moved to the top | |
| if __name__ == '__main__': | |
| # Use FLASK_DEBUG instead of FLASK_ENV for debug mode control | |
| debug_mode = os.environ.get('FLASK_DEBUG', '0') == '1' | |
| # Port configuration - Hugging Face might override this via its execution environment | |
| port = int(os.environ.get('PORT', 7860)) # Default to 7860 if PORT env var not set | |
| app.logger.info(f"Starting Flask app. Debug mode: {debug_mode}. Port: {port}") | |
| # Note: When running via gunicorn (common in HF), gunicorn command sets the port, | |
| # and this app.run() is not executed directly. | |
| # The port setting here is mainly for local `python app.py` execution. | |
| app.run(host='0.0.0.0', port=port, debug=debug_mode) |