text: """ Config manager """
  import os
  import yaml
  from flask import current_app
  from werkzeug.local import LocalProxy
  import logging
  
  
  cfg_defaults = {  # key => default value
          "site": {
              "name": 'Throat',
              "lema": 'Throat: Open discussion ;D',
              "copyright": "Umbrella Corp",
              "enable_totp": False,
              "placeholder_account": "Site",
              "sub_prefix": 's',
              "enable_security_question": False,
              "cas_authorized_hosts": [],
              "allow_uploads": False,
              "allow_video_uploads": True,
              "upload_max_size": 16777216,
              "upload_min_level": 0,
              "enable_chat": True,
              "sitelog_public": True,
              "force_sublog_public": True,
  
              "changelog_sub": None,
              "btc_address": None,
              "xmr_address": None,
  
              "title_edit_timeout": 300,
  
              "sub_creation_min_level": 2,
              "sub_creation_admin_only": False,
              "sub_ownership_limit": 20,
              "edit_history": False,
              "anonymous_modding": False,
  
              "daily_sub_posting_limit": 10,
              "daily_site_posting_limit": 25,
  
              # Removing things from this list will work but adding will not.
              # See Expando.js.
              "expando_sites": ['hooktube.com', 'www.hooktube.com', 'youtube.com',
                                'www.youtube.com', 'youtu.be', 'gfycat.com',
                                'streamja.com', 'streamable.com', 'vimeo.com',
                                'vine.co', 'instaud.io'],
  
              "footer": {
                  "links": {
                      "ToS": "/wiki/tos",
                      "Privacy": "/wiki/privacy"
                  }
              },
  
              "archive_post_after": 60,
              "trusted_proxy_count": 0
          },
          "auth": {
              "provider": 'LOCAL',
              "require_valid_emails": False,
              "keycloak": {}
          },
          "cache": {
              "type": "null"
          },
          "mail": {},
          "storage": {
              "provider": 'LOCAL',
              "acl": "private",
              "server": False,
              "server_url": '/files/',
              "thumbnails": {
                  "path": './thumbs',
                  "url": 'https://thumbnails.shitposting.space/',
              },
              "uploads": {
                  "path": './stor',
                  "url": 'https://useruploads.shitposting.space/',
              }
          },
          "app": {
              "redis_url": 'redis://127.0.0.1:6379',
              "secret_key": 'yS\x1c\x88\xd7\xb5\xb0\xdc\t:kO\r\xf0D{"Y\x1f\xbc^\xad',
              "force_https": False,
              "debug": True,
              "development": False,
              "wtf_csrf_time_limit": None,
              "max_content_length": 10485760,  # 10mb
              "fallback_language": "en",
              "testing": False
          },
          "aws": {},
          "database": {
              "autoconnect": False
          },
          "ratelimit": {
              "default": "60/minute"
          },
      }
  
  
  class Map(dict):
      """ A dictionary object whose keys are accessable as attributes. """
      def __init__(self, sdict, defaults, use_environment=True, prefix=''):
          """Create a Map from the dictionary sdict, with missing values filled
          in from defaults. If a non-empty prefix string is supplied,
          and any environment variables exist beginning with that
          prefix, add those values to the dictionary overwriting
          anything in sdict or default.
          """
          super(Map, self).__init__(dict(sdict))
          self.prefix = ('' if prefix == '' else prefix + '_').upper()
  
          # If any values are missing, copy them from defaults.
          # Turn all subdictionaries into Maps as well.
          for key, val in defaults.items():
              if isinstance(val, dict):
                  existing = self.get(key, {})
                  if existing is None:
                      existing = {}
                  elif not isinstance(existing, dict):
                      raise TypeError(f'Value found where dict expected at {prefix}{key} in config.yaml')
                  self[key] = Map(existing, val, use_environment, f'{self.prefix}{key}')
              elif key not in self.keys():
                  self[key] = val
  
         # Look for environment variables that override values or add additional values.
          if self.prefix != '' and use_environment:
              for var in os.environ.keys():
                  if var.startswith(self.prefix):
                      self[var[len(self.prefix):].lower()] = os.environ.get(var)
  
      def __getattr__(self, attr):
          return self[attr]
  
  
  class Config(Map):
      """ Main config object """
      def __init__(self, config_filename=None, use_environment=True):
          if config_filename is None:
              cfg = {}
          else:
              with open(config_filename, 'r') as stream:
                  cfg = yaml.safe_load(stream)
  
          super(Config, self).__init__(cfg, cfg_defaults, use_environment=use_environment)
          self.check_storage_config()
          self.check_auth_config()
          self.check_ratelimit_config()
  
      def check_storage_config(self):
          """Adjust our storage config for compatibility with flask-cloudy."""
          storage = self.storage
          if storage.provider == 'LOCAL':
              # Make sure our storage paths are absolute.
              storage.thumbnails['path'] = os.path.abspath(storage.thumbnails['path'])
              storage.uploads['path'] = os.path.abspath(storage.uploads['path'])
              storage['container'] = storage.uploads['path']
              if storage.server:
                  if storage.uploads.path != storage.thumbnails.path:
                      logging.warning("Thumbnails will not be served by local server "
                                      "because thumbnails and uploads paths differ")
                  if storage['server_url'][-1] == '/':
                      # flask-cloudy does not want the trailing slash
                      self.storage['server_url'] = self.storage['server_url'][:-1]
  
          storage.acl = '' if storage.acl is None else storage.acl
  
          # This is not for flask-cloudy, just to make config more human-friendly
          ensure_trailing_slash(storage.thumbnails, 'url')
          ensure_trailing_slash(storage.uploads, 'url')
  
      def check_auth_config(self):
          ensure_trailing_slash(self.auth.keycloak, 'server')
  
      def check_ratelimit_config(self):
          if 'storage_url' not in self.ratelimit:
              self.ratelimit['storage_url'] = self.app.redis_url
  
      def get_flask_dict(self):
          flattened = {}
          for cpk in ['cache', 'mail', 'sendgrid', 'app', 'ratelimit']:
              if cpk in self.keys():
                  for i in self[cpk]:
                      if cpk == 'app':
                          key = i.upper()
                      else:
                          key = '{}_{}'.format(cpk, i).upper()
                      flattened[key] = self[cpk][i]
  
          # These values are used by flask-cloudy.
          for key in ['provider', 'key', 'secret', 'container', 'server', 'server_url']:
              if key in self.storage:
                  flattened[f'STORAGE_{key}'.upper()] = self.storage[key]
          return flattened
  
  
  def ensure_trailing_slash(d, key):
      """ Add a slash to the end of a dictionary entry if it doesn't already have one. """
      if key in d and d[key] and d[key][-1] != '/':
          d[key] = d[key] + '/'
  
  
  config = LocalProxy(lambda: current_app.config['THROAT_CONFIG'])
  