text: import datetime
  import os
  import sys
  import weakref
  from distutils.version import StrictVersion
  
  from flask import Flask, Request
  from flask_migrate import upgrade
  from jinja2 import FileSystemLoader
  from jinja2.sandbox import SandboxedEnvironment
  from six.moves import input
  from werkzeug.middleware.proxy_fix import ProxyFix
  from werkzeug.utils import cached_property
  
  from CTFd import utils
  from CTFd.plugins import init_plugins
  from CTFd.utils.crypto import sha256
  from CTFd.utils.initialization import (
      init_events,
      init_logs,
      init_request_processors,
      init_template_filters,
      init_template_globals,
  )
  from CTFd.utils.migrations import create_database, migrations, stamp_latest_revision
  from CTFd.utils.sessions import CachingSessionInterface
  from CTFd.utils.updates import update_check
  
  # Hack to support Unicode in Python 2 properly
  if sys.version_info[0] < 3:
      reload(sys)  # noqa: F821
      sys.setdefaultencoding("utf-8")
  
  __version__ = "2.5.0"
  
  
  class CTFdRequest(Request):
      @cached_property
      def path(self):
          """
          Hijack the original Flask request path because it does not account for subdirectory deployments in an intuitive
          manner. We append script_root so that the path always points to the full path as seen in the browser.
          e.g. /subdirectory/path/route vs /path/route
  
          :return: string
          """
          return self.script_root + super(CTFdRequest, self).path
  
  
  class CTFdFlask(Flask):
      def __init__(self, *args, **kwargs):
          """Overriden Jinja constructor setting a custom jinja_environment"""
          self.jinja_environment = SandboxedBaseEnvironment
          self.session_interface = CachingSessionInterface(key_prefix="session")
          self.request_class = CTFdRequest
  
          # Store server start time
          self.start_time = datetime.datetime.utcnow()
  
          # Create generally unique run identifier
          self.run_id = sha256(str(self.start_time))[0:8]
          Flask.__init__(self, *args, **kwargs)
  
      def create_jinja_environment(self):
          """Overridden jinja environment constructor"""
          return super(CTFdFlask, self).create_jinja_environment()
  
  
  class SandboxedBaseEnvironment(SandboxedEnvironment):
      """SandboxEnvironment that mimics the Flask BaseEnvironment"""
  
      def __init__(self, app, **options):
          if "loader" not in options:
              options["loader"] = app.create_global_jinja_loader()
          SandboxedEnvironment.__init__(self, **options)
          self.app = app
  
      def _load_template(self, name, globals):
          if self.loader is None:
              raise TypeError("no loader for this environment specified")
  
          # Add theme to the LRUCache cache key
          cache_name = name
          if name.startswith("admin/") is False:
              theme = str(utils.get_config("ctf_theme"))
              cache_name = theme + "/" + name
  
          # Rest of this code is copied from Jinja
          # https://github.com/pallets/jinja/blob/master/src/jinja2/environment.py#L802-L815
          cache_key = (weakref.ref(self.loader), cache_name)
          if self.cache is not None:
              template = self.cache.get(cache_key)
              if template is not None and (
                  not self.auto_reload or template.is_up_to_date
              ):
                  return template
          template = self.loader.load(self, name, globals)
          if self.cache is not None:
              self.cache[cache_key] = template
          return template
  
  
  class ThemeLoader(FileSystemLoader):
      """Custom FileSystemLoader that switches themes based on the configuration value"""
  
      def __init__(self, searchpath, encoding="utf-8", followlinks=False):
          super(ThemeLoader, self).__init__(searchpath, encoding, followlinks)
          self.overriden_templates = {}
  
      def get_source(self, environment, template):
          # Check if the template has been overriden
          if template in self.overriden_templates:
              return self.overriden_templates[template], template, lambda: True
  
          # Check if the template requested is for the admin panel
          if template.startswith("admin/"):
              template = template[6:]  # Strip out admin/
              template = "/".join(["admin", "templates", template])
              return super(ThemeLoader, self).get_source(environment, template)
  
          # Load regular theme data
          theme = str(utils.get_config("ctf_theme"))
          template = "/".join([theme, "templates", template])
          return super(ThemeLoader, self).get_source(environment, template)
  
  
  def confirm_upgrade():
      if sys.stdin.isatty():
          print("/*\\ CTFd has updated and must update the database! /*\\")
          print("/*\\ Please backup your database before proceeding! /*\\")
          print("/*\\ CTFd maintainers are not responsible for any data loss! /*\\")
          if input("Run database migrations (Y/N)").lower().strip() == "y":
              return True
          else:
              print("/*\\ Ignored database migrations... /*\\")
              return False
      else:
          return True
  
  
  def run_upgrade():
      upgrade()
      utils.set_config("ctf_version", __version__)
  
  
  def create_app(config="CTFd.config.Config"):
      app = CTFdFlask(__name__)
      with app.app_context():
          app.config.from_object(config)
  
          theme_loader = ThemeLoader(
              os.path.join(app.root_path, "themes"), followlinks=True
          )
          app.jinja_loader = theme_loader
  
          from CTFd.models import (  # noqa: F401
              db,
              Teams,
              Solves,
              Challenges,
              Fails,
              Flags,
              Tags,
              Files,
              Tracking,
          )
  
          url = create_database()
  
          # This allows any changes to the SQLALCHEMY_DATABASE_URI to get pushed back in
          # This is mostly so we can force MySQL's charset
          app.config["SQLALCHEMY_DATABASE_URI"] = str(url)
  
          # Register database
          db.init_app(app)
  
          # Register Flask-Migrate
          migrations.init_app(app, db)
  
          # Alembic sqlite support is lacking so we should just create_all anyway
          if url.drivername.startswith("sqlite"):
              # Enable foreign keys for SQLite. This must be before the
              # db.create_all call because tests use the in-memory SQLite
              # database (each connection, including db creation, is a new db).
              # https://docs.sqlalchemy.org/en/13/dialects/sqlite.html#foreign-key-support
              from sqlalchemy.engine import Engine
              from sqlalchemy import event
  
              @event.listens_for(Engine, "connect")
              def set_sqlite_pragma(dbapi_connection, connection_record):
                  cursor = dbapi_connection.cursor()
                  cursor.execute("PRAGMA foreign_keys=ON")
                  cursor.close()
  
              db.create_all()
              stamp_latest_revision()
          else:
              # This creates tables instead of db.create_all()
              # Allows migrations to happen properly
              upgrade()
  
          from CTFd.models import ma
  
          ma.init_app(app)
  
          app.db = db
          app.VERSION = __version__
  
          from CTFd.cache import cache
  
          cache.init_app(app)
          app.cache = cache
  
          reverse_proxy = app.config.get("REVERSE_PROXY")
          if reverse_proxy:
              if type(reverse_proxy) is str and "," in reverse_proxy:
                  proxyfix_args = [int(i) for i in reverse_proxy.split(",")]
                  app.wsgi_app = ProxyFix(app.wsgi_app, None, *proxyfix_args)
              else:
                  app.wsgi_app = ProxyFix(
                      app.wsgi_app,
                      num_proxies=None,
                      x_for=1,
                      x_proto=1,
                      x_host=1,
                      x_port=1,
                      x_prefix=1,
                  )
  
          version = utils.get_config("ctf_version")
  
          # Upgrading from an older version of CTFd
          if version and (StrictVersion(version) < StrictVersion(__version__)):
              if confirm_upgrade():
                  run_upgrade()
              else:
                  exit()
  
          if not version:
              utils.set_config("ctf_version", __version__)
  
          if not utils.get_config("ctf_theme"):
              utils.set_config("ctf_theme", "core")
  
          update_check(force=True)
  
          init_request_processors(app)
          init_template_filters(app)
          init_template_globals(app)
  
          # Importing here allows tests to use sensible names (e.g. api instead of api_bp)
          from CTFd.views import views
          from CTFd.teams import teams
          from CTFd.users import users
          from CTFd.challenges import challenges
          from CTFd.scoreboard import scoreboard
          from CTFd.auth import auth
          from CTFd.admin import admin
          from CTFd.api import api
          from CTFd.events import events
          from CTFd.errors import page_not_found, forbidden, general_error, gateway_error
  
          app.register_blueprint(views)
          app.register_blueprint(teams)
          app.register_blueprint(users)
          app.register_blueprint(challenges)
          app.register_blueprint(scoreboard)
          app.register_blueprint(auth)
          app.register_blueprint(api)
          app.register_blueprint(events)
  
          app.register_blueprint(admin)
  
          app.register_error_handler(404, page_not_found)
          app.register_error_handler(403, forbidden)
          app.register_error_handler(500, general_error)
          app.register_error_handler(502, gateway_error)
  
          init_logs(app)
          init_events(app)
          init_plugins(app)
  
          return app
  