8 from psycopg2cffi
import compat
18 django
.utils
.six
= six
20 BASE_DIR
= os
.path
.dirname(os
.path
.abspath(__file__
))
23 def get_bool(name
, default
):
24 return os
.getenv(name
, str(default
)).lower() == "true"
27 def get_intOrNone(name
, default
):
28 """ Parses the env variable, accepts ints and literal None"""
29 value
= os
.getenv(name
, str(default
))
30 if value
.lower() == "none":
35 DEBUG
= get_bool("DEBUG", False)
37 ADMINS
= re
.findall(r
"\s*([^<]+) <([^>]+)>\s*", os
.getenv("ADMINS", ""))
42 "default": dj_database_url
.config(default
="postgres://mygpo:mygpo@localhost/mygpo")
46 _USE_GEVENT
= get_bool("USE_GEVENT", False)
48 # see https://github.com/jneight/django-db-geventpool
49 default
= DATABASES
["default"]
50 default
["ENGINE"] = ("django_db_geventpool.backends.postgresql_psycopg2",)
51 default
["CONN_MAX_AGE"] = 0
52 options
= default
.get("OPTIONS", {})
53 options
["MAX_CONNS"] = 20
56 _cache_used
= bool(os
.getenv("CACHE_BACKEND", False))
62 "CACHE_BACKEND", "django.core.cache.backends.memcached.MemcachedCache"
64 "LOCATION": os
.getenv("CACHE_LOCATION"),
68 # Local time zone for this installation. Choices can be found here:
69 # http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
70 # although not all choices may be available on all operating systems.
71 # If running in a Windows environment this must be set to the same as your
75 # Language code for this installation. All choices can be found here:
76 # http://www.i18nguy.com/unicode/language-identifiers.html
77 LANGUAGE_CODE
= "en-us"
81 # If you set this to False, Django will make some optimizations so as not
82 # to load the internationalization machinery.
88 STATIC_ROOT
= "staticfiles"
89 STATIC_URL
= "/static/"
91 STATICFILES_DIRS
= (os
.path
.abspath(os
.path
.join(BASE_DIR
, "..", "static")),)
96 MEDIA_ROOT
= os
.getenv(
97 "MEDIA_ROOT", os
.path
.abspath(os
.path
.join(BASE_DIR
, "..", "media"))
100 MEDIA_URL
= "/media/"
105 "BACKEND": "django.template.backends.django.DjangoTemplates",
109 "context_processors": [
110 "django.contrib.auth.context_processors.auth",
111 "django.template.context_processors.debug",
112 "django.template.context_processors.i18n",
113 "django.template.context_processors.media",
114 "django.template.context_processors.static",
115 "django.template.context_processors.tz",
116 "django.contrib.messages.context_processors.messages",
117 "mygpo.web.google.analytics",
118 "mygpo.web.google.adsense",
119 # make the debug variable available in templates
120 # https://docs.djangoproject.com/en/dev/ref/templates/api/#django-core-context-processors-debug
121 "django.template.context_processors.debug",
122 # required so that the request obj can be accessed from
123 # templates. this is used to direct users to previous
125 "django.template.context_processors.request",
127 "libraries": {"staticfiles": "django.templatetags.static"},
130 "django.template.loaders.cached.Loader",
131 ["django.template.loaders.app_directories.Loader"],
140 "django.middleware.common.CommonMiddleware",
141 "django.middleware.csrf.CsrfViewMiddleware",
142 "django.contrib.sessions.middleware.SessionMiddleware",
143 "django.contrib.auth.middleware.AuthenticationMiddleware",
144 "django.middleware.locale.LocaleMiddleware",
145 "django.contrib.messages.middleware.MessageMiddleware",
148 ROOT_URLCONF
= "mygpo.urls"
151 "django.contrib.contenttypes",
152 "django.contrib.messages",
153 "django.contrib.admin",
154 "django.contrib.humanize",
155 "django.contrib.auth",
156 "django.contrib.sessions",
157 "django.contrib.staticfiles",
158 "django.contrib.sites",
159 "django.contrib.postgres",
160 "django_celery_results",
161 "django_celery_beat",
170 "mygpo.subscriptions",
173 "mygpo.usersettings",
179 "mygpo.episodestates",
182 "mygpo.administration",
184 "mygpo.podcastlists",
192 INSTALLED_APPS
+= ["debug_toolbar"]
193 MIDDLEWARE
+= ["debug_toolbar.middleware.DebugToolbarMiddleware"]
201 import django_extensions
203 INSTALLED_APPS
+= ["django_extensions"]
209 ACCOUNT_ACTIVATION_DAYS
= int(os
.getenv("ACCOUNT_ACTIVATION_DAYS", 7))
211 AUTHENTICATION_BACKENDS
= (
212 "mygpo.users.backend.CaseInsensitiveModelBackend",
213 "mygpo.web.auth.EmailAuthenticationBackend",
216 SESSION_ENGINE
= "django.contrib.sessions.backends.cached_db"
218 # TODO: use (default) JSON serializer for security
219 # this would currently fail as we're (de)serializing datetime objects
220 # https://docs.djangoproject.com/en/1.5/topics/http/sessions/#session-serialization
221 SESSION_SERIALIZER
= "django.contrib.sessions.serializers.PickleSerializer"
224 MESSAGE_STORAGE
= "django.contrib.messages.storage.session.SessionStorage"
226 USER_CLASS
= "mygpo.users.models.User"
228 LOGIN_URL
= "/login/"
230 CSRF_FAILURE_VIEW
= "mygpo.web.views.csrf_failure"
233 DEFAULT_FROM_EMAIL
= os
.getenv("DEFAULT_FROM_EMAIL", "")
235 SERVER_EMAIL
= os
.getenv("SERVER_EMAIL", DEFAULT_FROM_EMAIL
)
237 SECRET_KEY
= os
.getenv("SECRET_KEY", "")
239 if "pytest" in sys
.argv
[0]:
242 GOOGLE_ANALYTICS_PROPERTY_ID
= os
.getenv("GOOGLE_ANALYTICS_PROPERTY_ID", "")
244 DIRECTORY_EXCLUDED_TAGS
= os
.getenv("DIRECTORY_EXCLUDED_TAGS", "").split()
246 FLICKR_API_KEY
= os
.getenv("FLICKR_API_KEY", "")
248 SOUNDCLOUD_CONSUMER_KEY
= os
.getenv("SOUNDCLOUD_CONSUMER_KEY", "")
250 MAINTENANCE
= get_bool("MAINTENANCE", False)
253 ALLOWED_HOSTS
= ["*"]
258 "disable_existing_loggers": False,
260 "verbose": {"format": "%(asctime)s %(name)s %(levelname)s %(message)s"}
262 "filters": {"require_debug_false": {"()": "django.utils.log.RequireDebugFalse"}},
265 "level": os
.getenv("LOGGING_CONSOLE_LEVEL", "DEBUG"),
266 "class": "logging.StreamHandler",
267 "formatter": "verbose",
271 "filters": ["require_debug_false"],
272 "class": "django.utils.log.AdminEmailHandler",
277 "handlers": os
.getenv("LOGGING_DJANGO_HANDLERS", "console").split(),
279 "level": os
.getenv("LOGGING_DJANGO_LEVEL", "WARN"),
282 "handlers": os
.getenv("LOGGING_MYGPO_HANDLERS", "console").split(),
283 "level": os
.getenv("LOGGING_MYGPO_LEVEL", "INFO"),
286 "handlers": os
.getenv("LOGGING_CELERY_HANDLERS", "console").split(),
287 "level": os
.getenv("LOGGING_CELERY_LEVEL", "DEBUG"),
292 _use_log_file
= bool(os
.getenv("LOGGING_FILENAME", False))
295 LOGGING
["handlers"]["file"] = {
297 "class": "logging.handlers.RotatingFileHandler",
298 "filename": os
.getenv("LOGGING_FILENAME"),
299 "maxBytes": 10_000_000,
301 "formatter": "verbose",
305 DATA_UPLOAD_MAX_MEMORY_SIZE
= get_intOrNone("DATA_UPLOAD_MAX_MEMORY_SIZE", None)
308 # minimum number of subscribers a podcast must have to be assigned a slug
309 PODCAST_SLUG_SUBSCRIBER_LIMIT
= int(os
.getenv("PODCAST_SLUG_SUBSCRIBER_LIMIT", 10))
311 # minimum number of subscribers that a podcast needs to "push" one of its
312 # categories to the top
313 MIN_SUBSCRIBERS_CATEGORY
= int(os
.getenv("MIN_SUBSCRIBERS_CATEGORY", 10))
315 # maximum number of episode actions that the API processes immediatelly before
316 # returning the response. Larger requests will be handled in background.
317 # Handler can be set to None to disable
318 API_ACTIONS_MAX_NONBG
= get_intOrNone("API_ACTIONS_MAX_NONBG", 100)
319 API_ACTIONS_BG_HANDLER
= "mygpo.api.tasks.episode_actions_celery_handler"
322 ADSENSE_CLIENT
= os
.getenv("ADSENSE_CLIENT", "")
324 ADSENSE_SLOT_BOTTOM
= os
.getenv("ADSENSE_SLOT_BOTTOM", "")
326 # we're running behind a proxy that sets the X-Forwarded-Proto header correctly
327 # see https://docs.djangoproject.com/en/dev/ref/settings/#secure-proxy-ssl-header
328 SECURE_PROXY_SSL_HEADER
= ("HTTP_X_FORWARDED_PROTO", "https")
331 # enabled access to staff-only areas with ?staff=<STAFF_TOKEN>
332 STAFF_TOKEN
= os
.getenv("STAFF_TOKEN", None)
334 # The User-Agent string used for outgoing HTTP requests
335 USER_AGENT
= "gpodder.net (+https://github.com/gpodder/mygpo)"
337 # Base URL of the website that is used if the actually used parameters is not
338 # available. Request handlers, for example, can access the requested domain.
339 # Code that runs in background can not do this, and therefore requires a
340 # default value. This should be set to something like 'http://example.com'
341 DEFAULT_BASE_URL
= os
.getenv("DEFAULT_BASE_URL", "")
346 CELERY_BROKER_URL
= os
.getenv("BROKER_URL", "redis://localhost")
347 CELERY_RESULT_BACKEND
= "django-db"
349 CELERY_RESULT_EXPIRES
= 60 * 60 # 1h expiry time in seconds
351 CELERY_ACCEPT_CONTENT
= ["json"]
356 GOOGLE_CLIENT_ID
= os
.getenv("GOOGLE_CLIENT_ID", "")
357 GOOGLE_CLIENT_SECRET
= os
.getenv("GOOGLE_CLIENT_SECRET", "")
359 # URL where users of the site can get support
360 SUPPORT_URL
= os
.getenv("SUPPORT_URL", "")
363 FEEDSERVICE_URL
= os
.getenv("FEEDSERVICE_URL", "http://feeds.gpodder.net/")
366 # time for how long an activation is valid; after that, an unactivated user
368 ACTIVATION_VALID_DAYS
= int(os
.getenv("ACTIVATION_VALID_DAYS", 10))
372 "ORGANIZATION_ID": os
.getenv("OPBEAT_ORGANIZATION_ID", ""),
373 "APP_ID": os
.getenv("OPBEAT_APP_ID", ""),
374 "SECRET_TOKEN": os
.getenv("OPBEAT_SECRET_TOKEN", ""),
377 LOCALE_PATHS
= [os
.path
.abspath(os
.path
.join(BASE_DIR
, "locale"))]
379 INTERNAL_IPS
= os
.getenv("INTERNAL_IPS", "").split()
381 EMAIL_BACKEND
= os
.getenv(
382 "EMAIL_BACKEND", "django.core.mail.backends.smtp.EmailBackend"
385 PODCAST_AD_ID
= os
.getenv("PODCAST_AD_ID")
388 MAX_EPISODE_ACTIONS
= int(os
.getenv("MAX_EPISODE_ACTIONS", 1000))
390 SEARCH_CUTOFF
= float(os
.getenv("SEARCH_CUTOFF", 0.3))
392 # Maximum non-whitespace length of search query
393 # If length of query is shorter than QUERY_LENGTH_CUTOFF, no results
394 # will be returned to avoid a server timeout due to too many possible
396 QUERY_LENGTH_CUTOFF
= int(os
.getenv("QUERY_LENGTH_CUTOFF", 3))
402 from sentry_sdk
.integrations
.django
import DjangoIntegration
403 from sentry_sdk
.integrations
.celery
import CeleryIntegration
404 from sentry_sdk
.integrations
.redis
import RedisIntegration
406 # Sentry Data Source Name (DSN)
407 sentry_dsn
= os
.getenv("SENTRY_DSN", "")
409 raise ValueError("Could not set up sentry because " "SENTRY_DSN is not set")
413 integrations
=[DjangoIntegration(), CeleryIntegration(), RedisIntegration()],
414 send_default_pii
=True,
417 except (ImportError, ValueError):