-
Notifications
You must be signed in to change notification settings - Fork 27
Expand file tree
/
Copy pathsettings.py
More file actions
executable file
·743 lines (633 loc) · 22.8 KB
/
Copy pathsettings.py
File metadata and controls
executable file
·743 lines (633 loc) · 22.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
import logging
import os
import subprocess
import sys
from pathlib import Path
import environs
import structlog
from corsheaders.defaults import default_headers
from django.core.exceptions import ImproperlyConfigured
from pythonjsonlogger import jsonlogger
env = environs.Env()
READ_DOT_ENV_FILE = env.bool("DJANGO_READ_DOT_ENV_FILE", default=False)
if READ_DOT_ENV_FILE:
env.read_env()
print("The .env file has been loaded. See config/settings.py for more information")
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = env.bool("DJANGO_DEBUG", default=False)
DEBUG_TOOLBAR = env.bool("DEBUG_TOOLBAR", default=False)
if DEBUG:
root = logging.getLogger()
root.setLevel(logging.INFO)
lh = logging.StreamHandler(sys.stderr)
root.addHandler(lh)
env.log_level("LOG_LEVEL", default="DEBUG")
# Whether or not we're in local development mode
LOCAL_DEVELOPMENT = env.bool("LOCAL_DEVELOPMENT", default=False)
CI = env.bool("CI", default=False)
if CI or LOCAL_DEVELOPMENT:
# This is the default value for the development environment.
# This enables the tests to run.
SITE_ID = 1
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = Path(__file__).parent.parent
APPS_DIR = BASE_DIR.joinpath("config")
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = env("SECRET_KEY")
host_list = env.list("ALLOWED_HOSTS", default="localhost")
ALLOWED_HOSTS = [el.strip() for el in host_list]
# Add 'web' for Docker container-to-container communication (Storybook -> Django)
if "web" not in ALLOWED_HOSTS:
ALLOWED_HOSTS.append("web")
INSTALLED_APPS = [
"django_admin_env_notice", # Third-party
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.humanize",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"django.contrib.sites",
"django.contrib.postgres",
]
# Third-party apps
INSTALLED_APPS += [
"waffle",
"anymail",
"rest_framework",
"corsheaders",
"django_extensions",
"health_check",
"health_check.db",
"health_check.contrib.celery",
"imagekit",
"import_export",
"django_countries",
# Allows authentication for Mailman
"oauth2_provider",
# Allauth dependencies:
"allauth",
"allauth.account",
"allauth.socialaccount",
"allauth.socialaccount.providers.github",
"allauth.socialaccount.providers.google",
"mptt",
"haystack",
"widget_tweaks",
]
# Wagtail Apps
INSTALLED_APPS += [
"wagtail.contrib.forms",
"wagtail.contrib.redirects",
"wagtail.embeds",
"wagtail.sites",
"wagtail.users",
"wagtail.snippets",
"wagtail.documents",
"wagtail.images",
"wagtail.search",
"wagtail.admin",
"wagtail",
"wagtailmarkdown",
"modelcluster",
"taggit",
]
# Pattern Library (for Storybook) — only when installed AND explicitly enabled.
# Defaults to True in DEBUG mode; set ENABLE_PATTERN_LIBRARY=false in production
# unless you intentionally want to expose the endpoint.
ENABLE_PATTERN_LIBRARY = env.bool("ENABLE_PATTERN_LIBRARY", default=DEBUG)
try:
if ENABLE_PATTERN_LIBRARY:
import pattern_library # noqa: F401
INSTALLED_APPS += ["pattern_library"]
except ImportError:
pass
# Pre-built Storybook static bundle (output of `yarn build-storybook`).
# Served at /storybook/ by StorybookView, staff-only.
STORYBOOK_ROOT = BASE_DIR / "var" / "storybook"
# Our Apps
INSTALLED_APPS += [
"ak",
"users",
"versions",
"libraries",
"mailing_list",
"marketing",
"news",
"reports",
"core",
"slack",
"testimonials",
"patches",
"asciidoctor_sandbox",
]
# django-pattern-library settings (used by storybook-django)
PATTERN_LIBRARY = {
"SECTIONS": (
("v3/includes", ["v3/includes"]),
("v3/examples", ["v3/examples"]),
("includes", ["includes"]),
),
"TEMPLATE_SUFFIX": ".html",
"PATTERN_BASE_TEMPLATE_NAME": "patterns/base.html",
"BASE_TEMPLATE_NAMES": ["patterns/base.html"],
}
AUTH_USER_MODEL = "users.User"
CSRF_COOKIE_HTTPONLY = True
# See https://docs.djangoproject.com/en/4.2/ref/settings/#csrf-trusted-origins
csrf_trusted_origins = env.list(
"CSRF_TRUSTED_ORIGINS", default=["http://0.0.0.0", "http://localhost"]
)
CSRF_TRUSTED_ORIGINS = [el.strip() for el in csrf_trusted_origins]
MIDDLEWARE = [
"corsheaders.middleware.CorsMiddleware",
"tracer.middleware.RequestID",
"django.middleware.security.SecurityMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"core.middleware.PatternLibraryStaffMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
"allauth.account.middleware.AccountMiddleware",
"oauth2_provider.middleware.OAuth2TokenMiddleware",
"wagtail.contrib.redirects.middleware.RedirectMiddleware",
"waffle.middleware.WaffleMiddleware",
]
# django-waffle settings
WAFFLE_CREATE_MISSING_FLAGS = True
WAFFLE_FLAG_DEFAULT = False
if DEBUG:
# These are necessary to turn on Whitenoise which will serve our static
# files while doing local development
MIDDLEWARE.append("whitenoise.middleware.WhiteNoiseMiddleware")
WHITENOISE_USE_FINDERS = True
WHITENOISE_AUTOREFRESH = True
ROOT_URLCONF = "config.urls"
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": [
str(BASE_DIR.joinpath("templates")),
],
"OPTIONS": {
"context_processors": [
# Django Admin Env Notice
"django_admin_env_notice.context_processors.from_settings",
# Django stuff
"django.template.context_processors.debug",
"django.template.context_processors.request",
"django.contrib.auth.context_processors.auth",
"django.contrib.messages.context_processors.messages",
"core.context_processors.current_version",
"core.context_processors.selected_version",
"core.context_processors.active_nav_item",
"core.context_processors.header_context",
"core.context_processors.debug",
],
"loaders": [
"django.template.loaders.filesystem.Loader",
"django.template.loaders.app_directories.Loader",
],
},
}
]
WSGI_APPLICATION = "config.wsgi.application"
# Database
# https://docs.djangoproject.com/en/1.10/ref/settings/#databases
try:
DATABASES = {
"default": env.dj_db_url("DATABASE_URL"),
"hyperkitty": env.dj_db_url("HYPERKITTY_DATABASE_URL"),
}
except (ImproperlyConfigured, environs.EnvError):
DATABASES = {
"default": {
"ENGINE": "django_db_geventpool.backends.postgresql_psycopg2",
"HOST": env("PGHOST"),
"NAME": env("PGDATABASE"),
"PASSWORD": env("PGPASSWORD"),
"PORT": env.int("PGPORT", default=5432),
"USER": env("PGUSER"),
"CONN_MAX_AGE": 0,
"OPTIONS": {"MAX_CONNS": env("MAX_CONNECTIONS", default=20)},
},
"hyperkitty": {
"ENGINE": "django_db_geventpool.backends.postgresql_psycopg2",
"HOST": env("PGHOST"),
"NAME": env("HYPERKITTY_DATABASE_NAME", default=""),
"PASSWORD": env("PGPASSWORD"),
"PORT": env.int("PGPORT", default=5432),
"USER": env("PGUSER"),
"CONN_MAX_AGE": 0,
"OPTIONS": {"MAX_CONNS": env("MAX_CONNECTIONS", default=20)},
},
}
# Password validation
# Only used in production
AUTH_PASSWORD_VALIDATORS = [
{
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator"
},
{
"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
"OPTIONS": {"min_length": 9},
},
{"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator"},
{"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator"},
]
# Sessions
# Give each project their own session cookie name to avoid local development
# login conflicts
SESSION_COOKIE_NAME = "config-sessionid"
# Increase default cookie age from 2 to 12 weeks
SESSION_COOKIE_AGE = 60 * 60 * 24 * 7 * 12
# Internationalization
# https://docs.djangoproject.com/en/1.10/topics/i18n/
LANGUAGE_CODE = "en-us"
TIME_ZONE = "UTC"
USE_I18N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.10/howto/static-files/
# The relative URL of where we serve our static files from
STATIC_URL = "/static/"
# Additional directories from where we should collect static files from
STATICFILES_DIRS = [
BASE_DIR.joinpath("static"),
]
# Directory where collectstatic puts static files
STATIC_ROOT = str(BASE_DIR.joinpath("static_deploy"))
# Directory where uploaded media is saved.
MEDIA_ROOT = os.path.join(BASE_DIR, "media")
# Public URL at the browser
MEDIA_URL = "/media/"
# Logging setup
# Configure struct log
structlog.configure(
processors=[
structlog.stdlib.filter_by_level,
structlog.stdlib.add_logger_name,
structlog.stdlib.add_log_level,
structlog.stdlib.PositionalArgumentsFormatter(),
structlog.processors.TimeStamper(fmt="iso"),
structlog.processors.StackInfoRenderer(),
structlog.processors.format_exc_info,
structlog.processors.UnicodeDecoder(),
structlog.stdlib.render_to_log_kwargs,
],
context_class=dict,
logger_factory=structlog.stdlib.LoggerFactory(),
wrapper_class=structlog.stdlib.BoundLogger,
cache_logger_on_first_use=True,
)
# Configure Python logging
root = logging.getLogger()
root.setLevel(logging.INFO)
handler = logging.StreamHandler(sys.stdout)
handler.setFormatter(
jsonlogger.JsonFormatter(
"%(name)s %(levelname)s %(filename)s:%(lineno)d %(message)s"
)
)
if LOCAL_DEVELOPMENT:
handler.setFormatter(
logging.Formatter(
"%(asctime)s [%(levelname)s] %(filename)s:%(lineno)d - %(message)s",
datefmt="%Y-%m-%d %H:%M:%S",
)
)
root.addHandler(handler)
# Configure Redis
REDIS_HOST = env("REDIS_HOST", default="redis")
# Configure Celery
CELERY_BROKER_URL = f"redis://{REDIS_HOST}:6379"
CELERY_RESULT_BACKEND = f"redis://{REDIS_HOST}:6379"
CELERY_ACCEPT_CONTENT = ["application/json"]
CELERY_TASK_SERIALIZER = "json"
CELERY_RESULT_SERIALIZER = "json"
CELERY_TIMEZONE = "UTC"
CELERY_BROKER_TRANSPORT_OPTIONS = {
"max_connections": env.int("MAX_CELERY_CONNECTIONS", default=60)
}
CELERY_RESULT_BACKEND_THREAD_SAFE = True
CELERY_RESULT_EXTENDED = True
CELERY_TASK_TRACK_STARTED = True
CELERY_TASK_ALWAYS_EAGER = env("CELERY_TASK_ALWAYS_EAGER", False)
# Reduce large amount of logging in redis. Usually 1 day.
CELERY_TASK_RESULT_EXPIRES = 3600
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": f"redis://{REDIS_HOST}:6379",
},
"static_content": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": f"redis://{REDIS_HOST}:6379/2",
"TIMEOUT": env(
"STATIC_CACHE_TIMEOUT", default="60"
), # Cache timeout in seconds: 1 minute
},
}
ENABLE_DB_CACHE = env.bool("ENABLE_DB_CACHE", default=False)
# Default interval by which to clear the static content cache
# New method: "never" clear, just overwrite, so that the id
# field doesn't expand without bounds.
CLEAR_STATIC_CONTENT_CACHE_DAYS = 7000
# Hyperkitty
HYPERKITTY_DATABASE_NAME = env("HYPERKITTY_DATABASE_NAME", default="")
if HYPERKITTY_DATABASE_NAME:
HYPERKITTY_DATABASE_URL = "postgresql://{}:{}@{}:{}/{}".format(
DATABASES["default"]["USER"],
DATABASES["default"]["PASSWORD"],
DATABASES["default"]["HOST"],
DATABASES["default"]["PORT"],
HYPERKITTY_DATABASE_NAME,
)
else:
HYPERKITTY_DATABASE_URL = ""
MAILMAN_CORE_DATABASE = env("MAILMAN_CORE_DATABASE", default="unknown")
# Mailman API credentials
MAILMAN_REST_API_URL = env("MAILMAN_REST_API_URL", default="http://localhost:8001")
MAILMAN_REST_API_USER = env("MAILMAN_REST_API_USER", default="restadmin")
MAILMAN_REST_API_PASS = env("MAILMAN_REST_API_PASS", default="restpass")
# Fastly API credentials
FASTLY_SERVICE = env("FASTLY_SERVICE", default="empty")
FASTLY_SERVICE2 = env("FASTLY_SERVICE2", default="empty")
FASTLY_API_TOKEN = env("FASTLY_API_TOKEN", default="empty")
HAYSTACK_CONNECTIONS = {
"default": {
"ENGINE": "haystack.backends.simple_backend.SimpleEngine",
},
}
DEFAULT_AUTO_FIELD = "django.db.models.AutoField"
AUTHENTICATION_BACKENDS = (
"allauth.account.auth_backends.AuthenticationBackend",
"oauth2_provider.backends.OAuth2Backend",
)
# GitHub settings
GITHUB_TOKEN = env("GITHUB_TOKEN", default=None)
JDOODLE_API_CLIENT_ID = env("JDOODLE_API_CLIENT_ID", "")
JDOODLE_API_CLIENT_SECRET = env("JDOODLE_API_CLIENT_SECRET", "")
# Monday.com CRM integration
MONDAY_API_TOKEN = env("MONDAY_API_TOKEN", default="")
MONDAY_CONTACTS_BOARD_ID = env("MONDAY_CONTACTS_BOARD_ID", default="")
MONDAY_LEADS_BOARD_ID = env("MONDAY_LEADS_BOARD_ID", default="")
# Django Allauth settings
ACCOUNT_EMAIL_VERIFICATION = "mandatory"
LOGIN_REDIRECT_URL = "home"
ACCOUNT_LOGOUT_ON_GET = True
ACCOUNT_USER_MODEL_USERNAME_FIELD = None
ACCOUNT_LOGIN_METHODS = {"email"}
ACCOUNT_SIGNUP_FIELDS = ["email*", "password1*", "password2*"]
SOCIALACCOUNT_QUERY_EMAIL = True
SOCIALACCOUNT_LOGIN_ON_GET = True
ACCOUNT_UNIQUE_EMAIL = True
SOCIALACCOUNT_EMAIL_AUTHENTICATION = True
SOCIALACCOUNT_EMAIL_AUTHENTICATION_AUTO_CONNECT = True
# Allow us to override some of allauth's forms
ACCOUNT_FORMS = {
"reset_password_from_key": "users.forms.CustomResetPasswordFromKeyForm",
}
SOCIALACCOUNT_PROVIDERS = {
"google": {
"SCOPE": [
"profile",
"email",
],
"AUTH_PARAMS": {
"access_type": "online",
"prompt": "select_account",
},
"OAUTH_PKCE_ENABLED": True,
},
"github": {},
}
if LOCAL_DEVELOPMENT:
github_oauth_client_id = env("GITHUB_OAUTH_CLIENT_ID", default=None)
github_oauth_secret = env("GITHUB_OAUTH_CLIENT_SECRET", default=None)
if not github_oauth_client_id or not github_oauth_secret:
logging.warning("Github OAuth credentials not set")
else:
SOCIALACCOUNT_PROVIDERS["github"] = {
"APPS": [
{
"client_id": github_oauth_client_id,
"secret": github_oauth_secret,
}
]
}
google_oauth_client_id = env("GOOGLE_OAUTH_CLIENT_ID", default=None)
google_oauth_secret = env("GOOGLE_OAUTH_CLIENT_SECRET", default=None)
if not google_oauth_client_id or not google_oauth_secret:
logging.warning("Google OAuth credentials not set")
else:
SOCIALACCOUNT_PROVIDERS["google"]["APPS"] = [
{
"client_id": google_oauth_client_id,
"secret": google_oauth_secret,
},
]
ACCOUNT_DEFAULT_HTTP_PROTOCOL = "http"
if not LOCAL_DEVELOPMENT:
ACCOUNT_DEFAULT_HTTP_PROTOCOL = "https"
SECURE_PROXY_SSL_HEADER = (
"HTTP_X_FORWARDED_PROTO",
ACCOUNT_DEFAULT_HTTP_PROTOCOL,
)
# Admin banner configuration
ENV_NAME = env("ENVIRONMENT_NAME", default="Unknown Environment")
IMAGE_TAG = env("IMAGE_TAG", default="Unknown Version")
if LOCAL_DEVELOPMENT:
try:
output = subprocess.check_output(
["git", "describe", "--tags"], universal_newlines=True
)
IMAGE_TAG = str(output.strip())
except Exception:
print("WARNING: Unable to run git, unable to determine local image tag")
IMAGE_TAG = "UNKNOWN-VERSION"
ENVIRONMENT_NAME = f"{ENV_NAME} - {IMAGE_TAG}"
ENVIRONMENT_COLOR = "#718096" # Gray for unknown
if ENV_NAME == "Development Environment":
ENVIRONMENT_COLOR = "#38A169" # Green
elif ENV_NAME == "Production Environment":
ENVIRONMENT_COLOR = "#E53E3E"
# S3 Compatiable Storage Settings
if not LOCAL_DEVELOPMENT:
AWS_ACCESS_KEY_ID = env("AWS_ACCESS_KEY_ID", default="changeme")
AWS_SECRET_ACCESS_KEY = env("AWS_SECRET_ACCESS_KEY", default="changeme")
MEDIA_BUCKET_NAME = env("MEDIA_BUCKET_NAME", default="changeme")
AWS_STORAGE_BUCKET_NAME = MEDIA_BUCKET_NAME
AWS_S3_OBJECT_PARAMETERS = {"CacheControl": "max-age=86400"}
AWS_S3_USE_THREADS = False # boto3 threads deadlock under gevent
AWS_DEFAULT_ACL = None
AWS_S3_ENDPOINT_URL = env(
"AWS_S3_ENDPOINT_URL", default="https://sfo2.digitaloceanspaces.com"
)
AWS_S3_REGION_NAME = env("AWS_S3_REGION_NAME", default="sfo2")
STORAGES = {
"default": {"BACKEND": "core.storages.MediaStorage"},
"staticfiles": {
"BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage"
},
}
MEDIA_URL = f"{AWS_S3_ENDPOINT_URL}/{MEDIA_BUCKET_NAME}/"
# Staticly rendered content from S3 such as Antora docs, etc
STATIC_CONTENT_AWS_ACCESS_KEY_ID = env(
"STATIC_CONTENT_AWS_ACCESS_KEY_ID", default="changeme"
)
STATIC_CONTENT_AWS_SECRET_ACCESS_KEY = env(
"STATIC_CONTENT_AWS_SECRET_ACCESS_KEY", default="changeme"
)
STATIC_CONTENT_BUCKET_NAME = env("STATIC_CONTENT_BUCKET_NAME", default="changeme")
STATIC_CONTENT_REGION = env("STATIC_CONTENT_REGION", default="us-east-2")
STATIC_CONTENT_AWS_S3_ENDPOINT_URL = env(
"STATIC_CONTENT_AWS_S3_ENDPOINT_URL",
default="https://s3.dualstack.us-east-2.amazonaws.com",
)
# LinkPreview API Key
# LINK_PREVIEW_API_KEY = env(
# "LINK_PREVIEW_API_KEY", default="changeme"
# )
# JSON configuration of how we map static content in the S3 buckets to URL paths
STATIC_CONTENT_MAPPING = env(
"STATIC_CONTENT_MAPPING", default="stage_static_config.json"
)
# Markdown content
BASE_CONTENT = env("BOOST_CONTENT_DIRECTORY", "/website")
# News: list of users who are allowed to post without requiring moderation.
# This complements the 'moderator' Group that also have posting privileges.
NEWS_MODERATION_ALLOWLIST = [
# Add either a user's email address or a User instance PK. Mixing emails
# with PKs is safe since users.User's PKs are integers.
]
# EMAIL SETTINGS -- THESE NEED ADJUSTMENT WHEN DECIDED WHICH ESP WILL BE USED
EMAIL_HOST = "maildev"
EMAIL_PORT = 1025
DEFAULT_FROM_EMAIL = "boost@cppalliance.org"
SERVER_EMAIL = "errors@cppalliance.org"
# Deployed email configuration
if LOCAL_DEVELOPMENT:
EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
else:
EMAIL_BACKEND = "anymail.backends.mailgun.EmailBackend"
ANYMAIL = {
"MAILGUN_API_KEY": env("MAILGUN_API_KEY", default="changeme"),
"MAILGUN_SENDER_DOMAIN": env(
"MAILGUN_SENDER_DOMAIN", default="boost.revsys.dev"
),
}
CORS_ALLOW_ALL_ORIGINS = True
CORS_ALLOW_CREDENTIALS = True
SESSION_COOKIE_HTTPONLY = False
CORS_ALLOW_METHODS = (
"DELETE",
"GET",
"OPTIONS",
"PATCH",
"POST",
"PUT",
)
CORS_ALLOW_HEADERS = (
*default_headers,
"hx-request",
"hx-target",
"hx-current-url",
"credentials",
)
# Legacy Artifactory settings
# Please note that these settings are not used in the current version of the site,
# but are kept here for reference.
ARTIFACTORY_URL = env(
"ARTIFACTORY_URL", default="https://boostorg.jfrog.io/artifactory/api/storage/main/"
)
MIN_ARTIFACTORY_RELEASE = "boost-1.63.0"
# archives.boost.io settings
# This is the URL where the archives.boost.io site is hosted.
ARCHIVES_URL = env("ARCHIVES_URL", default="https://archives.boost.io/")
MIN_ARCHIVES_RELEASE = "boost-1.63.0"
# The min Boost version is the oldest version of Boost that our import scripts
# will retrieve.
MINIMUM_BOOST_VERSION = "1.16.1"
# The highest Boost version with its docs stored in S3
MAXIMUM_BOOST_DOCS_VERSION = "boost-1.30.2"
# In Progress Release Notes URL
RELEASE_NOTES_IN_PROGRESS_URL = "https://raw.githubusercontent.com/boostorg/website/master/users/history/in_progress.html"
RELEASE_NOTES_IN_PROGRESS_CACHE_KEY = "release-notes-in-progress"
# Boost Google Calendar
BOOST_CALENDAR = "5rorfm42nvmpt77ac0vult9iig@group.calendar.google.com"
CALENDAR_API_KEY = env("CALENDAR_API_KEY", default="changeme")
PLAUSIBLE_STATS_KEY = env("PLAUSIBLE_STATS_KEY", default="changeme")
EVENTS_CACHE_KEY = "homepage_events"
EVENTS_CACHE_TIMEOUT = 300 # 5 min
# OAuth settings
OAUTH_APP_NAME = (
"Boost OAuth Concept" # Stored in the admin; replicated for convenience
)
# Frame loading
X_FRAME_OPTIONS = "SAMEORIGIN"
SLACK_BOT_TOKEN = env("SLACK_BOT_TOKEN", default="")
ACCOUNT_DELETION_GRACE_PERIOD_DAYS = 10
if DEBUG_TOOLBAR:
INSTALLED_APPS += ["debug_toolbar"]
INTERNAL_IPS = ["127.0.0.1", "localhost"]
DEBUG_TOOLBAR_CONFIG = {
"SHOW_TOOLBAR_CALLBACK": lambda x: True,
"ROOT_TAG_EXTRA_ATTRS": "hx-preserve",
}
MIDDLEWARE.append("debug_toolbar.middleware.DebugToolbarMiddleware")
BOOST_BRANCHES = ["master", "develop"]
OPENROUTER_URL = "https://openrouter.ai/api/v1"
OPENROUTER_API_KEY = env("OPENROUTER_API_KEY")
ALGOLIA = {
"app_id": env("ALGOLIA_APP_ID", None),
"analytics_api_key": env("ALGOLIA_ANALYTICS_API_KEY", None),
"region": env("ALGOLIA_APP_REGION", "us"),
}
# Required by Wagtail
DATA_UPLOAD_MAX_NUMBER_FIELDS = 10_000
WAGTAIL_SITE_NAME = "Boost.org"
WAGTAILADMIN_BASE_URL = env("WAGTAILADMIN_BASE_URL", default="https://www.boost.org")
WAGTAILADMIN_NOTIFICATION_INCLUDE_SUPERUSERS = False
WAGTAILDOCS_EXTENSIONS = [
"csv",
"docx",
"key",
"odt",
"pdf",
"pptx",
"rtf",
"txt",
"xlsx",
"zip",
]
RICH_TEXT_FEATURES = [
"h1",
"h2",
"h3",
"bold",
"italic",
"link",
"ol",
"ul",
"code",
"blockquote",
]
WAGTAILMARKDOWN = {
"autodownload_fontawesome": True,
"allowed_tags": [], # optional. a list of HTML tags. e.g. ['div', 'p', 'a']
"allowed_styles": [], # optional. a list of styles
"allowed_attributes": {}, # optional. a dict with HTML tag as key and a list of attributes as value
"allowed_settings_mode": "extend", # optional. Possible values: "extend" or "override". Defaults to "extend".
"extensions": [], # optional. a list of python-markdown supported extensions
"extension_configs": {}, # optional. a dictionary with the extension name as key, and its configuration as value
"extensions_settings_mode": "extend", # optional. Possible values: "extend" or "override". Defaults to "extend".
"tab_length": 4, # optional. Sets the length of tabs used by python-markdown to render the output. This is the number of spaces used to replace with a tab character. Defaults to 4.
}
# DOWNSCALING PREFERRED VALUES
DOWNSCALED_IMAGE_HEIGHT = 720 # Preferred height to downscale to
DOWNSCALED_IMAGE_WIDTH = 1440 # preferred width to downscale to
DOWNSCALE_IMAGE_THRESHOLD = 1 * 1024 * 1024 # threshold overwhich to downscale