Files
eveAI/eveai_api/__init__.py
Josako 7702a6dfcc - Modernized authentication with the introduction of TenantProject
- Created a base mail template
- Adapt and improve document API to usage of catalogs and processors
- Adapt eveai_sync to new authentication mechanism and usage of catalogs and processors
2024-11-21 17:24:33 +01:00

155 lines
4.5 KiB
Python

import traceback
from flask import Flask, jsonify, request
from flask_jwt_extended import get_jwt_identity, verify_jwt_in_request
from sqlalchemy.exc import SQLAlchemyError
from werkzeug.exceptions import HTTPException
from common.extensions import db, api_rest, jwt, minio_client, simple_encryption
import os
import logging.config
from common.utils.database import Database
from config.logging_config import LOGGING
from .api.document_api import document_ns
from .api.auth import auth_ns
from config.config import get_config
from common.utils.celery_utils import make_celery, init_celery
from common.utils.eveai_exceptions import EveAIException
def create_app(config_file=None):
app = Flask(__name__)
environment = os.getenv('FLASK_ENV', 'development')
match environment:
case 'development':
app.config.from_object(get_config('dev'))
case 'production':
app.config.from_object(get_config('prod'))
case _:
app.config.from_object(get_config('dev'))
app.config['SESSION_KEY_PREFIX'] = 'eveai_api_'
app.celery = make_celery(app.name, app.config)
init_celery(app.celery, app)
logging.config.dictConfig(LOGGING)
logger = logging.getLogger(__name__)
logger.info("eveai_api starting up")
# Register Necessary Extensions
register_extensions(app)
# register Namespaces
register_namespaces(api_rest)
# Register Blueprints
register_blueprints(app)
# Register Error Handlers
register_error_handlers(app)
@app.before_request
def before_request():
# Check if this a health check request
if request.path.startswith('/_healthz') or request.path.startswith('/healthz'):
pass
else:
try:
verify_jwt_in_request(optional=True)
tenant_id = get_jwt_identity()
if tenant_id:
Database(tenant_id).switch_schema()
except Exception as e:
app.logger.error(f'Error in before_request: {str(e)}')
# Don't raise the exception here, let the request continue
# The appropriate error handling will be done in the specific endpoints
@app.route('/api/v1')
def swagger():
return api_rest.render_doc()
return app
def register_extensions(app):
db.init_app(app)
api_rest.init_app(app, title='EveAI API', version='1.0', description='EveAI API')
jwt.init_app(app)
minio_client.init_app(app)
simple_encryption.init_app(app)
def register_namespaces(app):
api_rest.add_namespace(document_ns, path='/api/v1/documents')
api_rest.add_namespace(auth_ns, path='/api/v1/auth')
def register_blueprints(app):
from .views.healthz_views import healthz_bp
app.register_blueprint(healthz_bp)
def register_error_handlers(app):
@app.errorhandler(Exception)
def handle_exception(e):
"""Handle all unhandled exceptions with detailed error responses"""
# Get the current exception info
exc_info = traceback.format_exc()
# Log the full exception details
app.logger.error(f"Unhandled exception: {str(e)}\n{exc_info}")
# Start with a default error response
response = {
"error": "Internal Server Error",
"message": str(e),
"type": e.__class__.__name__
}
status_code = 500
# Handle specific types of exceptions
if isinstance(e, HTTPException):
status_code = e.code
response["error"] = e.name
elif isinstance(e, SQLAlchemyError):
response["error"] = "Database Error"
response["details"] = str(e.__cause__ or e)
elif isinstance(e, ValueError):
status_code = 400
response["error"] = "Invalid Input"
# In development, include additional debug information
if app.debug:
response["debug"] = {
"exception": exc_info,
"class": e.__class__.__name__,
"module": e.__class__.__module__
}
return jsonify(response), status_code
@app.errorhandler(404)
def not_found_error(e):
return jsonify({
"error": "Not Found",
"message": str(e),
"type": "NotFoundError"
}), 404
@app.errorhandler(400)
def bad_request_error(e):
return jsonify({
"error": "Bad Request",
"message": str(e),
"type": "BadRequestError"
}), 400