Bump babel from 2.9.0 to 2.9.1
[mygpo.git] / mygpo / settings.py
blob33bd283f2f1e8f1490027859532f9870d9d1c14b
1 import re
2 import sys
3 import os.path
4 import dj_database_url
7 try:
8 from psycopg2cffi import compat
10 compat.register()
11 except ImportError:
12 pass
15 import django
16 import six
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":
31 return None
32 return int(value)
35 DEBUG = get_bool("DEBUG", False)
37 ADMINS = re.findall(r"\s*([^<]+) <([^>]+)>\s*", os.getenv("ADMINS", ""))
39 MANAGERS = ADMINS
41 DATABASES = {
42 "default": dj_database_url.config(default="postgres://mygpo:mygpo@localhost/mygpo")
46 _USE_GEVENT = get_bool("USE_GEVENT", False)
47 if _USE_GEVENT:
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))
58 if _cache_used:
59 CACHES = {}
60 CACHES["default"] = {
61 "BACKEND": os.getenv(
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
72 # system time zone.
73 TIME_ZONE = "UTC"
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"
79 SITE_ID = 1
81 # If you set this to False, Django will make some optimizations so as not
82 # to load the internationalization machinery.
83 USE_I18N = True
86 # Static Files
88 STATIC_ROOT = "staticfiles"
89 STATIC_URL = "/static/"
91 STATICFILES_DIRS = (os.path.abspath(os.path.join(BASE_DIR, "..", "static")),)
94 # Media Files
96 MEDIA_ROOT = os.getenv(
97 "MEDIA_ROOT", os.path.abspath(os.path.join(BASE_DIR, "..", "media"))
100 MEDIA_URL = "/media/"
103 TEMPLATES = [
105 "BACKEND": "django.template.backends.django.DjangoTemplates",
106 "DIRS": [],
107 "OPTIONS": {
108 "debug": DEBUG,
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
124 # page after login
125 "django.template.context_processors.request",
127 "libraries": {"staticfiles": "django.templatetags.static"},
128 "loaders": [
130 "django.template.loaders.cached.Loader",
131 ["django.template.loaders.app_directories.Loader"],
139 MIDDLEWARE = [
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"
150 INSTALLED_APPS = [
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",
162 "mygpo.core",
163 "mygpo.podcasts",
164 "mygpo.chapters",
165 "mygpo.search",
166 "mygpo.users",
167 "mygpo.api",
168 "mygpo.web",
169 "mygpo.publisher",
170 "mygpo.subscriptions",
171 "mygpo.history",
172 "mygpo.favorites",
173 "mygpo.usersettings",
174 "mygpo.data",
175 "mygpo.userfeeds",
176 "mygpo.suggestions",
177 "mygpo.directory",
178 "mygpo.categories",
179 "mygpo.episodestates",
180 "mygpo.maintenance",
181 "mygpo.share",
182 "mygpo.administration",
183 "mygpo.pubsub",
184 "mygpo.podcastlists",
185 "mygpo.votes",
188 try:
189 if DEBUG:
190 import debug_toolbar
192 INSTALLED_APPS += ["debug_toolbar"]
193 MIDDLEWARE += ["debug_toolbar.middleware.DebugToolbarMiddleware"]
195 except ImportError:
196 pass
199 try:
200 if DEBUG:
201 import django_extensions
203 INSTALLED_APPS += ["django_extensions"]
205 except ImportError:
206 pass
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]:
240 SECRET_KEY = "test"
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 = ["*"]
256 LOGGING = {
257 "version": 1,
258 "disable_existing_loggers": False,
259 "formatters": {
260 "verbose": {"format": "%(asctime)s %(name)s %(levelname)s %(message)s"}
262 "filters": {"require_debug_false": {"()": "django.utils.log.RequireDebugFalse"}},
263 "handlers": {
264 "console": {
265 "level": os.getenv("LOGGING_CONSOLE_LEVEL", "DEBUG"),
266 "class": "logging.StreamHandler",
267 "formatter": "verbose",
269 "mail_admins": {
270 "level": "ERROR",
271 "filters": ["require_debug_false"],
272 "class": "django.utils.log.AdminEmailHandler",
275 "loggers": {
276 "django": {
277 "handlers": os.getenv("LOGGING_DJANGO_HANDLERS", "console").split(),
278 "propagate": True,
279 "level": os.getenv("LOGGING_DJANGO_LEVEL", "WARN"),
281 "mygpo": {
282 "handlers": os.getenv("LOGGING_MYGPO_HANDLERS", "console").split(),
283 "level": os.getenv("LOGGING_MYGPO_LEVEL", "INFO"),
285 "celery": {
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))
294 if _use_log_file:
295 LOGGING["handlers"]["file"] = {
296 "level": "INFO",
297 "class": "logging.handlers.RotatingFileHandler",
298 "filename": os.getenv("LOGGING_FILENAME"),
299 "maxBytes": 10_000_000,
300 "backupCount": 10,
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", "")
344 ### Celery
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"]
354 ### Google API
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
367 # will be deleted
368 ACTIVATION_VALID_DAYS = int(os.getenv("ACTIVATION_VALID_DAYS", 10))
371 OPBEAT = {
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
395 # responses
396 QUERY_LENGTH_CUTOFF = int(os.getenv("QUERY_LENGTH_CUTOFF", 3))
398 ### Sentry
400 try:
401 import sentry_sdk
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", "")
408 if not sentry_dsn:
409 raise ValueError("Could not set up sentry because " "SENTRY_DSN is not set")
411 sentry_sdk.init(
412 dsn=sentry_dsn,
413 integrations=[DjangoIntegration(), CeleryIntegration(), RedisIntegration()],
414 send_default_pii=True,
417 except (ImportError, ValueError):
418 pass