1"""
2Default Plain settings. Override these with settings in the module pointed to
3by the PLAIN_SETTINGS_MODULE environment variable.
4"""
5
6import os
7
8from .secret import Secret
9from .utils import get_app_info_from_pyproject
10
11# MARK: Core Settings
12
13DEBUG: bool = False
14
15name, version = get_app_info_from_pyproject()
16NAME: str = name
17VERSION: str = version
18
19# List of strings representing installed packages.
20INSTALLED_PACKAGES: list[str] = []
21
22URLS_ROUTER: str
23
24# Whether routes have trailing slashes by default. Routes can override
25# per-endpoint with `path(..., force_slash=True|False)`. Requests that
26# disagree with a route's effective form are 308-redirected to it.
27URLS_TRAILING_SLASH: bool = False
28
29# List of environment variable prefixes to check for settings.
30# Settings can be configured via environment variables using these prefixes.
31# Example: ENV_SETTINGS_PREFIXES = ["PLAIN_", "MYAPP_"]
32# Then both PLAIN_DEBUG and MYAPP_DEBUG would set the DEBUG setting.
33ENV_SETTINGS_PREFIXES: list[str] = ["PLAIN_"]
34
35# MARK: HTTP and Security
36
37# Hosts/domain names that are valid for this site.
38# - An empty list [] allows all hosts (useful for development).
39# - ".example.com" matches example.com and all subdomains
40# - "192.168.1.0/24" matches IP addresses in that CIDR range
41ALLOWED_HOSTS: list[str] = []
42
43# Path for the built-in healthcheck endpoint.
44# When set, the server responds directly on the event loop with a 200 "ok"
45# before the thread pool or any middleware runs.
46# Example: HEALTHCHECK_PATH = "/up/"
47HEALTHCHECK_PATH: str = ""
48
49# Default headers for all responses.
50# Header values can include {request.attribute} placeholders for dynamic content.
51# Example: "script-src 'nonce-{request.csp_nonce}'" will use the request's nonce.
52# Views can override, remove, or extend these headers - see plain/http/README.md
53# for customization patterns.
54DEFAULT_RESPONSE_HEADERS: dict = {
55 # "Content-Security-Policy": "default-src 'self'; script-src 'self' 'nonce-{request.csp_nonce}'",
56 # https://hstspreload.org/
57 # "Strict-Transport-Security": "max-age=31536000; includeSubDomains; preload",
58 "Cross-Origin-Opener-Policy": "same-origin",
59 "Referrer-Policy": "same-origin",
60 "X-Content-Type-Options": "nosniff",
61 "X-Frame-Options": "DENY",
62}
63
64# Whether to redirect all non-HTTPS requests to HTTPS (blanket redirect).
65# For anything more advanced (custom host, path exemptions, etc.), write
66# your own middleware.
67HTTPS_REDIRECT_ENABLED: bool = True
68
69# If your Plain app is behind a proxy that sets a header to specify secure
70# connections, AND that proxy ensures that user-submitted headers with the
71# same name are ignored (so that people can't spoof it), set this value to
72# a string in the format "Header-Name: value". For any requests that come in
73# with that header/value, request.is_https() will return True.
74# WARNING! Only set this if you fully understand what you're doing. Otherwise,
75# you may be opening yourself up to a security risk.
76# Example: HTTPS_PROXY_HEADER = "X-Forwarded-Proto: https"
77HTTPS_PROXY_HEADER: str = ""
78
79# Whether to use the X-Forwarded-Host, X-Forwarded-Port, and X-Forwarded-For
80# headers when determining the host, port, and client IP for the request.
81# Only enable these when behind a trusted proxy that overwrites these headers.
82HTTP_X_FORWARDED_HOST: bool = False
83HTTP_X_FORWARDED_PORT: bool = False
84HTTP_X_FORWARDED_FOR: bool = False
85
86# A secret key for this particular Plain installation. Used in secret-key
87# hashing algorithms. Set this in your settings, or Plain will complain
88# loudly.
89SECRET_KEY: Secret[str]
90
91# List of secret keys used to verify the validity of signatures. This allows
92# secret key rotation.
93SECRET_KEY_FALLBACKS: Secret[list[str]] = []
94
95# MARK: Internationalization
96
97# Local time zone for this installation. All choices can be found here:
98# https://en.wikipedia.org/wiki/List_of_tz_zones_by_name (although not all
99# systems may support all possibilities). This is interpreted as the default
100# user time zone.
101TIME_ZONE: str = "UTC"
102
103
104# MARK: URL Configuration
105
106# The base URL of the site, used to generate absolute URLs outside of request contexts.
107# Should include scheme and host with no trailing slash (e.g. "https://example.com").
108BASE_URL: str = ""
109
110# MARK: File Uploads
111
112# List of upload handler classes to be applied in order.
113FILE_UPLOAD_HANDLERS: list[str] = [
114 "plain.internal.files.uploadhandler.MemoryFileUploadHandler",
115 "plain.internal.files.uploadhandler.TemporaryFileUploadHandler",
116]
117
118# Maximum size, in bytes, of a request before it will be streamed to the
119# file system instead of into memory.
120FILE_UPLOAD_MAX_MEMORY_SIZE: int = 2621440 # i.e. 2.5 MB
121
122# Maximum size in bytes of request data (excluding file uploads) that will be
123# read before a SuspiciousOperationError400 (RequestDataTooBigError400) is raised.
124DATA_UPLOAD_MAX_MEMORY_SIZE: int = 2621440 # i.e. 2.5 MB
125
126# Maximum number of GET/POST parameters that will be read before a
127# SuspiciousOperationError400 (TooManyFieldsSentError400) is raised.
128DATA_UPLOAD_MAX_NUMBER_FIELDS: int = 1000
129
130# Maximum number of files encoded in a multipart upload that will be read
131# before a SuspiciousOperationError400 (TooManyFilesSentError400) is raised.
132DATA_UPLOAD_MAX_NUMBER_FILES: int = 100
133
134# Directory in which upload streamed files will be temporarily saved. A value of
135# `None` will make Plain use the operating system's default temporary directory
136# (i.e. "/tmp" on *nix systems).
137FILE_UPLOAD_TEMP_DIR: str | None = None
138
139# MARK: Middleware
140
141# List of middleware to use. Order is important; in the request phase, these
142# middleware will be applied in the order given, and in the response
143# phase the middleware will be applied in reverse order.
144MIDDLEWARE: list[str] = []
145
146# MARK: CSRF
147
148# A list of trusted origins for unsafe (POST/PUT/DELETE etc.) requests.
149# These origins will be allowed regardless of the normal CSRF checks.
150# Each origin should be a full origin like "https://example.com" or "https://sub.example.com:8080"
151CSRF_TRUSTED_ORIGINS: list[str] = []
152
153# Regex patterns for paths that should be exempt from CSRF protection
154# Examples: [r"^/api/", r"/webhooks/.*", r"/health$"]
155CSRF_EXEMPT_PATHS: list[str] = []
156
157# MARK: Logging
158
159FRAMEWORK_LOG_LEVEL: str = "INFO"
160LOG_LEVEL: str = "INFO"
161LOG_FORMAT: str = "keyvalue"
162LOG_STREAM: str = "split" # "split", "stdout", or "stderr"
163
164# MARK: Server
165
166SERVER_WORKERS: int = int(os.environ.get("WEB_CONCURRENCY", 0)) # 0 = auto (CPU count)
167SERVER_THREADS: int = 4
168SERVER_TIMEOUT: int = 30
169SERVER_ACCESS_LOG: bool = True
170SERVER_ACCESS_LOG_FIELDS: list[str] = [
171 "method",
172 "path",
173 "query",
174 "status",
175 "duration_ms",
176 "size",
177 "ip",
178 "user_agent",
179 "referer",
180]
181SERVER_GRACEFUL_TIMEOUT: int = 30
182SERVER_SENDFILE: bool = True
183SERVER_CONNECTIONS: int = 1000
184SERVER_H2_MAX_CONCURRENT_STREAMS: int = 100
185SERVER_MAX_REQUESTS: int = 1000 # 0 = disabled
186SERVER_MAX_REQUESTS_JITTER: int = 100 # random variance to stagger restarts
187
188# MARK: Preflight Checks
189
190# Silence checks by name
191PREFLIGHT_SILENCED_CHECKS: list[str] = []
192
193# Silence specific check results by id
194PREFLIGHT_SILENCED_RESULTS: list[str] = []
195
196# MARK: Shell
197
198SHELL_IMPORT: str = ""