Skip to content
Snippets Groups Projects
__init__.py 3.47 KiB
"""
automatically invoked app factory
"""
import logging
import os

import pkg_resources
import sentry_sdk
from sentry_sdk.integrations.flask import FlaskIntegration

from flask import Flask
from flask_cors import CORS  # for debugging
# the currently available stubs for flask_migrate are old (they depend on sqlalchemy 1.4 types)
from flask_migrate import Migrate, upgrade  # type: ignore
from flask_login import LoginManager  # type: ignore

from compendium_v2 import config, environment
from compendium_v2.db import db
from compendium_v2.auth import setup_oauth
from compendium_v2.auth.session_management import setup_login_manager


sentry_dsn = os.getenv('SENTRY_DSN')
if sentry_dsn:
    sentry_sdk.init(
        dsn=sentry_dsn,
        integrations=[FlaskIntegration()],
        release=pkg_resources.get_distribution('compendium-v2').version)

environment.setup_logging()

logger = logging.getLogger(__name__)


def _create_app() -> Flask:
    # used by sphinx to create documentation without config and db migrations
    app = Flask(__name__)
    CORS(app)

    from compendium_v2.routes import default
    app.register_blueprint(default.routes, url_prefix='/')

    from compendium_v2.routes import authentication
    app.register_blueprint(authentication.routes, url_prefix='/')

    from compendium_v2.routes import api
    app.register_blueprint(api.routes, url_prefix='/api')

    return app


def _create_app_with_db(app_config) -> Flask:
    # used by the tests and the publishers
    app = _create_app()

    app.config['SECRET_KEY'] = app_config['SECRET_KEY']
    app.config['SESSION_COOKIE_SECURE'] = True
    if 'oidc' not in app_config:
        app.config['LOGIN_DISABLED'] = True
        logger.info('No OIDC configuration found, authentication disabled')
    else:
        logger.info('OIDC configuration found, authentication will be enabled')

    app.config['SQLALCHEMY_DATABASE_URI'] = app_config['SQLALCHEMY_DATABASE_URI']

    if 'SQLALCHEMY_BINDS' in app_config:
        # for the publishers
        app.config['SQLALCHEMY_BINDS'] = app_config['SQLALCHEMY_BINDS']

    if 'mail' in app_config:
        mail_config = app_config['mail']
        app.config['MAIL_ENABLE'] = True
        app.config['MAIL_SERVER'] = mail_config['MAIL_SERVER']
        app.config['MAIL_PORT'] = mail_config['MAIL_PORT']
        app.config['MAIL_SENDER_EMAIL'] = mail_config['MAIL_SENDER_EMAIL']  # email address to send emails from
        excluded_admins = mail_config.get('MAIL_EXCLUDED_ADMINS', [])
        app.config['MAIL_EXCLUDED_ADMINS'] = excluded_admins  # list of admin emails not to send emails to
    else:
        app.config['MAIL_ENABLE'] = False

    db.init_app(app)
    return app


def create_app() -> Flask:
    """
    overrides default settings with those found
    in the file read from env var SETTINGS_FILENAME

    :return: a new flask app instance
    """

    assert 'SETTINGS_FILENAME' in os.environ, "environment variable 'SETTINGS_FILENAME' is required"

    with open(os.environ['SETTINGS_FILENAME']) as f:
        app_config = config.load(f)

    app = _create_app_with_db(app_config)

    Migrate(app, db, directory=os.path.join(os.path.dirname(__file__), 'migrations'))

    logging.info('Flask app initialized')
    login_manager = LoginManager()
    login_manager.init_app(app)
    login_manager.login_view = 'authentication.login'
    setup_login_manager(login_manager)
    setup_oauth(app, app_config.get('oidc'))

    # run migrations on startup
    with app.app_context():
        upgrade()

    return app