Skip to content
Snippets Groups Projects
app.py 4.58 KiB
Newer Older
  • Learn to ignore specific revisions
  • Alex's avatar
    Alex committed
    
    
    from flask_migrate import Migrate
    from flask import Flask, send_from_directory, request, redirect
    from flask_cors import CORS
    from flask_compress import Compress
    
    import config
    from database.db import lookup_db_for_binding, init_all_dbs_with_app, init_binding_to_db_for_bindings
    from config.config import APP_NAME, ENV, DEBUG_MODE, get_app_config, get_static_url
    
    PORT_ENV_VAR_KEY = "PORT"
    
    Alex's avatar
    Alex committed
    
    
    # Create and configure our Flask app
    app = Flask(__name__, static_url_path=get_static_url())
    app.url_map.strict_slashes = False
    
    Alex's avatar
    Alex committed
    
    
    # Enable Gzip Compression on all responses
    Compress(app)
    
    # Enable CORS for all routes
    CORS(app)
    # NOTE: if there is an error in your route, e.g.: printing a variable that doesn't
    #       exist, then you will get a CORS related error which has nothing to do with CORS
    # Source: https://stackoverflow.com/questions/25594893/how-to-enable-cors-in-flask
    
    # Load config
    flask_app_config = get_app_config()
    app.config.from_object(flask_app_config)
    init_binding_to_db_for_bindings(flask_app_config.SQLALCHEMY_BINDS.keys())
    
    # Initialise all databases
    init_all_dbs_with_app(app)
    
    # The import below registers all models to their respective database
    from models import *
    
    # Register all blueprints/routes
    from blueprints import home_blueprint
    # from blueprints.auth import auth_blueprint
    app.register_blueprint(home_blueprint)
    # app.register_blueprint(auth_blueprint)
    
    Alex's avatar
    Alex committed
    
    
    # ===================== MIGRATION SETUP =====================
    APP_CONFIG_BINDINGS_DICT_KEY = "SQLALCHEMY_BINDS"
    # The below environment variables should be set by the script that initializes the migration
    # This environment variable is used by "Flask_Migrate" and should not be changed
    FLASK_MIGRATE_FLASK_APP_ENV_VAR_KEY = "FLASK_APP"
    # This environment variable is a hack to specify the DB we want to migrate, since we have multiple dbs
    #       and only require/want to migrate one at a time.
    FLASK_DB_BINDING_ENV_VAR_KEY = "FLASK_DB_BINDING_TO_MIGRATE"
    MIGRATION_MODE = os.environ.get(FLASK_MIGRATE_FLASK_APP_ENV_VAR_KEY, None) is not None
    if MIGRATION_MODE:
        binding_name = os.environ.get(FLASK_DB_BINDING_ENV_VAR_KEY, None)
        if not binding_name:
            print("Invalid binding name specified:", binding_name)
            exit(-1)
        with app.app_context():
            print("Initializing migration")
            # Source: https://flask-migrate.readthedocs.io/en/latest/
            # After the extension is initialized, a "db" group will be added to the command-line options with several
            # sub-commands
            bindings_dict = app.config[APP_CONFIG_BINDINGS_DICT_KEY]
            # Set the main db
            app.config["SQLALCHEMY_DATABASE_URI"] = bindings_dict[binding_name]
            # engine = db.get_engine(flask_app, bind_name)
            migrate = Migrate()
            # "db" arg below needs to be the database you want the above main db to become/migrate to
            db_to_migrate = lookup_db_for_binding(binding_name)
            migrate.init_app(app, db_to_migrate)
    # ===================== End MIGRATION SETUP. =====================
    # Get arguments to this program call
    if not MIGRATION_MODE:
        # The Flask Migrate CLI passes arguments from its own CLI, and we don't want to handle those.
        from cli_handler import handle_cli
        cli_args = sys.argv[1:]
        if len(cli_args) > 0:
            if DEBUG_MODE:
                with app.app_context():
                    handle_cli(app, lookup_db_for_binding, cli_args)
                exit(1)
            else:
                print("!!!WARNING!!! Application should not be run with CLI arguments, "
                      "if you intended to run the CLI please run in development mode.")
                exit(-1)
    
    
    
    # Serve all static assets for the frontend
    @app.route('/static/<path:path>')
    def serve_static_files(path):
        return send_from_directory('static', path)
    
    
    
    # Force secure connection
    @app.before_request
    def before_request():
        if not DEBUG_MODE:
            scheme = request.headers.get('X-Forwarded-Proto')
            if scheme and scheme == 'http' and request.url.startswith('http://'):
                url = request.url.replace('http://', 'https://', 1)
                code = 301
                return redirect(url, code=code)
    
    
    # Hook any custom Jinja templating functions
    from config import CUSTOM_TEMPLATE_FUNCTIONS
    app.jinja_env.globals.update(CUSTOM_TEMPLATE_FUNCTIONS)
    
    if __name__ == '__main__':
    
        host = '127.0.0.1'
        port = int(os.environ.get(PORT_ENV_VAR_KEY, 5000))
        if DEBUG_MODE:
            mode_desc = "debug"
        else:
            mode_desc = "production"
        # NOTE: it will say "Environment: production", but if DEBUG_MODE=True, then ignore that.
        app.run(debug=DEBUG_MODE, host=host, port=port)
        print(f"!!!WARNING!!! Running in {mode_desc} mode")