1
0
Fork 0

Fix template string syntax, formatting

This commit is contained in:
Reinout Meliesie 2025-03-11 01:19:29 +01:00
parent 6ed1025a8d
commit 0702731c7f
Signed by: zedfrigg
GPG key ID: 3AFCC06481308BC6
2 changed files with 295 additions and 334 deletions

View file

@ -1,7 +1,12 @@
""" models that will show up in django admin for superuser """ """ Models that will show up in Django admin for superuser """
from django.contrib import admin
from bookwyrm import models
admin.site.register(models.User)
admin.site.register(models.FederatedServer)
admin.site.register(models.Connector) from bookwyrm import models
from django . contrib import admin
admin . site . register ( models . User )
admin . site . register ( models . FederatedServer )
admin . site . register ( models . Connector )

View file

@ -1,11 +1,12 @@
""" bookwyrm settings and configuration """ """ Bookwyrm settings and configuration """
import os import os
import requests import requests
from django.core.exceptions import ImproperlyConfigured
from django.utils.translation import gettext_lazy as _ from django . core . exceptions import ImproperlyConfigured
from django . utils . translation import gettext_lazy as _
from environs import Env from environs import Env
from typing import AnyStr from typing import AnyStr
@ -43,225 +44,206 @@ EMAIL_USE_TLS = env . bool ( "EMAIL_USE_TLS" , True )
EMAIL_USE_SSL = env . bool ( "EMAIL_USE_SSL" , False ) EMAIL_USE_SSL = env . bool ( "EMAIL_USE_SSL" , False )
EMAIL_SENDER_NAME = env ( "EMAIL_SENDER_NAME" , "admin" ) EMAIL_SENDER_NAME = env ( "EMAIL_SENDER_NAME" , "admin" )
EMAIL_SENDER_DOMAIN = env ( "EMAIL_SENDER_DOMAIN" , DOMAIN ) EMAIL_SENDER_DOMAIN = env ( "EMAIL_SENDER_DOMAIN" , DOMAIN )
EMAIL_SENDER = f "{EMAIL_SENDER_NAME}@{EMAIL_SENDER_DOMAIN}" EMAIL_SENDER = f"{EMAIL_SENDER_NAME}@{EMAIL_SENDER_DOMAIN}"
# Build paths inside the project like this: os.path.join(BASE_DIR, ...) # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR: AnyStr = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) BASE_DIR : AnyStr = os . path . dirname ( os . path . dirname ( os . path . abspath (__file__) ) )
LOCALE_PATHS = [ LOCALE_PATHS = [ os . path . join ( BASE_DIR , "locale" ) ]
os.path.join(BASE_DIR, "locale"), LANGUAGE_COOKIE_NAME = env . str ( "LANGUAGE_COOKIE_NAME" , "django_language" )
]
LANGUAGE_COOKIE_NAME = env.str("LANGUAGE_COOKIE_NAME", "django_language")
STATIC_ROOT = os.path.join(BASE_DIR, env("STATIC_ROOT", "static")) STATIC_ROOT = os . path . join ( BASE_DIR , env ( "STATIC_ROOT" , "static" ) )
MEDIA_ROOT = os.path.join(BASE_DIR, env("MEDIA_ROOT", "images")) MEDIA_ROOT = os . path . join ( BASE_DIR , env ( "MEDIA_ROOT" , "images" ) )
DEFAULT_AUTO_FIELD = "django.db.models.AutoField" DEFAULT_AUTO_FIELD = "django.db.models.AutoField"
# Preview image # Preview image
ENABLE_PREVIEW_IMAGES = env.bool("ENABLE_PREVIEW_IMAGES", False) ENABLE_PREVIEW_IMAGES = env . bool ( "ENABLE_PREVIEW_IMAGES" , False )
PREVIEW_BG_COLOR = env.str("PREVIEW_BG_COLOR", "use_dominant_color_light") PREVIEW_BG_COLOR = env . str ( "PREVIEW_BG_COLOR" , "use_dominant_color_light" )
PREVIEW_TEXT_COLOR = env.str("PREVIEW_TEXT_COLOR", "#363636") PREVIEW_TEXT_COLOR = env . str ( "PREVIEW_TEXT_COLOR" , "#363636" )
PREVIEW_IMG_WIDTH = env.int("PREVIEW_IMG_WIDTH", 1200) PREVIEW_IMG_WIDTH = env . int ( "PREVIEW_IMG_WIDTH" , 1200 )
PREVIEW_IMG_HEIGHT = env.int("PREVIEW_IMG_HEIGHT", 630) PREVIEW_IMG_HEIGHT = env . int ( "PREVIEW_IMG_HEIGHT" , 630 )
PREVIEW_DEFAULT_COVER_COLOR = env.str("PREVIEW_DEFAULT_COVER_COLOR", "#002549") PREVIEW_DEFAULT_COVER_COLOR = env . str ( "PREVIEW_DEFAULT_COVER_COLOR" , "#002549" )
PREVIEW_DEFAULT_FONT = env.str("PREVIEW_DEFAULT_FONT", "Source Han Sans") PREVIEW_DEFAULT_FONT = env . str ( "PREVIEW_DEFAULT_FONT" , "Source Han Sans" )
FONTS = { FONTS = {
"Source Han Sans": { "Source Han Sans" : {
"directory": "source_han_sans", "directory" : "source_han_sans" ,
"filename": "SourceHanSans-VF.ttf.ttc", "filename" : "SourceHanSans-VF.ttf.ttc" ,
"url": "https://github.com/adobe-fonts/source-han-sans/raw/release/Variable/OTC/SourceHanSans-VF.ttf.ttc", "url" : "https://github.com/adobe-fonts/source-han-sans/raw/release/Variable/OTC/SourceHanSans-VF.ttf.ttc" ,
} }
} }
FONT_DIR = os.path.join(STATIC_ROOT, "fonts") FONT_DIR = os . path . join ( STATIC_ROOT , "fonts" )
# Quick-start development settings - unsuitable for production # Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/ # See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/
# SECURITY WARNING: don't run with debug turned on in production! # SECURITY WARNING: Don't run with debug turned on in production
DEBUG = env.bool("DEBUG", True) DEBUG = env . bool ( "DEBUG" , True )
USE_HTTPS = env.bool("USE_HTTPS", not DEBUG) USE_HTTPS = env . bool ( "USE_HTTPS" , not DEBUG )
# SECURITY WARNING: keep the secret key used in production secret! # SECURITY WARNING: Keep the secret key used in production secret
SECRET_KEY = env("SECRET_KEY") SECRET_KEY = env ("SECRET_KEY")
if not DEBUG and SECRET_KEY == "7(2w1sedok=aznpq)ta1mc4i%4h=xx@hxwx*o57ctsuml0x%fr": if not DEBUG and SECRET_KEY == "7(2w1sedok=aznpq)ta1mc4i%4h=xx@hxwx*o57ctsuml0x%fr" :
raise ImproperlyConfigured("You must change the SECRET_KEY env variable") raise ImproperlyConfigured ("You must change the SECRET_KEY env variable")
ALLOWED_HOSTS = env.list("ALLOWED_HOSTS", ["*"]) ALLOWED_HOSTS = env . list ( "ALLOWED_HOSTS" , [ "*" ] )
# Application definition # Application definition
INSTALLED_APPS = [ INSTALLED_APPS = [
"django.contrib.admin", "django.contrib.admin" ,
"django.contrib.auth", "django.contrib.auth" ,
"django.contrib.contenttypes", "django.contrib.contenttypes" ,
"django.contrib.sessions", "django.contrib.sessions" ,
"django.contrib.messages", "django.contrib.messages" ,
"django.contrib.staticfiles", "django.contrib.staticfiles" ,
"django.contrib.humanize", "django.contrib.humanize" ,
"oauth2_provider", "oauth2_provider" ,
"file_resubmit", "file_resubmit" ,
"sass_processor", "sass_processor" ,
"bookwyrm", "bookwyrm" ,
"celery", "celery" ,
"django_celery_beat", "django_celery_beat" ,
"imagekit", "imagekit" ,
"pgtrigger", "pgtrigger" ,
"storages", "storages" ,
] ]
MIDDLEWARE = [ MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware", "django.middleware.security.SecurityMiddleware" ,
"django.contrib.sessions.middleware.SessionMiddleware", "django.contrib.sessions.middleware.SessionMiddleware" ,
"django.middleware.locale.LocaleMiddleware", "django.middleware.locale.LocaleMiddleware" ,
"django.middleware.common.CommonMiddleware", "django.middleware.common.CommonMiddleware" ,
"django.middleware.csrf.CsrfViewMiddleware", "django.middleware.csrf.CsrfViewMiddleware" ,
"csp.middleware.CSPMiddleware", "csp.middleware.CSPMiddleware" ,
"django.contrib.auth.middleware.AuthenticationMiddleware", "django.contrib.auth.middleware.AuthenticationMiddleware" ,
"bookwyrm.middleware.TimezoneMiddleware", "bookwyrm.middleware.TimezoneMiddleware" ,
"bookwyrm.middleware.IPBlocklistMiddleware", "bookwyrm.middleware.IPBlocklistMiddleware" ,
"django.contrib.messages.middleware.MessageMiddleware", "django.contrib.messages.middleware.MessageMiddleware" ,
"django.middleware.clickjacking.XFrameOptionsMiddleware", "django.middleware.clickjacking.XFrameOptionsMiddleware" ,
"bookwyrm.middleware.FileTooBig", "bookwyrm.middleware.FileTooBig" ,
] ]
ROOT_URLCONF = "bookwyrm.urls" ROOT_URLCONF = "bookwyrm.urls"
TEMPLATES = [ TEMPLATES = [
{ {
"BACKEND": "django.template.backends.django.DjangoTemplates", "BACKEND" : "django.template.backends.django.DjangoTemplates" ,
"DIRS": ["templates"], "DIRS" : [ "templates" ] ,
"APP_DIRS": True, "APP_DIRS" : True ,
"OPTIONS": { "OPTIONS" : {
"context_processors": [ "context_processors" : [
"django.template.context_processors.debug", "django.template.context_processors.debug" ,
"django.template.context_processors.request", "django.template.context_processors.request" ,
"django.contrib.auth.context_processors.auth", "django.contrib.auth.context_processors.auth" ,
"django.contrib.messages.context_processors.messages", "django.contrib.messages.context_processors.messages" ,
"bookwyrm.context_processors.site_settings", "bookwyrm.context_processors.site_settings" ,
], ] ,
}, } ,
}, } ,
] ]
LOG_LEVEL = env("LOG_LEVEL", "INFO").upper() LOG_LEVEL = env ( "LOG_LEVEL" , "INFO" ) . upper ()
# Override aspects of the default handler to our taste # Override aspects of the default handler to our taste
# See https://docs.djangoproject.com/en/3.2/topics/logging/#default-logging-configuration # See https://docs.djangoproject.com/en/3.2/topics/logging/#default-logging-configuration for a reference to the defaults we're overriding
# for a reference to the defaults we're overriding # It seems that in order to override anything you have to include its entire dependency tree (handlers and filters) which makes this a bit verbose
#
# It seems that in order to override anything you have to include its
# entire dependency tree (handlers and filters) which makes this a
# bit verbose
LOGGING = { LOGGING = {
"version": 1, "version" : 1 ,
"disable_existing_loggers": False, "disable_existing_loggers" : False ,
"filters": { "filters" : {
# These are copied from the default configuration, required for # These are copied from the default configuration, required for implementing mail_admins below
# implementing mail_admins below "require_debug_false" : { "()" : "django.utils.log.RequireDebugFalse" } ,
"require_debug_false": { "require_debug_true": { "()" : "django.utils.log.RequireDebugTrue" } ,
"()": "django.utils.log.RequireDebugFalse", "ignore_missing_variable" : { "()" : "bookwyrm.utils.log.IgnoreVariableDoesNotExist" } ,
}, } ,
"require_debug_true": { "handlers" : {
"()": "django.utils.log.RequireDebugTrue", # Overrides the default handler to make it log to console regardless of the DEBUG setting (default is to not log to console if DEBUG=False)
}, "console" : {
"ignore_missing_variable": { "level" : LOG_LEVEL ,
"()": "bookwyrm.utils.log.IgnoreVariableDoesNotExist", "filters" : [ "ignore_missing_variable" ] ,
}, "class" : "logging.StreamHandler" ,
},
"handlers": {
# Overrides the default handler to make it log to console
# regardless of the DEBUG setting (default is to not log to
# console if DEBUG=False)
"console": {
"level": LOG_LEVEL,
"filters": ["ignore_missing_variable"],
"class": "logging.StreamHandler",
},
# This is copied as-is from the default logger, and is
# required for the django section below
"mail_admins": {
"level": "ERROR",
"filters": ["require_debug_false"],
"class": "django.utils.log.AdminEmailHandler",
},
},
"loggers": {
# Install our new console handler for Django's logger, and
# override the log level while we're at it
"django": {
"handlers": ["console", "mail_admins"],
"level": LOG_LEVEL,
},
"django.utils.autoreload": {
"level": "INFO",
},
# Add a bookwyrm-specific logger
"bookwyrm": {
"handlers": ["console"],
"level": LOG_LEVEL,
},
}, },
# This is copied as-is from the default logger, and is required for the Django section below
"mail_admins" : {
"level" : "ERROR" ,
"filters" : [ "require_debug_false" ] ,
"class" : "django.utils.log.AdminEmailHandler" ,
} ,
} ,
"loggers" : {
# Install our new console handler for Django's logger, and override the log level while we're at it
"django" : {
"handlers" : [ "console" , "mail_admins" ] ,
"level" : LOG_LEVEL ,
} ,
"django.utils.autoreload" : { "level" : "INFO" } ,
# Add a Bookwyrm-specific logger
"bookwyrm" : {
"handlers" : [ "console" ] ,
"level" : LOG_LEVEL ,
} ,
} ,
} }
STATICFILES_FINDERS = [ STATICFILES_FINDERS = [
"django.contrib.staticfiles.finders.FileSystemFinder", "django.contrib.staticfiles.finders.FileSystemFinder" ,
"django.contrib.staticfiles.finders.AppDirectoriesFinder", "django.contrib.staticfiles.finders.AppDirectoriesFinder" ,
"sass_processor.finders.CssFinder", "sass_processor.finders.CssFinder" ,
] ]
SASS_PROCESSOR_INCLUDE_FILE_PATTERN = r"^.+\.[s]{0,1}(?:a|c)ss$" SASS_PROCESSOR_INCLUDE_FILE_PATTERN = r"^.+\.[s]{0,1}(?:a|c)ss$"
# when debug is disabled, make sure to compile themes once with `./bw-dev compile_themes` # When debug is disabled, make sure to compile themes once with `./bw-dev compile_themes`
SASS_PROCESSOR_ENABLED = DEBUG SASS_PROCESSOR_ENABLED = DEBUG
# minify css is production but not dev # Minify CSS in production but not dev
if not DEBUG: if not DEBUG:
SASS_OUTPUT_STYLE = "compressed" SASS_OUTPUT_STYLE = "compressed"
WSGI_APPLICATION = "bookwyrm.wsgi.application" WSGI_APPLICATION = "bookwyrm.wsgi.application"
# redis/activity streams settings # Redis/activity streams settings
REDIS_ACTIVITY_HOST = env("REDIS_ACTIVITY_HOST", "localhost") REDIS_ACTIVITY_HOST = env ( "REDIS_ACTIVITY_HOST" , "localhost" )
REDIS_ACTIVITY_PORT = env.int("REDIS_ACTIVITY_PORT", 6379) REDIS_ACTIVITY_PORT = env . int ( "REDIS_ACTIVITY_PORT" , 6379 )
REDIS_ACTIVITY_PASSWORD = requests.utils.quote(env("REDIS_ACTIVITY_PASSWORD", "")) REDIS_ACTIVITY_PASSWORD = requests . utils . quote ( env ( "REDIS_ACTIVITY_PASSWORD" , "" ) )
REDIS_ACTIVITY_DB_INDEX = env.int("REDIS_ACTIVITY_DB_INDEX", 0) REDIS_ACTIVITY_DB_INDEX = env . int ( "REDIS_ACTIVITY_DB_INDEX" , 0 )
REDIS_ACTIVITY_URL = env( REDIS_ACTIVITY_URL = env (
"REDIS_ACTIVITY_URL", "REDIS_ACTIVITY_URL" ,
f"redis://:{REDIS_ACTIVITY_PASSWORD}@{REDIS_ACTIVITY_HOST}:{REDIS_ACTIVITY_PORT}/{REDIS_ACTIVITY_DB_INDEX}", f"redis://:{REDIS_ACTIVITY_PASSWORD}@{REDIS_ACTIVITY_HOST}:{REDIS_ACTIVITY_PORT}/{REDIS_ACTIVITY_DB_INDEX}" ,
) )
MAX_STREAM_LENGTH = env.int("MAX_STREAM_LENGTH", 200) MAX_STREAM_LENGTH = env . int ( "MAX_STREAM_LENGTH" , 200 )
STREAMS = [ STREAMS = [
{"key": "home", "name": _("Home Timeline"), "shortname": _("Home")}, { "key" : "home" , "name" : _("Home Timeline") , "shortname" : _("Home") } ,
{"key": "books", "name": _("Books Timeline"), "shortname": _("Books")}, { "key" : "books" , "name" : _("Books Timeline") , "shortname" : _("Books") } ,
] ]
# Search configuration # Search configuration
# total time in seconds that the instance will spend searching connectors # Total time in seconds that the instance will spend searching connectors
SEARCH_TIMEOUT = env.int("SEARCH_TIMEOUT", 8) SEARCH_TIMEOUT = env . int ( "SEARCH_TIMEOUT" , 8 )
# timeout for a query to an individual connector # Timeout for a query to an individual connector
QUERY_TIMEOUT = env.int("INTERACTIVE_QUERY_TIMEOUT", env.int("QUERY_TIMEOUT", 5)) QUERY_TIMEOUT = env . int ( "INTERACTIVE_QUERY_TIMEOUT" , env . int ( "QUERY_TIMEOUT" , 5 ) )
# Redis cache backend # Redis cache backend
if env.bool("USE_DUMMY_CACHE", False): if env . bool ( "USE_DUMMY_CACHE" , False ) :
CACHES = { CACHES = {
"default": { "default" : {
"BACKEND": "django.core.cache.backends.dummy.DummyCache", "BACKEND" : "django.core.cache.backends.dummy.DummyCache" ,
}, } ,
"file_resubmit": { "file_resubmit" : {
"BACKEND": "django.core.cache.backends.dummy.DummyCache", "BACKEND" : "django.core.cache.backends.dummy.DummyCache" ,
"LOCATION": "/tmp/file_resubmit_tests/", "LOCATION" : "/tmp/file_resubmit_tests/" ,
}, } ,
} }
else: else :
CACHES = { CACHES = {
"default": { "default" : {
"BACKEND": "django.core.cache.backends.redis.RedisCache", "BACKEND" : "django.core.cache.backends.redis.RedisCache" ,
"LOCATION": REDIS_ACTIVITY_URL, "LOCATION" : REDIS_ACTIVITY_URL ,
}, } ,
"file_resubmit": { "file_resubmit" : {
"BACKEND": "django.core.cache.backends.filebased.FileBasedCache", "BACKEND" : "django.core.cache.backends.filebased.FileBasedCache" ,
"LOCATION": "/tmp/file_resubmit/", "LOCATION" : "/tmp/file_resubmit/" ,
}, } ,
} }
SESSION_ENGINE = "django.contrib.sessions.backends.cache" SESSION_ENGINE = "django.contrib.sessions.backends.cache"
@ -271,14 +253,14 @@ else:
# https://docs.djangoproject.com/en/3.2/ref/settings/#databases # https://docs.djangoproject.com/en/3.2/ref/settings/#databases
DATABASES = { DATABASES = {
"default": { "default" : {
"ENGINE": "django.db.backends.postgresql_psycopg2", "ENGINE" : "django.db.backends.postgresql_psycopg2" ,
"NAME": env("POSTGRES_DB", "bookwyrm"), "NAME" : env ( "POSTGRES_DB" , "bookwyrm" ) ,
"USER": env("POSTGRES_USER", "bookwyrm"), "USER" : env ( "POSTGRES_USER" , "bookwyrm" ) ,
"PASSWORD": env("POSTGRES_PASSWORD", "bookwyrm"), "PASSWORD" : env ( "POSTGRES_PASSWORD" , "bookwyrm" ) ,
"HOST": env("POSTGRES_HOST", ""), "HOST" : env ( "POSTGRES_HOST" , "" ) ,
"PORT": env.int("PGPORT", 5432), "PORT" : env . int ( "PGPORT" , 5432 ) ,
}, } ,
} }
@ -289,53 +271,45 @@ AUTH_USER_MODEL = "bookwyrm.User"
# https://docs.djangoproject.com/en/3.2/ref/settings/#auth-password-validators # https://docs.djangoproject.com/en/3.2/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [ AUTH_PASSWORD_VALIDATORS = [
{ { "NAME" : "django.contrib.auth.password_validation.UserAttributeSimilarityValidator" } ,
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator", { "NAME" : "django.contrib.auth.password_validation.MinimumLengthValidator" } ,
}, { "NAME" : "django.contrib.auth.password_validation.CommonPasswordValidator" } ,
{ { "NAME" : "django.contrib.auth.password_validation.NumericPasswordValidator" } ,
"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
},
{
"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
},
{
"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
},
] ]
# Internationalization # Internationalization
# https://docs.djangoproject.com/en/3.2/topics/i18n/ # https://docs.djangoproject.com/en/3.2/topics/i18n/
LANGUAGE_CODE = env("LANGUAGE_CODE", "en-us") LANGUAGE_CODE = env ( "LANGUAGE_CODE" , "en-us" )
LANGUAGES = [ LANGUAGES = [
("en-us", _("English")), ( "en-us" , _("English") ) ,
("ca-es", _("Català (Catalan)")), ( "ca-es" , _("Català (Catalan)") ) ,
("de-de", _("Deutsch (German)")), ( "de-de" , _("Deutsch (German)") ) ,
("eo-uy", _("Esperanto (Esperanto)")), ( "eo-uy" , _("Esperanto (Esperanto)") ) ,
("es-es", _("Español (Spanish)")), ( "es-es" , _("Español (Spanish)") ) ,
("eu-es", _("Euskara (Basque)")), ( "eu-es" , _("Euskara (Basque)") ) ,
("gl-es", _("Galego (Galician)")), ( "gl-es" , _("Galego (Galician)") ) ,
("it-it", _("Italiano (Italian)")), ( "it-it" , _("Italiano (Italian)") ) ,
("ko-kr", _("한국어 (Korean)")), ( "ko-kr" , _("한국어 (Korean)") ) ,
("fi-fi", _("Suomi (Finnish)")), ( "fi-fi" , _("Suomi (Finnish)") ) ,
("fr-fr", _("Français (French)")), ( "fr-fr" , _("Français (French)") ) ,
("lt-lt", _("Lietuvių (Lithuanian)")), ( "lt-lt" , _("Lietuvių (Lithuanian)") ) ,
("nl-nl", _("Nederlands (Dutch)")), ( "nl-nl" , _("Nederlands (Dutch)") ) ,
("no-no", _("Norsk (Norwegian)")), ( "no-no" , _("Norsk (Norwegian)") ) ,
("pl-pl", _("Polski (Polish)")), ( "pl-pl" , _("Polski (Polish)") ) ,
("pt-br", _("Português do Brasil (Brazilian Portuguese)")), ( "pt-br" , _("Português do Brasil (Brazilian Portuguese)") ) ,
("pt-pt", _("Português Europeu (European Portuguese)")), ( "pt-pt" , _("Português Europeu (European Portuguese)") ) ,
("ro-ro", _("Română (Romanian)")), ( "ro-ro" , _("Română (Romanian)") ) ,
("sv-se", _("Svenska (Swedish)")), ( "sv-se" , _("Svenska (Swedish)") ) ,
("uk-ua", _("Українська (Ukrainian)")), ( "uk-ua" , _("Українська (Ukrainian)") ) ,
("zh-hans", _("简体中文 (Simplified Chinese)")), ( "zh-hans" , _("简体中文 (Simplified Chinese)") ) ,
("zh-hant", _("繁體中文 (Traditional Chinese)")), ( "zh-hant" , _("繁體中文 (Traditional Chinese)") ) ,
] ]
LANGUAGE_ARTICLES = { LANGUAGE_ARTICLES = {
"English": {"the", "a", "an"}, "English" : { "the" , "a" , "an" } ,
"Español (Spanish)": {"un", "una", "unos", "unas", "el", "la", "los", "las"}, "Español (Spanish)" : { "un" , "una" , "unos" , "unas" , "el" , "la" , "los" , "las" } ,
} }
TIME_ZONE = "UTC" TIME_ZONE = "UTC"
@ -345,78 +319,78 @@ USE_I18N = True
USE_TZ = True USE_TZ = True
# Imagekit generated thumbnails # Imagekit generated thumbnails
ENABLE_THUMBNAIL_GENERATION = env.bool("ENABLE_THUMBNAIL_GENERATION", False) ENABLE_THUMBNAIL_GENERATION = env . bool ( "ENABLE_THUMBNAIL_GENERATION" , False )
IMAGEKIT_CACHEFILE_DIR = "thumbnails" IMAGEKIT_CACHEFILE_DIR = "thumbnails"
IMAGEKIT_DEFAULT_CACHEFILE_STRATEGY = "bookwyrm.thumbnail_generation.Strategy" IMAGEKIT_DEFAULT_CACHEFILE_STRATEGY = "bookwyrm.thumbnail_generation.Strategy"
PROJECT_DIR = os.path.dirname(os.path.abspath(__file__)) PROJECT_DIR = os . path . dirname ( os . path . abspath (__file__) )
CSP_ADDITIONAL_HOSTS = env.list("CSP_ADDITIONAL_HOSTS", []) CSP_ADDITIONAL_HOSTS = env . list ( "CSP_ADDITIONAL_HOSTS" , [] )
PROTOCOL = "http" PROTOCOL = "http"
if USE_HTTPS: if USE_HTTPS :
PROTOCOL = "https" PROTOCOL = "https"
SESSION_COOKIE_SECURE = True SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True CSRF_COOKIE_SECURE = True
PORT = env.int("PORT", 443 if USE_HTTPS else 80) PORT = env . int ( "PORT" , 443 if USE_HTTPS else 80 )
if (USE_HTTPS and PORT == 443) or (not USE_HTTPS and PORT == 80): if ( USE_HTTPS and PORT == 443 ) or ( not USE_HTTPS and PORT == 80 ) :
NETLOC = DOMAIN NETLOC = DOMAIN
else: else :
NETLOC = f"{DOMAIN}:{PORT}" NETLOC = f"{DOMAIN}:{PORT}"
BASE_URL = f"{PROTOCOL}://{NETLOC}" BASE_URL = f"{PROTOCOL}://{NETLOC}"
CSRF_TRUSTED_ORIGINS = [BASE_URL] CSRF_TRUSTED_ORIGINS = [ BASE_URL ]
USER_AGENT = f"BookWyrm (BookWyrm/{VERSION}; +{BASE_URL})" USER_AGENT = f"BookWyrm (BookWyrm/{VERSION}; +{BASE_URL})"
# Storage # Storage
USE_S3 = env.bool("USE_S3", False) USE_S3 = env . bool ( "USE_S3" , False )
USE_AZURE = env.bool("USE_AZURE", False) USE_AZURE = env . bool ( "USE_AZURE" , False )
S3_SIGNED_URL_EXPIRY = env.int("S3_SIGNED_URL_EXPIRY", 900) S3_SIGNED_URL_EXPIRY = env . int ( "S3_SIGNED_URL_EXPIRY" , 900 )
if USE_S3: if USE_S3 :
# AWS settings # AWS settings
AWS_ACCESS_KEY_ID = env("AWS_ACCESS_KEY_ID") AWS_ACCESS_KEY_ID = env ("AWS_ACCESS_KEY_ID")
AWS_SECRET_ACCESS_KEY = env("AWS_SECRET_ACCESS_KEY") AWS_SECRET_ACCESS_KEY = env ("AWS_SECRET_ACCESS_KEY")
AWS_STORAGE_BUCKET_NAME = env("AWS_STORAGE_BUCKET_NAME") AWS_STORAGE_BUCKET_NAME = env ("AWS_STORAGE_BUCKET_NAME")
AWS_S3_CUSTOM_DOMAIN = env("AWS_S3_CUSTOM_DOMAIN", None) AWS_S3_CUSTOM_DOMAIN = env ( "AWS_S3_CUSTOM_DOMAIN" , None )
AWS_S3_REGION_NAME = env("AWS_S3_REGION_NAME", "") AWS_S3_REGION_NAME = env ( "AWS_S3_REGION_NAME" , "" )
AWS_S3_ENDPOINT_URL = env("AWS_S3_ENDPOINT_URL", None) AWS_S3_ENDPOINT_URL = env ( "AWS_S3_ENDPOINT_URL" , None )
AWS_DEFAULT_ACL = "public-read" AWS_DEFAULT_ACL = "public-read"
AWS_S3_OBJECT_PARAMETERS = {"CacheControl": "max-age=86400"} AWS_S3_OBJECT_PARAMETERS = { "CacheControl" : "max-age=86400" }
AWS_S3_URL_PROTOCOL = env("AWS_S3_URL_PROTOCOL", f"{PROTOCOL}:") AWS_S3_URL_PROTOCOL = env ( "AWS_S3_URL_PROTOCOL" , f"{PROTOCOL}:" )
# Storages # Storages
STORAGES = { STORAGES = {
"default": { "default" : {
"BACKEND": "storages.backends.s3.S3Storage", "BACKEND" : "storages.backends.s3.S3Storage" ,
"OPTIONS": { "OPTIONS" : {
"location": "images", "location" : "images" ,
"default_acl": "public-read", "default_acl" : "public-read" ,
"file_overwrite": False, "file_overwrite" : False ,
}, } ,
}, } ,
"staticfiles": { "staticfiles" : {
"BACKEND": "storages.backends.s3.S3Storage", "BACKEND" : "storages.backends.s3.S3Storage" ,
"OPTIONS": { "OPTIONS" : {
"location": "static", "location" : "static" ,
"default_acl": "public-read", "default_acl" : "public-read" ,
}, } ,
}, } ,
"sass_processor": { "sass_processor" : {
"BACKEND": "storages.backends.s3.S3Storage", "BACKEND" : "storages.backends.s3.S3Storage" ,
"OPTIONS": { "OPTIONS" : {
"location": "static", "location" : "static" ,
"default_acl": "public-read", "default_acl" : "public-read" ,
}, } ,
}, } ,
"exports": { "exports" : {
"BACKEND": "storages.backends.s3.S3Storage", "BACKEND" : "storages.backends.s3.S3Storage" ,
"OPTIONS": { "OPTIONS" : {
"location": "images", "location" : "images" ,
"default_acl": None, "default_acl" : None ,
"file_overwrite": False, "file_overwrite" : False ,
}, } ,
}, } ,
} }
# S3 Static settings # S3 Static settings
STATIC_LOCATION = "static" STATIC_LOCATION = "static"
@ -428,72 +402,62 @@ if USE_S3:
MEDIA_FULL_URL = MEDIA_URL MEDIA_FULL_URL = MEDIA_URL
# Content Security Policy # Content Security Policy
CSP_DEFAULT_SRC = [ CSP_DEFAULT_SRC = [
"'self'", "'self'" ,
f"{AWS_S3_URL_PROTOCOL}//{AWS_S3_CUSTOM_DOMAIN}" f"{AWS_S3_URL_PROTOCOL}//{AWS_S3_CUSTOM_DOMAIN}" if AWS_S3_CUSTOM_DOMAIN else None ,
if AWS_S3_CUSTOM_DOMAIN
else None,
] + CSP_ADDITIONAL_HOSTS ] + CSP_ADDITIONAL_HOSTS
CSP_SCRIPT_SRC = [ CSP_SCRIPT_SRC = [
"'self'", "'self'" ,
f"{AWS_S3_URL_PROTOCOL}//{AWS_S3_CUSTOM_DOMAIN}" f"{AWS_S3_URL_PROTOCOL}//{AWS_S3_CUSTOM_DOMAIN}" if AWS_S3_CUSTOM_DOMAIN else None ,
if AWS_S3_CUSTOM_DOMAIN
else None,
] + CSP_ADDITIONAL_HOSTS ] + CSP_ADDITIONAL_HOSTS
elif USE_AZURE: elif USE_AZURE :
# Azure settings # Azure settings
AZURE_ACCOUNT_NAME = env("AZURE_ACCOUNT_NAME") AZURE_ACCOUNT_NAME = env ("AZURE_ACCOUNT_NAME")
AZURE_ACCOUNT_KEY = env("AZURE_ACCOUNT_KEY") AZURE_ACCOUNT_KEY = env ("AZURE_ACCOUNT_KEY")
AZURE_CONTAINER = env("AZURE_CONTAINER") AZURE_CONTAINER = env ("AZURE_CONTAINER")
AZURE_CUSTOM_DOMAIN = env("AZURE_CUSTOM_DOMAIN") AZURE_CUSTOM_DOMAIN = env ("AZURE_CUSTOM_DOMAIN")
# Storages # Storages
STORAGES = { STORAGES = {
"default": { "default" : {
"BACKEND": "storages.backends.azure_storage.AzureStorage", "BACKEND" : "storages.backends.azure_storage.AzureStorage" ,
"OPTIONS": { "OPTIONS" : {
"location": "images", "location" : "images" ,
"overwrite_files": False, "overwrite_files" : False ,
}, } ,
}, } ,
"staticfiles": { "staticfiles" : {
"BACKEND": "storages.backends.azure_storage.AzureStorage", "BACKEND" : "storages.backends.azure_storage.AzureStorage" ,
"OPTIONS": { "OPTIONS" : {
"location": "static", "location" : "static" ,
}, } ,
}, } ,
"exports": { "exports" : {
"BACKEND": None, # not implemented yet "BACKEND" : None , # Not implemented yet
}, } ,
} }
# Azure Static settings # Azure Static settings
STATIC_LOCATION = "static" STATIC_LOCATION = "static"
STATIC_URL = ( STATIC_URL = ( f"{PROTOCOL}://{AZURE_CUSTOM_DOMAIN}/{AZURE_CONTAINER}/{STATIC_LOCATION}/" )
f"{PROTOCOL}://{AZURE_CUSTOM_DOMAIN}/{AZURE_CONTAINER}/{STATIC_LOCATION}/"
)
STATIC_FULL_URL = STATIC_URL STATIC_FULL_URL = STATIC_URL
# Azure Media settings # Azure Media settings
MEDIA_LOCATION = "images" MEDIA_LOCATION = "images"
MEDIA_URL = ( MEDIA_URL = ( f"{PROTOCOL}://{AZURE_CUSTOM_DOMAIN}/{AZURE_CONTAINER}/{MEDIA_LOCATION}/" )
f"{PROTOCOL}://{AZURE_CUSTOM_DOMAIN}/{AZURE_CONTAINER}/{MEDIA_LOCATION}/"
)
MEDIA_FULL_URL = MEDIA_URL MEDIA_FULL_URL = MEDIA_URL
# Content Security Policy # Content Security Policy
CSP_DEFAULT_SRC = ["'self'", AZURE_CUSTOM_DOMAIN] + CSP_ADDITIONAL_HOSTS CSP_DEFAULT_SRC = [ "'self'" , AZURE_CUSTOM_DOMAIN ] + CSP_ADDITIONAL_HOSTS
CSP_SCRIPT_SRC = ["'self'", AZURE_CUSTOM_DOMAIN] + CSP_ADDITIONAL_HOSTS CSP_SCRIPT_SRC = [ "'self'" , AZURE_CUSTOM_DOMAIN ] + CSP_ADDITIONAL_HOSTS
else: else :
# Storages # Storages
STORAGES = { STORAGES = {
"default": { "default" : {
"BACKEND": "django.core.files.storage.FileSystemStorage", "BACKEND" : "django.core.files.storage.FileSystemStorage" ,
}, } ,
"staticfiles": { "staticfiles" : {
"BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage", "BACKEND" : "django.contrib.staticfiles.storage.StaticFilesStorage" ,
}, } ,
"exports": { "exports" : {
"BACKEND": "django.core.files.storage.FileSystemStorage", "BACKEND" : "django.core.files.storage.FileSystemStorage" ,
"OPTIONS": { "OPTIONS" : { "location" : "exports" } ,
"location": "exports", } ,
},
},
} }
# Static settings # Static settings
STATIC_URL = "/static/" STATIC_URL = "/static/"
@ -502,29 +466,21 @@ else:
MEDIA_URL = "/images/" MEDIA_URL = "/images/"
MEDIA_FULL_URL = BASE_URL + MEDIA_URL MEDIA_FULL_URL = BASE_URL + MEDIA_URL
# Content Security Policy # Content Security Policy
CSP_DEFAULT_SRC = ["'self'"] + CSP_ADDITIONAL_HOSTS CSP_DEFAULT_SRC = [ "'self'" ] + CSP_ADDITIONAL_HOSTS
CSP_SCRIPT_SRC = ["'self'"] + CSP_ADDITIONAL_HOSTS CSP_SCRIPT_SRC = [ "'self'" ] + CSP_ADDITIONAL_HOSTS
CSP_INCLUDE_NONCE_IN = ["script-src"] CSP_INCLUDE_NONCE_IN = [ "script-src" ]
OTEL_EXPORTER_OTLP_ENDPOINT = env("OTEL_EXPORTER_OTLP_ENDPOINT", None) TWO_FACTOR_LOGIN_MAX_SECONDS = env . int ( "TWO_FACTOR_LOGIN_MAX_SECONDS" , 60 )
OTEL_EXPORTER_OTLP_HEADERS = env("OTEL_EXPORTER_OTLP_HEADERS", None) TWO_FACTOR_LOGIN_VALIDITY_WINDOW = env . int ( "TWO_FACTOR_LOGIN_VALIDITY_WINDOW" , 2 )
OTEL_SERVICE_NAME = env("OTEL_SERVICE_NAME", None)
OTEL_EXPORTER_CONSOLE = env.bool("OTEL_EXPORTER_CONSOLE", False)
TWO_FACTOR_LOGIN_MAX_SECONDS = env.int("TWO_FACTOR_LOGIN_MAX_SECONDS", 60) HTTP_X_FORWARDED_PROTO = env . bool ( "SECURE_PROXY_SSL_HEADER" , False )
TWO_FACTOR_LOGIN_VALIDITY_WINDOW = env.int("TWO_FACTOR_LOGIN_VALIDITY_WINDOW", 2) if HTTP_X_FORWARDED_PROTO :
SECURE_PROXY_SSL_HEADER = ( "HTTP_X_FORWARDED_PROTO" , "https" )
HTTP_X_FORWARDED_PROTO = env.bool("SECURE_PROXY_SSL_HEADER", False) # Instance Actor for signing GET requests to "secure mode" Mastodon servers.
if HTTP_X_FORWARDED_PROTO: # Do not change this setting unless you already have an existing user with the same username - in which case you should change it
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
# Instance Actor for signing GET requests to "secure mode"
# Mastodon servers.
# Do not change this setting unless you already have an existing
# user with the same username - in which case you should change it!
INSTANCE_ACTOR_USERNAME = "bookwyrm.instance.actor" INSTANCE_ACTOR_USERNAME = "bookwyrm.instance.actor"
# We only allow specifying DATA_UPLOAD_MAX_MEMORY_SIZE in MiB from .env # We only allow specifying DATA_UPLOAD_MAX_MEMORY_SIZE in MiB from .env (note the difference in variable names).
# (note the difference in variable names). DATA_UPLOAD_MAX_MEMORY_SIZE = env . int ( "DATA_UPLOAD_MAX_MEMORY_MiB" , 100 ) << 20
DATA_UPLOAD_MAX_MEMORY_SIZE = env.int("DATA_UPLOAD_MAX_MEMORY_MiB", 100) << 20