We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Fails to start when reverse proxy is configured with boolean
<!--
If this is a bug report please fill out the template below.

If this is a feature request please describe the behavior that you'd like to see.
-->

**Environment**:

  - CTFd Version/Commit: 22c1323
  - Operating System: Ubuntu 18.04
  - Web Browser and Version: n/a

**What happened?**

Running with docker-compose, fails to start due to bad type checking on the reverse proxy config var

**What did you expect to happen?**

Should have properly started

**How to reproduce your issue**

Set `REVERSE_PROXY` in CTFd/config.py to `True`

**Any associated stack traces or error logs**

```
ctfd_1   | Traceback (most recent call last):
ctfd_1   |   File "manage.py", line 9, in <module>
ctfd_1   |     app = create_app()
ctfd_1   |   File "/opt/CTFd/CTFd/__init__.py", line 180, in create_app
ctfd_1   |     if "," in reverse_proxy:
ctfd_1   | TypeError: argument of type 'bool' is not iterable
ctfd_1   | Traceback (most recent call last):
ctfd_1   |   File "manage.py", line 9, in <module>
ctfd_1   |     app = create_app()
ctfd_1   |   File "/opt/CTFd/CTFd/__init__.py", line 180, in create_app
ctfd_1   |     if "," in reverse_proxy:
ctfd_1   | TypeError: argument of type 'bool' is not iterable
ctfd_ctfd_1 exited with code 1
```

I have a patch for this ready to go and will make a pull request.

Fails to start when reverse proxy is configured with boolean
<!--
If this is a bug report please fill out the template below.

If this is a feature request please describe the behavior that you'd like to see.
-->

**Environment**:

  - CTFd Version/Commit: 22c1323
  - Operating System: Ubuntu 18.04
  - Web Browser and Version: n/a

**What happened?**

Running with docker-compose, fails to start due to bad type checking on the reverse proxy config var

**What did you expect to happen?**

Should have properly started

**How to reproduce your issue**

Set `REVERSE_PROXY` in CTFd/config.py to `True`

**Any associated stack traces or error logs**

```
ctfd_1   | Traceback (most recent call last):
ctfd_1   |   File "manage.py", line 9, in <module>
ctfd_1   |     app = create_app()
ctfd_1   |   File "/opt/CTFd/CTFd/__init__.py", line 180, in create_app
ctfd_1   |     if "," in reverse_proxy:
ctfd_1   | TypeError: argument of type 'bool' is not iterable
ctfd_1   | Traceback (most recent call last):
ctfd_1   |   File "manage.py", line 9, in <module>
ctfd_1   |     app = create_app()
ctfd_1   |   File "/opt/CTFd/CTFd/__init__.py", line 180, in create_app
ctfd_1   |     if "," in reverse_proxy:
ctfd_1   | TypeError: argument of type 'bool' is not iterable
ctfd_ctfd_1 exited with code 1
```

I have a patch for this ready to go and will make a pull request.


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `CTFd/__init__.py`
Content:
```
1 import datetime
2 import os
3 import sys
4 from distutils.version import StrictVersion
5 
6 from flask import Flask, Request
7 from flask_migrate import upgrade
8 from jinja2 import FileSystemLoader
9 from jinja2.sandbox import SandboxedEnvironment
10 from six.moves import input
11 from werkzeug.middleware.proxy_fix import ProxyFix
12 from werkzeug.utils import cached_property
13 
14 from CTFd import utils
15 from CTFd.plugins import init_plugins
16 from CTFd.utils.crypto import sha256
17 from CTFd.utils.initialization import (
18     init_events,
19     init_logs,
20     init_request_processors,
21     init_template_filters,
22     init_template_globals,
23 )
24 from CTFd.utils.migrations import create_database, migrations, stamp_latest_revision
25 from CTFd.utils.sessions import CachingSessionInterface
26 from CTFd.utils.updates import update_check
27 
28 # Hack to support Unicode in Python 2 properly
29 if sys.version_info[0] < 3:
30     reload(sys)  # noqa: F821
31     sys.setdefaultencoding("utf-8")
32 
33 __version__ = "2.3.1"
34 
35 
36 class CTFdRequest(Request):
37     @cached_property
38     def path(self):
39         """
40         Hijack the original Flask request path because it does not account for subdirectory deployments in an intuitive
41         manner. We append script_root so that the path always points to the full path as seen in the browser.
42         e.g. /subdirectory/path/route vs /path/route
43 
44         :return: string
45         """
46         return self.script_root + super(CTFdRequest, self).path
47 
48 
49 class CTFdFlask(Flask):
50     def __init__(self, *args, **kwargs):
51         """Overriden Jinja constructor setting a custom jinja_environment"""
52         self.jinja_environment = SandboxedBaseEnvironment
53         self.session_interface = CachingSessionInterface(key_prefix="session")
54         self.request_class = CTFdRequest
55 
56         # Store server start time
57         self.start_time = datetime.datetime.utcnow()
58 
59         # Create generally unique run identifier
60         self.run_id = sha256(str(self.start_time))[0:8]
61         Flask.__init__(self, *args, **kwargs)
62 
63     def create_jinja_environment(self):
64         """Overridden jinja environment constructor"""
65         return super(CTFdFlask, self).create_jinja_environment()
66 
67 
68 class SandboxedBaseEnvironment(SandboxedEnvironment):
69     """SandboxEnvironment that mimics the Flask BaseEnvironment"""
70 
71     def __init__(self, app, **options):
72         if "loader" not in options:
73             options["loader"] = app.create_global_jinja_loader()
74         # Disable cache entirely so that themes can be switched (#662)
75         # If the cache is enabled, switching themes will cause odd rendering errors
76         SandboxedEnvironment.__init__(self, cache_size=0, **options)
77         self.app = app
78 
79 
80 class ThemeLoader(FileSystemLoader):
81     """Custom FileSystemLoader that switches themes based on the configuration value"""
82 
83     def __init__(self, searchpath, encoding="utf-8", followlinks=False):
84         super(ThemeLoader, self).__init__(searchpath, encoding, followlinks)
85         self.overriden_templates = {}
86 
87     def get_source(self, environment, template):
88         # Check if the template has been overriden
89         if template in self.overriden_templates:
90             return self.overriden_templates[template], template, True
91 
92         # Check if the template requested is for the admin panel
93         if template.startswith("admin/"):
94             template = template[6:]  # Strip out admin/
95             template = "/".join(["admin", "templates", template])
96             return super(ThemeLoader, self).get_source(environment, template)
97 
98         # Load regular theme data
99         theme = utils.get_config("ctf_theme")
100         template = "/".join([theme, "templates", template])
101         return super(ThemeLoader, self).get_source(environment, template)
102 
103 
104 def confirm_upgrade():
105     if sys.stdin.isatty():
106         print("/*\\ CTFd has updated and must update the database! /*\\")
107         print("/*\\ Please backup your database before proceeding! /*\\")
108         print("/*\\ CTFd maintainers are not responsible for any data loss! /*\\")
109         if input("Run database migrations (Y/N)").lower().strip() == "y":
110             return True
111         else:
112             print("/*\\ Ignored database migrations... /*\\")
113             return False
114     else:
115         return True
116 
117 
118 def run_upgrade():
119     upgrade()
120     utils.set_config("ctf_version", __version__)
121 
122 
123 def create_app(config="CTFd.config.Config"):
124     app = CTFdFlask(__name__)
125     with app.app_context():
126         app.config.from_object(config)
127 
128         theme_loader = ThemeLoader(
129             os.path.join(app.root_path, "themes"), followlinks=True
130         )
131         app.jinja_loader = theme_loader
132 
133         from CTFd.models import (  # noqa: F401
134             db,
135             Teams,
136             Solves,
137             Challenges,
138             Fails,
139             Flags,
140             Tags,
141             Files,
142             Tracking,
143         )
144 
145         url = create_database()
146 
147         # This allows any changes to the SQLALCHEMY_DATABASE_URI to get pushed back in
148         # This is mostly so we can force MySQL's charset
149         app.config["SQLALCHEMY_DATABASE_URI"] = str(url)
150 
151         # Register database
152         db.init_app(app)
153 
154         # Register Flask-Migrate
155         migrations.init_app(app, db)
156 
157         # Alembic sqlite support is lacking so we should just create_all anyway
158         if url.drivername.startswith("sqlite"):
159             db.create_all()
160             stamp_latest_revision()
161         else:
162             # This creates tables instead of db.create_all()
163             # Allows migrations to happen properly
164             upgrade()
165 
166         from CTFd.models import ma
167 
168         ma.init_app(app)
169 
170         app.db = db
171         app.VERSION = __version__
172 
173         from CTFd.cache import cache
174 
175         cache.init_app(app)
176         app.cache = cache
177 
178         reverse_proxy = app.config.get("REVERSE_PROXY")
179         if reverse_proxy:
180             if "," in reverse_proxy:
181                 proxyfix_args = [int(i) for i in reverse_proxy.split(",")]
182                 app.wsgi_app = ProxyFix(app.wsgi_app, None, *proxyfix_args)
183             else:
184                 app.wsgi_app = ProxyFix(
185                     app.wsgi_app,
186                     num_proxies=None,
187                     x_for=1,
188                     x_proto=1,
189                     x_host=1,
190                     x_port=1,
191                     x_prefix=1,
192                 )
193 
194         version = utils.get_config("ctf_version")
195 
196         # Upgrading from an older version of CTFd
197         if version and (StrictVersion(version) < StrictVersion(__version__)):
198             if confirm_upgrade():
199                 run_upgrade()
200             else:
201                 exit()
202 
203         if not version:
204             utils.set_config("ctf_version", __version__)
205 
206         if not utils.get_config("ctf_theme"):
207             utils.set_config("ctf_theme", "core")
208 
209         update_check(force=True)
210 
211         init_request_processors(app)
212         init_template_filters(app)
213         init_template_globals(app)
214 
215         # Importing here allows tests to use sensible names (e.g. api instead of api_bp)
216         from CTFd.views import views
217         from CTFd.teams import teams
218         from CTFd.users import users
219         from CTFd.challenges import challenges
220         from CTFd.scoreboard import scoreboard
221         from CTFd.auth import auth
222         from CTFd.admin import admin
223         from CTFd.api import api
224         from CTFd.events import events
225         from CTFd.errors import page_not_found, forbidden, general_error, gateway_error
226 
227         app.register_blueprint(views)
228         app.register_blueprint(teams)
229         app.register_blueprint(users)
230         app.register_blueprint(challenges)
231         app.register_blueprint(scoreboard)
232         app.register_blueprint(auth)
233         app.register_blueprint(api)
234         app.register_blueprint(events)
235 
236         app.register_blueprint(admin)
237 
238         app.register_error_handler(404, page_not_found)
239         app.register_error_handler(403, forbidden)
240         app.register_error_handler(500, general_error)
241         app.register_error_handler(502, gateway_error)
242 
243         init_logs(app)
244         init_events(app)
245         init_plugins(app)
246 
247         return app
248
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/CTFd/__init__.py b/CTFd/__init__.py
--- a/CTFd/__init__.py
+++ b/CTFd/__init__.py
@@ -177,7 +177,7 @@
 
         reverse_proxy = app.config.get("REVERSE_PROXY")
         if reverse_proxy:
-            if "," in 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:

{"golden_diff": "diff --git a/CTFd/__init__.py b/CTFd/__init__.py\n--- a/CTFd/__init__.py\n+++ b/CTFd/__init__.py\n@@ -177,7 +177,7 @@\n \n         reverse_proxy = app.config.get(\"REVERSE_PROXY\")\n         if reverse_proxy:\n-            if \",\" in reverse_proxy:\n+            if type(reverse_proxy) is str and \",\" in reverse_proxy:\n                 proxyfix_args = [int(i) for i in reverse_proxy.split(\",\")]\n                 app.wsgi_app = ProxyFix(app.wsgi_app, None, *proxyfix_args)\n             else:\n", "issue": "Fails to start when reverse proxy is configured with boolean\n<!--\r\nIf this is a bug report please fill out the template below.\r\n\r\nIf this is a feature request please describe the behavior that you'd like to see.\r\n-->\r\n\r\n**Environment**:\r\n\r\n  - CTFd Version/Commit: 22c1323\r\n  - Operating System: Ubuntu 18.04\r\n  - Web Browser and Version: n/a\r\n\r\n**What happened?**\r\n\r\nRunning with docker-compose, fails to start due to bad type checking on the reverse proxy config var\r\n\r\n**What did you expect to happen?**\r\n\r\nShould have properly started\r\n\r\n**How to reproduce your issue**\r\n\r\nSet `REVERSE_PROXY` in CTFd/config.py to `True`\r\n\r\n**Any associated stack traces or error logs**\r\n\r\n```\r\nctfd_1   | Traceback (most recent call last):\r\nctfd_1   |   File \"manage.py\", line 9, in <module>\r\nctfd_1   |     app = create_app()\r\nctfd_1   |   File \"/opt/CTFd/CTFd/__init__.py\", line 180, in create_app\r\nctfd_1   |     if \",\" in reverse_proxy:\r\nctfd_1   | TypeError: argument of type 'bool' is not iterable\r\nctfd_1   | Traceback (most recent call last):\r\nctfd_1   |   File \"manage.py\", line 9, in <module>\r\nctfd_1   |     app = create_app()\r\nctfd_1   |   File \"/opt/CTFd/CTFd/__init__.py\", line 180, in create_app\r\nctfd_1   |     if \",\" in reverse_proxy:\r\nctfd_1   | TypeError: argument of type 'bool' is not iterable\r\nctfd_ctfd_1 exited with code 1\r\n```\r\n\r\nI have a patch for this ready to go and will make a pull request.\r\n\nFails to start when reverse proxy is configured with boolean\n<!--\r\nIf this is a bug report please fill out the template below.\r\n\r\nIf this is a feature request please describe the behavior that you'd like to see.\r\n-->\r\n\r\n**Environment**:\r\n\r\n  - CTFd Version/Commit: 22c1323\r\n  - Operating System: Ubuntu 18.04\r\n  - Web Browser and Version: n/a\r\n\r\n**What happened?**\r\n\r\nRunning with docker-compose, fails to start due to bad type checking on the reverse proxy config var\r\n\r\n**What did you expect to happen?**\r\n\r\nShould have properly started\r\n\r\n**How to reproduce your issue**\r\n\r\nSet `REVERSE_PROXY` in CTFd/config.py to `True`\r\n\r\n**Any associated stack traces or error logs**\r\n\r\n```\r\nctfd_1   | Traceback (most recent call last):\r\nctfd_1   |   File \"manage.py\", line 9, in <module>\r\nctfd_1   |     app = create_app()\r\nctfd_1   |   File \"/opt/CTFd/CTFd/__init__.py\", line 180, in create_app\r\nctfd_1   |     if \",\" in reverse_proxy:\r\nctfd_1   | TypeError: argument of type 'bool' is not iterable\r\nctfd_1   | Traceback (most recent call last):\r\nctfd_1   |   File \"manage.py\", line 9, in <module>\r\nctfd_1   |     app = create_app()\r\nctfd_1   |   File \"/opt/CTFd/CTFd/__init__.py\", line 180, in create_app\r\nctfd_1   |     if \",\" in reverse_proxy:\r\nctfd_1   | TypeError: argument of type 'bool' is not iterable\r\nctfd_ctfd_1 exited with code 1\r\n```\r\n\r\nI have a patch for this ready to go and will make a pull request.\r\n\n", "before_files": [{"content": "import datetime\nimport os\nimport sys\nfrom distutils.version import StrictVersion\n\nfrom flask import Flask, Request\nfrom flask_migrate import upgrade\nfrom jinja2 import FileSystemLoader\nfrom jinja2.sandbox import SandboxedEnvironment\nfrom six.moves import input\nfrom werkzeug.middleware.proxy_fix import ProxyFix\nfrom werkzeug.utils import cached_property\n\nfrom CTFd import utils\nfrom CTFd.plugins import init_plugins\nfrom CTFd.utils.crypto import sha256\nfrom CTFd.utils.initialization import (\n    init_events,\n    init_logs,\n    init_request_processors,\n    init_template_filters,\n    init_template_globals,\n)\nfrom CTFd.utils.migrations import create_database, migrations, stamp_latest_revision\nfrom CTFd.utils.sessions import CachingSessionInterface\nfrom CTFd.utils.updates import update_check\n\n# Hack to support Unicode in Python 2 properly\nif sys.version_info[0] < 3:\n    reload(sys)  # noqa: F821\n    sys.setdefaultencoding(\"utf-8\")\n\n__version__ = \"2.3.1\"\n\n\nclass CTFdRequest(Request):\n    @cached_property\n    def path(self):\n        \"\"\"\n        Hijack the original Flask request path because it does not account for subdirectory deployments in an intuitive\n        manner. We append script_root so that the path always points to the full path as seen in the browser.\n        e.g. /subdirectory/path/route vs /path/route\n\n        :return: string\n        \"\"\"\n        return self.script_root + super(CTFdRequest, self).path\n\n\nclass CTFdFlask(Flask):\n    def __init__(self, *args, **kwargs):\n        \"\"\"Overriden Jinja constructor setting a custom jinja_environment\"\"\"\n        self.jinja_environment = SandboxedBaseEnvironment\n        self.session_interface = CachingSessionInterface(key_prefix=\"session\")\n        self.request_class = CTFdRequest\n\n        # Store server start time\n        self.start_time = datetime.datetime.utcnow()\n\n        # Create generally unique run identifier\n        self.run_id = sha256(str(self.start_time))[0:8]\n        Flask.__init__(self, *args, **kwargs)\n\n    def create_jinja_environment(self):\n        \"\"\"Overridden jinja environment constructor\"\"\"\n        return super(CTFdFlask, self).create_jinja_environment()\n\n\nclass SandboxedBaseEnvironment(SandboxedEnvironment):\n    \"\"\"SandboxEnvironment that mimics the Flask BaseEnvironment\"\"\"\n\n    def __init__(self, app, **options):\n        if \"loader\" not in options:\n            options[\"loader\"] = app.create_global_jinja_loader()\n        # Disable cache entirely so that themes can be switched (#662)\n        # If the cache is enabled, switching themes will cause odd rendering errors\n        SandboxedEnvironment.__init__(self, cache_size=0, **options)\n        self.app = app\n\n\nclass ThemeLoader(FileSystemLoader):\n    \"\"\"Custom FileSystemLoader that switches themes based on the configuration value\"\"\"\n\n    def __init__(self, searchpath, encoding=\"utf-8\", followlinks=False):\n        super(ThemeLoader, self).__init__(searchpath, encoding, followlinks)\n        self.overriden_templates = {}\n\n    def get_source(self, environment, template):\n        # Check if the template has been overriden\n        if template in self.overriden_templates:\n            return self.overriden_templates[template], template, True\n\n        # Check if the template requested is for the admin panel\n        if template.startswith(\"admin/\"):\n            template = template[6:]  # Strip out admin/\n            template = \"/\".join([\"admin\", \"templates\", template])\n            return super(ThemeLoader, self).get_source(environment, template)\n\n        # Load regular theme data\n        theme = utils.get_config(\"ctf_theme\")\n        template = \"/\".join([theme, \"templates\", template])\n        return super(ThemeLoader, self).get_source(environment, template)\n\n\ndef confirm_upgrade():\n    if sys.stdin.isatty():\n        print(\"/*\\\\ CTFd has updated and must update the database! /*\\\\\")\n        print(\"/*\\\\ Please backup your database before proceeding! /*\\\\\")\n        print(\"/*\\\\ CTFd maintainers are not responsible for any data loss! /*\\\\\")\n        if input(\"Run database migrations (Y/N)\").lower().strip() == \"y\":\n            return True\n        else:\n            print(\"/*\\\\ Ignored database migrations... /*\\\\\")\n            return False\n    else:\n        return True\n\n\ndef run_upgrade():\n    upgrade()\n    utils.set_config(\"ctf_version\", __version__)\n\n\ndef create_app(config=\"CTFd.config.Config\"):\n    app = CTFdFlask(__name__)\n    with app.app_context():\n        app.config.from_object(config)\n\n        theme_loader = ThemeLoader(\n            os.path.join(app.root_path, \"themes\"), followlinks=True\n        )\n        app.jinja_loader = theme_loader\n\n        from CTFd.models import (  # noqa: F401\n            db,\n            Teams,\n            Solves,\n            Challenges,\n            Fails,\n            Flags,\n            Tags,\n            Files,\n            Tracking,\n        )\n\n        url = create_database()\n\n        # This allows any changes to the SQLALCHEMY_DATABASE_URI to get pushed back in\n        # This is mostly so we can force MySQL's charset\n        app.config[\"SQLALCHEMY_DATABASE_URI\"] = str(url)\n\n        # Register database\n        db.init_app(app)\n\n        # Register Flask-Migrate\n        migrations.init_app(app, db)\n\n        # Alembic sqlite support is lacking so we should just create_all anyway\n        if url.drivername.startswith(\"sqlite\"):\n            db.create_all()\n            stamp_latest_revision()\n        else:\n            # This creates tables instead of db.create_all()\n            # Allows migrations to happen properly\n            upgrade()\n\n        from CTFd.models import ma\n\n        ma.init_app(app)\n\n        app.db = db\n        app.VERSION = __version__\n\n        from CTFd.cache import cache\n\n        cache.init_app(app)\n        app.cache = cache\n\n        reverse_proxy = app.config.get(\"REVERSE_PROXY\")\n        if reverse_proxy:\n            if \",\" in reverse_proxy:\n                proxyfix_args = [int(i) for i in reverse_proxy.split(\",\")]\n                app.wsgi_app = ProxyFix(app.wsgi_app, None, *proxyfix_args)\n            else:\n                app.wsgi_app = ProxyFix(\n                    app.wsgi_app,\n                    num_proxies=None,\n                    x_for=1,\n                    x_proto=1,\n                    x_host=1,\n                    x_port=1,\n                    x_prefix=1,\n                )\n\n        version = utils.get_config(\"ctf_version\")\n\n        # Upgrading from an older version of CTFd\n        if version and (StrictVersion(version) < StrictVersion(__version__)):\n            if confirm_upgrade():\n                run_upgrade()\n            else:\n                exit()\n\n        if not version:\n            utils.set_config(\"ctf_version\", __version__)\n\n        if not utils.get_config(\"ctf_theme\"):\n            utils.set_config(\"ctf_theme\", \"core\")\n\n        update_check(force=True)\n\n        init_request_processors(app)\n        init_template_filters(app)\n        init_template_globals(app)\n\n        # Importing here allows tests to use sensible names (e.g. api instead of api_bp)\n        from CTFd.views import views\n        from CTFd.teams import teams\n        from CTFd.users import users\n        from CTFd.challenges import challenges\n        from CTFd.scoreboard import scoreboard\n        from CTFd.auth import auth\n        from CTFd.admin import admin\n        from CTFd.api import api\n        from CTFd.events import events\n        from CTFd.errors import page_not_found, forbidden, general_error, gateway_error\n\n        app.register_blueprint(views)\n        app.register_blueprint(teams)\n        app.register_blueprint(users)\n        app.register_blueprint(challenges)\n        app.register_blueprint(scoreboard)\n        app.register_blueprint(auth)\n        app.register_blueprint(api)\n        app.register_blueprint(events)\n\n        app.register_blueprint(admin)\n\n        app.register_error_handler(404, page_not_found)\n        app.register_error_handler(403, forbidden)\n        app.register_error_handler(500, general_error)\n        app.register_error_handler(502, gateway_error)\n\n        init_logs(app)\n        init_events(app)\n        init_plugins(app)\n\n        return app\n", "path": "CTFd/__init__.py"}], "after_files": [{"content": "import datetime\nimport os\nimport sys\nfrom distutils.version import StrictVersion\n\nfrom flask import Flask, Request\nfrom flask_migrate import upgrade\nfrom jinja2 import FileSystemLoader\nfrom jinja2.sandbox import SandboxedEnvironment\nfrom six.moves import input\nfrom werkzeug.middleware.proxy_fix import ProxyFix\nfrom werkzeug.utils import cached_property\n\nfrom CTFd import utils\nfrom CTFd.plugins import init_plugins\nfrom CTFd.utils.crypto import sha256\nfrom CTFd.utils.initialization import (\n    init_events,\n    init_logs,\n    init_request_processors,\n    init_template_filters,\n    init_template_globals,\n)\nfrom CTFd.utils.migrations import create_database, migrations, stamp_latest_revision\nfrom CTFd.utils.sessions import CachingSessionInterface\nfrom CTFd.utils.updates import update_check\n\n# Hack to support Unicode in Python 2 properly\nif sys.version_info[0] < 3:\n    reload(sys)  # noqa: F821\n    sys.setdefaultencoding(\"utf-8\")\n\n__version__ = \"2.3.1\"\n\n\nclass CTFdRequest(Request):\n    @cached_property\n    def path(self):\n        \"\"\"\n        Hijack the original Flask request path because it does not account for subdirectory deployments in an intuitive\n        manner. We append script_root so that the path always points to the full path as seen in the browser.\n        e.g. /subdirectory/path/route vs /path/route\n\n        :return: string\n        \"\"\"\n        return self.script_root + super(CTFdRequest, self).path\n\n\nclass CTFdFlask(Flask):\n    def __init__(self, *args, **kwargs):\n        \"\"\"Overriden Jinja constructor setting a custom jinja_environment\"\"\"\n        self.jinja_environment = SandboxedBaseEnvironment\n        self.session_interface = CachingSessionInterface(key_prefix=\"session\")\n        self.request_class = CTFdRequest\n\n        # Store server start time\n        self.start_time = datetime.datetime.utcnow()\n\n        # Create generally unique run identifier\n        self.run_id = sha256(str(self.start_time))[0:8]\n        Flask.__init__(self, *args, **kwargs)\n\n    def create_jinja_environment(self):\n        \"\"\"Overridden jinja environment constructor\"\"\"\n        return super(CTFdFlask, self).create_jinja_environment()\n\n\nclass SandboxedBaseEnvironment(SandboxedEnvironment):\n    \"\"\"SandboxEnvironment that mimics the Flask BaseEnvironment\"\"\"\n\n    def __init__(self, app, **options):\n        if \"loader\" not in options:\n            options[\"loader\"] = app.create_global_jinja_loader()\n        # Disable cache entirely so that themes can be switched (#662)\n        # If the cache is enabled, switching themes will cause odd rendering errors\n        SandboxedEnvironment.__init__(self, cache_size=0, **options)\n        self.app = app\n\n\nclass ThemeLoader(FileSystemLoader):\n    \"\"\"Custom FileSystemLoader that switches themes based on the configuration value\"\"\"\n\n    def __init__(self, searchpath, encoding=\"utf-8\", followlinks=False):\n        super(ThemeLoader, self).__init__(searchpath, encoding, followlinks)\n        self.overriden_templates = {}\n\n    def get_source(self, environment, template):\n        # Check if the template has been overriden\n        if template in self.overriden_templates:\n            return self.overriden_templates[template], template, True\n\n        # Check if the template requested is for the admin panel\n        if template.startswith(\"admin/\"):\n            template = template[6:]  # Strip out admin/\n            template = \"/\".join([\"admin\", \"templates\", template])\n            return super(ThemeLoader, self).get_source(environment, template)\n\n        # Load regular theme data\n        theme = utils.get_config(\"ctf_theme\")\n        template = \"/\".join([theme, \"templates\", template])\n        return super(ThemeLoader, self).get_source(environment, template)\n\n\ndef confirm_upgrade():\n    if sys.stdin.isatty():\n        print(\"/*\\\\ CTFd has updated and must update the database! /*\\\\\")\n        print(\"/*\\\\ Please backup your database before proceeding! /*\\\\\")\n        print(\"/*\\\\ CTFd maintainers are not responsible for any data loss! /*\\\\\")\n        if input(\"Run database migrations (Y/N)\").lower().strip() == \"y\":\n            return True\n        else:\n            print(\"/*\\\\ Ignored database migrations... /*\\\\\")\n            return False\n    else:\n        return True\n\n\ndef run_upgrade():\n    upgrade()\n    utils.set_config(\"ctf_version\", __version__)\n\n\ndef create_app(config=\"CTFd.config.Config\"):\n    app = CTFdFlask(__name__)\n    with app.app_context():\n        app.config.from_object(config)\n\n        theme_loader = ThemeLoader(\n            os.path.join(app.root_path, \"themes\"), followlinks=True\n        )\n        app.jinja_loader = theme_loader\n\n        from CTFd.models import (  # noqa: F401\n            db,\n            Teams,\n            Solves,\n            Challenges,\n            Fails,\n            Flags,\n            Tags,\n            Files,\n            Tracking,\n        )\n\n        url = create_database()\n\n        # This allows any changes to the SQLALCHEMY_DATABASE_URI to get pushed back in\n        # This is mostly so we can force MySQL's charset\n        app.config[\"SQLALCHEMY_DATABASE_URI\"] = str(url)\n\n        # Register database\n        db.init_app(app)\n\n        # Register Flask-Migrate\n        migrations.init_app(app, db)\n\n        # Alembic sqlite support is lacking so we should just create_all anyway\n        if url.drivername.startswith(\"sqlite\"):\n            db.create_all()\n            stamp_latest_revision()\n        else:\n            # This creates tables instead of db.create_all()\n            # Allows migrations to happen properly\n            upgrade()\n\n        from CTFd.models import ma\n\n        ma.init_app(app)\n\n        app.db = db\n        app.VERSION = __version__\n\n        from CTFd.cache import cache\n\n        cache.init_app(app)\n        app.cache = cache\n\n        reverse_proxy = app.config.get(\"REVERSE_PROXY\")\n        if reverse_proxy:\n            if type(reverse_proxy) is str and \",\" in reverse_proxy:\n                proxyfix_args = [int(i) for i in reverse_proxy.split(\",\")]\n                app.wsgi_app = ProxyFix(app.wsgi_app, None, *proxyfix_args)\n            else:\n                app.wsgi_app = ProxyFix(\n                    app.wsgi_app,\n                    num_proxies=None,\n                    x_for=1,\n                    x_proto=1,\n                    x_host=1,\n                    x_port=1,\n                    x_prefix=1,\n                )\n\n        version = utils.get_config(\"ctf_version\")\n\n        # Upgrading from an older version of CTFd\n        if version and (StrictVersion(version) < StrictVersion(__version__)):\n            if confirm_upgrade():\n                run_upgrade()\n            else:\n                exit()\n\n        if not version:\n            utils.set_config(\"ctf_version\", __version__)\n\n        if not utils.get_config(\"ctf_theme\"):\n            utils.set_config(\"ctf_theme\", \"core\")\n\n        update_check(force=True)\n\n        init_request_processors(app)\n        init_template_filters(app)\n        init_template_globals(app)\n\n        # Importing here allows tests to use sensible names (e.g. api instead of api_bp)\n        from CTFd.views import views\n        from CTFd.teams import teams\n        from CTFd.users import users\n        from CTFd.challenges import challenges\n        from CTFd.scoreboard import scoreboard\n        from CTFd.auth import auth\n        from CTFd.admin import admin\n        from CTFd.api import api\n        from CTFd.events import events\n        from CTFd.errors import page_not_found, forbidden, general_error, gateway_error\n\n        app.register_blueprint(views)\n        app.register_blueprint(teams)\n        app.register_blueprint(users)\n        app.register_blueprint(challenges)\n        app.register_blueprint(scoreboard)\n        app.register_blueprint(auth)\n        app.register_blueprint(api)\n        app.register_blueprint(events)\n\n        app.register_blueprint(admin)\n\n        app.register_error_handler(404, page_not_found)\n        app.register_error_handler(403, forbidden)\n        app.register_error_handler(500, general_error)\n        app.register_error_handler(502, gateway_error)\n\n        init_logs(app)\n        init_events(app)\n        init_plugins(app)\n\n        return app\n", "path": "CTFd/__init__.py"}]}