enflow-api / app.py
dhruv575
Fix openai calls
087b518
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
@app.errorhandler(Exception)
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
@app.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
@app.route('/api/test')
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
@app.route('/health')
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)