import os import flask_excel as excel import sentry_sdk from flask import Flask, g, jsonify, request from flask.logging import default_handler from flask_cors import CORS from flask_sqlalchemy_replication import Replication from sentry_sdk.integrations.flask import FlaskIntegration from sentry_sdk.utils import BadDsn from auth import util as auth_util from cache import cache from core.blueprint import blueprint as core_blueprint from core.client_base import BaseAPIError from core.json import CustomJSONEncoder from core.logging import JsonFormatter from main_db.base import db from push_notifications.blueprint import blueprint as push_notifications_blueprint from redis_db.blueprint import blueprint as redis_db_blueprint from redis_db.util import get_cache_mode # Add sentry logging sentry_dsn = os.environ.get("SENTRY_DSN") sentry_env = os.environ.get("FLASK_ENV", "development") if sentry_dsn: try: sentry_sdk.init(dsn=sentry_dsn, environment=sentry_env, integrations=[FlaskIntegration()]) except BadDsn: pass app = Flask(__name__) app.config.from_object(os.environ["FLASK_CONFIG_MODULE"]) app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False db.init_app(app) cache.init_app(app) excel.init_excel(app) Replication(app) CORS(app, origins=app.config["ALLOWED_ORIGINS"]) default_handler.setFormatter(JsonFormatter()) app.register_blueprint(core_blueprint) app.register_blueprint(push_notifications_blueprint, url_prefix=app.config["API_PREFIX"] + "/push-notifications") app.register_blueprint(redis_db_blueprint) app.json_encoder = CustomJSONEncoder if app.config.get("DEBUG"): from apispec import APISpec from apispec.ext.marshmallow import MarshmallowPlugin from flask_apispec.extension import FlaskApiSpec app.config.update( { "APISPEC_SPEC": APISpec( title="Apollo API", version="1.0.0", openapi_version="2.0", plugins=[MarshmallowPlugin()], securityDefinitions={ "apiKey": { "description": "Service API key", "in": "header", "name": "Authorization", "type": "apiKey", }, "userId": {"description": "Current user ID", "in": "header", "name": "X-User-Id", "type": "apiKey"}, }, schemes=["http", "https"], security=[{"apiKey": [], "userId": []}], ), "APISPEC_SWAGGER_URL": "/api/docs/swagger.json", "APISPEC_SWAGGER_UI_URL": "/api/doc", } ) docs = FlaskApiSpec(app) for name, rule in app.view_functions.items(): try: blueprint_name, endpoint_name = name.split(".") except ValueError: continue try: docs.register(rule.view_class, endpoint=endpoint_name, blueprint=blueprint_name) except (AttributeError, TypeError): pass @app.errorhandler(BaseAPIError) def handle_ext_api_error(e: BaseAPIError): """Handle external API errors to prevent them from throwing to sentry.""" return jsonify({"message": e.message, "data": e.response_data}), e.status_code @app.before_request def set_cache_mode(): """Set cache mode based on header and configuration.""" g._cache_mode = get_cache_mode() @app.before_request def set_user(): """Base user check.""" auth_util.authorize() g._user = auth_util.User(request.headers.get("X-User-Id")) @app.teardown_request def teardown_request(exception): if exception: db.session.rollback() else: try: db.session.commit() except Exception: db.session.rollback() if __name__ == "__main__": app.run(host="0.0.0.0", port=5000) # nosec