From 57f12cfac93227260f222dc81393134a16d7bcb8 Mon Sep 17 00:00:00 2001 From: Steven Bal Date: Tue, 14 May 2024 17:22:24 +0200 Subject: [PATCH] :construction: Initial work to add helptexts to envvars --- open_api_framework/conf/base.py | 186 +++++++++++++++++++++++++------ open_api_framework/conf/utils.py | 2 +- 2 files changed, 155 insertions(+), 33 deletions(-) diff --git a/open_api_framework/conf/base.py b/open_api_framework/conf/base.py index df97be4..97ded5f 100644 --- a/open_api_framework/conf/base.py +++ b/open_api_framework/conf/base.py @@ -29,19 +29,43 @@ # # Core Django settings # -SITE_ID = config("SITE_ID", default=1) +SITE_ID = config( + "SITE_ID", + default=1, + help_text="The database ID of the site object. You usually won't have to touch this.", +) # SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = config("SECRET_KEY") +SECRET_KEY = config( + "SECRET_KEY", + help_text="Secret key that's used for certain cryptographic utilities. You should generate one via `miniwebtool `_", +) # NEVER run with DEBUG=True in production-like environments -DEBUG = config("DEBUG", default=False) +DEBUG = config( + "DEBUG", + default=False, + help_text="Only set this to True on a local development environment. Various other security settings are derived from this setting!", +) # = domains we're running on -ALLOWED_HOSTS = config("ALLOWED_HOSTS", default="", split=True) -USE_X_FORWARDED_HOST = config("USE_X_FORWARDED_HOST", default=False) +ALLOWED_HOSTS = config( + "ALLOWED_HOSTS", + default="", + split=True, + help_text="a comma separated (without spaces!) list of domains that serve the installation. Used to protect against Host header attacks.", +) +USE_X_FORWARDED_HOST = config( + "USE_X_FORWARDED_HOST", + default=False, + help_text="whether to grab the domain/host from the X-Forwarded-Host header or not. This header is typically set by reverse proxies (such as nginx, traefik, Apache...). Default False - this is a header that can be spoofed and you need to ensure you control it before enabling this.", +) -IS_HTTPS = config("IS_HTTPS", default=not DEBUG) +IS_HTTPS = config( + "IS_HTTPS", + default=not DEBUG, + help_text="Used to construct absolute URLs and controls a variety of security settings", +) # Internationalization # https://docs.djangoproject.com/en/2.0/topics/i18n/ @@ -65,11 +89,33 @@ DATABASES = { "default": { "ENGINE": "django.db.backends.postgresql", - "NAME": config("DB_NAME", PROJECT_DIRNAME), - "USER": config("DB_USER", PROJECT_DIRNAME), - "PASSWORD": config("DB_PASSWORD", PROJECT_DIRNAME), - "HOST": config("DB_HOST", "localhost"), - "PORT": config("DB_PORT", 5432), + "NAME": config( + "DB_NAME", + PROJECT_DIRNAME, + group="Database", + help_text="name of the PostgreSQL database.", + ), + "USER": config( + "DB_USER", + PROJECT_DIRNAME, + group="Database", + help_text="username of the database user.", + ), + "PASSWORD": config( + "DB_PASSWORD", + PROJECT_DIRNAME, + group="Database", + help_text="password of the database user.", + ), + "HOST": config( + "DB_HOST", + "localhost", + group="Database", + help_text="hostname of the PostgreSQL database", + ), + "PORT": config( + "DB_PORT", 5432, group="Database", help_text="port number of the database" + ), } } @@ -80,7 +126,7 @@ CACHES = { "default": { "BACKEND": "django_redis.cache.RedisCache", - "LOCATION": f"redis://{config('CACHE_DEFAULT', 'localhost:6379/0')}", + "LOCATION": f"redis://{config('CACHE_DEFAULT', 'localhost:6379/0', help_text='redis cache address for the default cache')}", "OPTIONS": { "CLIENT_CLASS": "django_redis.client.DefaultClient", "IGNORE_EXCEPTIONS": True, @@ -88,7 +134,7 @@ }, "axes": { "BACKEND": "django_redis.cache.RedisCache", - "LOCATION": f"redis://{config('CACHE_AXES', 'localhost:6379/0')}", + "LOCATION": f"redis://{config('CACHE_AXES', 'localhost:6379/0', help_text='redis cache address for the brute force login protection cache')}", "OPTIONS": { "CLIENT_CLASS": "django_redis.client.DefaultClient", "IGNORE_EXCEPTIONS": True, @@ -220,24 +266,56 @@ # # Sending EMAIL # -EMAIL_HOST = config("EMAIL_HOST", default="localhost") +EMAIL_HOST = config( + "EMAIL_HOST", + default="localhost", + help_text="hostname for the outgoing e-mail server", +) EMAIL_PORT = config( - "EMAIL_PORT", default=25 + "EMAIL_PORT", + default=25, + help_text="port number of the outgoing e-mail server. Note that if you're on Google Cloud, sending e-mail via port 25 is completely blocked and you should use 487 for TLS.", ) # disabled on Google Cloud, use 487 instead -EMAIL_HOST_USER = config("EMAIL_HOST_USER", default="") -EMAIL_HOST_PASSWORD = config("EMAIL_HOST_PASSWORD", default="") -EMAIL_USE_TLS = config("EMAIL_USE_TLS", default=False) +EMAIL_HOST_USER = config( + "EMAIL_HOST_USER", default="", help_text="username to connect to the mail server" +) +EMAIL_HOST_PASSWORD = config( + "EMAIL_HOST_PASSWORD", + default="", + help_text="password to connect to the mail server", +) +EMAIL_USE_TLS = config( + "EMAIL_USE_TLS", + default=False, + help_text="whether to use TLS or not to connect to the mail server. Should be True if you're changing the EMAIL_PORT to 487.", +) EMAIL_TIMEOUT = 10 -DEFAULT_FROM_EMAIL = config("DEFAULT_FROM_EMAIL", f"{PROJECT_DIRNAME}@example.com") +DEFAULT_FROM_EMAIL = config( + "DEFAULT_FROM_EMAIL", + f"{PROJECT_DIRNAME}@example.com", + help_text="The default email address from which emails are sent", +) # # LOGGING # -LOG_STDOUT = config("LOG_STDOUT", default=False) -LOG_LEVEL = config("LOG_LEVEL", default="WARNING") -LOG_QUERIES = config("LOG_QUERIES", default=False) -LOG_REQUESTS = config("LOG_REQUESTS", default=False) +LOG_STDOUT = config( + "LOG_STDOUT", default=False, help_text="whether to log to stdout or not" +) +LOG_LEVEL = config( + "LOG_LEVEL", + default="WARNING", + help_text="control the verbosity of logging output. Available values are CRITICAL, ERROR, WARNING, INFO and DEBUG", +) +LOG_QUERIES = config( + "LOG_QUERIES", + default=False, + help_text="enable (query) logging at the database backend level. Note that you must also set DEBUG=1, which should be done very sparingly!", +) +LOG_REQUESTS = config( + "LOG_REQUESTS", default=False, help_text="enable logging of the outgoing requests" +) if LOG_QUERIES and not DEBUG: warnings.warn( "Requested LOG_QUERIES=1 but DEBUG is false, no query logs will be emited.", @@ -435,7 +513,11 @@ # # Custom settings # -ENVIRONMENT = config("ENVIRONMENT", "") +ENVIRONMENT = config( + "ENVIRONMENT", + "", + help_text="An identifier for the environment, displayed in the admin depending on the settings module used and included in the error monitoring (see SENTRY_DSN). The default is set according to DJANGO_SETTINGS_MODULE.", +) ENVIRONMENT_SHOWN_IN_ADMIN = True # Generating the schema, depending on the component @@ -465,6 +547,7 @@ "NUM_PROXIES", default=1, cast=lambda val: int(val) if val is not None else None, + help_text="the number of reverse proxies in front of the application, as an integer. This is used to determine the actual client IP adres. On Kubernetes with an ingress you typically want to set this to 2.", ) ############################## @@ -512,10 +595,25 @@ # # DJANGO-CORS-MIDDLEWARE # -CORS_ALLOW_ALL_ORIGINS = config("CORS_ALLOW_ALL_ORIGINS", default=False) -CORS_ALLOWED_ORIGINS = config("CORS_ALLOWED_ORIGINS", split=True, default=[]) +CORS_ALLOW_ALL_ORIGINS = config( + "CORS_ALLOW_ALL_ORIGINS", + default=False, + group="Cross-Origin-Resource-Sharing", + help_text="allow cross-domain access from any client", +) +CORS_ALLOWED_ORIGINS = config( + "CORS_ALLOWED_ORIGINS", + split=True, + default=[], + group="Cross-Origin-Resource-Sharing", + help_text="explicitly list the allowed origins for cross-domain requests. Example: http://localhost:3000,https://some-app.gemeente.nl", +) CORS_ALLOWED_ORIGIN_REGEXES = config( - "CORS_ALLOWED_ORIGIN_REGEXES", split=True, default=[] + "CORS_ALLOWED_ORIGIN_REGEXES", + split=True, + default=[], + group="Cross-Origin-Resource-Sharing", + help_text="same as ``CORS_ALLOWED_ORIGINS``, but supports regular expressions", ) # Authorization is included in default_cors_headers CORS_ALLOW_HEADERS = ( @@ -524,7 +622,13 @@ "accept-crs", "content-crs", ] - + config("CORS_EXTRA_ALLOW_HEADERS", split=True, default=[]) + + config( + "CORS_EXTRA_ALLOW_HEADERS", + split=True, + default=[], + group="Cross-Origin-Resource-Sharing", + help_text="headers that are allowed to be sent as part of the cross-domain request. By default, Authorization, Accept-Crs and Content-Crs are already included. The value of this variable is added to these already included headers.", + ) ) CORS_EXPOSE_HEADERS = [ "content-crs", @@ -552,7 +656,11 @@ # # NOTIFICATIONS-API-COMMON # -NOTIFICATIONS_DISABLED = config("NOTIFICATIONS_DISABLED", default=False) +NOTIFICATIONS_DISABLED = config( + "NOTIFICATIONS_DISABLED", + default=False, + help_text="if this variable is set to true, yes or 1, the notification mechanism will be disabled.", +) # # SENTRY - error monitoring @@ -560,7 +668,11 @@ def init_sentry(before_send: Callable | None = None): - SENTRY_DSN = config("SENTRY_DSN", None) + SENTRY_DSN = config( + "SENTRY_DSN", + None, + help_text="URL of the sentry project to send error reports to. Default empty, i.e. -> no monitoring set up. Highly recommended to configure this.", + ) if SENTRY_DSN: SENTRY_CONFIG = { @@ -583,8 +695,18 @@ def init_sentry(before_send: Callable | None = None): # # CELERY # -CELERY_BROKER_URL = config("CELERY_RESULT_BACKEND", "redis://localhost:6379/1") -CELERY_RESULT_BACKEND = config("CELERY_RESULT_BACKEND", "redis://localhost:6379/1") +CELERY_BROKER_URL = config( + "CELERY_RESULT_BACKEND", + "redis://localhost:6379/1", + group="Celery", + help_text="the URL of the broker that will be used to actually send the notifications", +) +CELERY_RESULT_BACKEND = config( + "CELERY_RESULT_BACKEND", + "redis://localhost:6379/1", + group="Celery", + help_text="the backend where the results of tasks will be stored", +) # diff --git a/open_api_framework/conf/utils.py b/open_api_framework/conf/utils.py index 2ac4f98..0f338e0 100644 --- a/open_api_framework/conf/utils.py +++ b/open_api_framework/conf/utils.py @@ -90,7 +90,7 @@ def strip_protocol_from_origin(origin: str) -> str: def get_project_dirname() -> str: - return config("DJANGO_SETTINGS_MODULE").split(".")[0] + return config("DJANGO_SETTINGS_MODULE", add_to_docs=False).split(".")[0] def get_django_project_dir() -> str: